aboutsummaryrefslogtreecommitdiff
path: root/src/armnn/layers
diff options
context:
space:
mode:
authortelsoa01 <telmo.soares@arm.com>2018-08-31 09:22:23 +0100
committertelsoa01 <telmo.soares@arm.com>2018-08-31 09:22:23 +0100
commitc577f2c6a3b4ddb6ba87a882723c53a248afbeba (patch)
treebd7d4c148df27f8be6649d313efb24f536b7cf34 /src/armnn/layers
parent4c7098bfeab1ffe1cdc77f6c15548d3e73274746 (diff)
downloadarmnn-c577f2c6a3b4ddb6ba87a882723c53a248afbeba.tar.gz
Release 18.08
Diffstat (limited to 'src/armnn/layers')
-rw-r--r--src/armnn/layers/ActivationLayer.cpp8
-rw-r--r--src/armnn/layers/AdditionLayer.cpp40
-rw-r--r--src/armnn/layers/AdditionLayer.hpp2
-rw-r--r--src/armnn/layers/BatchNormalizationLayer.cpp24
-rw-r--r--src/armnn/layers/BatchNormalizationLayer.hpp2
-rw-r--r--src/armnn/layers/ConstantLayer.cpp18
-rw-r--r--src/armnn/layers/ConstantLayer.hpp12
-rw-r--r--src/armnn/layers/ConvertFp16ToFp32Layer.cpp48
-rw-r--r--src/armnn/layers/ConvertFp16ToFp32Layer.hpp28
-rw-r--r--src/armnn/layers/ConvertFp32ToFp16Layer.cpp47
-rw-r--r--src/armnn/layers/ConvertFp32ToFp16Layer.hpp27
-rw-r--r--src/armnn/layers/Convolution2dLayer.cpp43
-rw-r--r--src/armnn/layers/Convolution2dLayer.hpp4
-rw-r--r--src/armnn/layers/DepthwiseConvolution2dLayer.cpp46
-rw-r--r--src/armnn/layers/DepthwiseConvolution2dLayer.hpp4
-rw-r--r--src/armnn/layers/FakeQuantizationLayer.cpp12
-rw-r--r--src/armnn/layers/FloorLayer.cpp16
-rw-r--r--src/armnn/layers/FullyConnectedLayer.cpp40
-rw-r--r--src/armnn/layers/FullyConnectedLayer.hpp3
-rw-r--r--src/armnn/layers/L2NormalizationLayer.cpp13
-rw-r--r--src/armnn/layers/LayerWithParameters.hpp6
-rw-r--r--src/armnn/layers/LstmLayer.cpp259
-rw-r--r--src/armnn/layers/LstmLayer.hpp70
-rw-r--r--src/armnn/layers/MemCopyLayer.cpp15
-rw-r--r--src/armnn/layers/MergerLayer.cpp73
-rw-r--r--src/armnn/layers/MergerLayer.hpp1
-rw-r--r--src/armnn/layers/MultiplicationLayer.cpp40
-rw-r--r--src/armnn/layers/MultiplicationLayer.hpp1
-rw-r--r--src/armnn/layers/NormalizationLayer.cpp10
-rw-r--r--src/armnn/layers/OutputLayer.cpp2
-rw-r--r--src/armnn/layers/PermuteLayer.cpp20
-rw-r--r--src/armnn/layers/PermuteLayer.hpp1
-rw-r--r--src/armnn/layers/Pooling2dLayer.cpp30
-rw-r--r--src/armnn/layers/Pooling2dLayer.hpp9
-rw-r--r--src/armnn/layers/ReshapeLayer.cpp16
-rw-r--r--src/armnn/layers/ReshapeLayer.hpp1
-rw-r--r--src/armnn/layers/ResizeBilinearLayer.cpp24
-rw-r--r--src/armnn/layers/ResizeBilinearLayer.hpp1
-rw-r--r--src/armnn/layers/SoftmaxLayer.cpp10
-rw-r--r--src/armnn/layers/SoftmaxLayer.hpp8
-rw-r--r--src/armnn/layers/SplitterLayer.cpp32
-rw-r--r--src/armnn/layers/SplitterLayer.hpp1
42 files changed, 866 insertions, 201 deletions
diff --git a/src/armnn/layers/ActivationLayer.cpp b/src/armnn/layers/ActivationLayer.cpp
index 2371eaa97c..ad1e4a9eba 100644
--- a/src/armnn/layers/ActivationLayer.cpp
+++ b/src/armnn/layers/ActivationLayer.cpp
@@ -30,12 +30,16 @@ ActivationLayer* ActivationLayer::Clone(Graph& graph) const
void ActivationLayer::ValidateTensorShapesFromInputs()
{
- auto& info = GetInputSlot(0).GetConnection()->GetTensorInfo();
+ VerifyLayerConnections(1, CHECK_LOCATION());
+
+ auto inferredShapes = InferOutputShapes({ GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape() });
+
+ BOOST_ASSERT(inferredShapes.size() == 1);
ConditionalThrowIfNotEqual<LayerValidationException>(
"ActivationLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
GetOutputSlot(0).GetTensorInfo().GetShape(),
- info.GetShape());
+ inferredShapes[0]);
}
} // namespace armnn
diff --git a/src/armnn/layers/AdditionLayer.cpp b/src/armnn/layers/AdditionLayer.cpp
index 85d12eabcb..ab73a918db 100644
--- a/src/armnn/layers/AdditionLayer.cpp
+++ b/src/armnn/layers/AdditionLayer.cpp
@@ -28,41 +28,51 @@ AdditionLayer* AdditionLayer::Clone(Graph& graph) const
return CloneBase<AdditionLayer>(graph, GetName());
}
-void AdditionLayer::ValidateTensorShapesFromInputs()
+std::vector<TensorShape> AdditionLayer::InferOutputShapes(const std::vector<TensorShape>& inputShapes) const
{
- auto& input0 = GetInputSlot(0).GetConnection()->GetTensorInfo();
- auto& input1 = GetInputSlot(1).GetConnection()->GetTensorInfo();
+ BOOST_ASSERT(inputShapes.size() == 2);
+ auto& input0 = inputShapes[0];
+ auto& input1 = inputShapes[1];
- // Get the max of the inputs
+ // Get the max of the inputs.
BOOST_ASSERT(input0.GetNumDimensions() == input1.GetNumDimensions());
unsigned int numDims = input0.GetNumDimensions();
std::vector<unsigned int> dims(numDims);
- // validate inputs are broadcast compatible
-#if !NDEBUG
for (unsigned int i = 0; i < numDims; i++)
{
- unsigned int dim0 = input0.GetShape()[i];
- unsigned int dim1 = input1.GetShape()[i];
+ unsigned int dim0 = input0[i];
+ unsigned int dim1 = input1[i];
+
+ // Validates inputs are broadcast compatible.
+#if !NDEBUG
if (dim0 != dim1)
{
BOOST_ASSERT_MSG(dim0 == 1 || dim1 == 1, "Dimensions should either match or one should be of size 1.");
}
- }
#endif
- for (unsigned int i = 0; i < numDims; i++)
- {
- unsigned int dim0 = input0.GetShape()[i];
- unsigned int dim1 = input1.GetShape()[i];
dims[i] = std::max(dim0, dim1);
}
- TensorShape outShape(numDims, dims.data());
+ return std::vector<TensorShape>({ TensorShape(numDims, dims.data()) });
+}
+
+void AdditionLayer::ValidateTensorShapesFromInputs()
+{
+ VerifyLayerConnections(2, CHECK_LOCATION());
+
+ auto inferredShapes = InferOutputShapes({
+ GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(),
+ GetInputSlot(1).GetConnection()->GetTensorInfo().GetShape()
+ });
+
+ BOOST_ASSERT(inferredShapes.size() == 1);
+
ConditionalThrowIfNotEqual<LayerValidationException>(
"AdditionLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
GetOutputSlot(0).GetTensorInfo().GetShape(),
- outShape);
+ inferredShapes[0]);
}
} // namespace armnn
diff --git a/src/armnn/layers/AdditionLayer.hpp b/src/armnn/layers/AdditionLayer.hpp
index c48c027763..37f0b5c259 100644
--- a/src/armnn/layers/AdditionLayer.hpp
+++ b/src/armnn/layers/AdditionLayer.hpp
@@ -19,6 +19,8 @@ public:
void ValidateTensorShapesFromInputs() override;
+ std::vector<TensorShape> InferOutputShapes(const std::vector<TensorShape>& inputShapes) const override;
+
protected:
AdditionLayer(const char* name);
~AdditionLayer() = default;
diff --git a/src/armnn/layers/BatchNormalizationLayer.cpp b/src/armnn/layers/BatchNormalizationLayer.cpp
index ebb8954ea7..0bf81ebec9 100644
--- a/src/armnn/layers/BatchNormalizationLayer.cpp
+++ b/src/armnn/layers/BatchNormalizationLayer.cpp
@@ -21,12 +21,19 @@ BatchNormalizationLayer::BatchNormalizationLayer(const armnn::BatchNormalization
std::unique_ptr<IWorkload> BatchNormalizationLayer::CreateWorkload(const Graph& graph,
const IWorkloadFactory& factory) const
{
+ // on this level constant data should not be released..
+ BOOST_ASSERT_MSG(m_Mean != nullptr, "BatchNormalizationLayer: Mean data should not be null.");
+ BOOST_ASSERT_MSG(m_Variance != nullptr, "BatchNormalizationLayer: Variance data should not be null.");
+ BOOST_ASSERT_MSG(m_Beta != nullptr, "BatchNormalizationLayer: Beta data should not be null.");
+ BOOST_ASSERT_MSG(m_Gamma != nullptr, "BatchNormalizationLayer: Gamma data should not be null.");
+
BatchNormalizationQueueDescriptor descriptor;
descriptor.m_Mean = m_Mean.get();
descriptor.m_Variance = m_Variance.get();
descriptor.m_Beta = m_Beta.get();
descriptor.m_Gamma = m_Gamma.get();
+
return factory.CreateBatchNormalization(descriptor, PrepInfoAndDesc(descriptor, graph));
}
@@ -44,17 +51,22 @@ BatchNormalizationLayer* BatchNormalizationLayer::Clone(Graph& graph) const
void BatchNormalizationLayer::ValidateTensorShapesFromInputs()
{
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection() != nullptr,
- "BatchNormalizationLayer: InputSlot must be connected to an OutputSlot");
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection()->IsTensorInfoSet(),
- "BatchNormalizationLayer: TensorInfo must be set on connected OutputSlot.");
+ VerifyLayerConnections(1, CHECK_LOCATION());
- auto& info = GetInputSlot(0).GetConnection()->GetTensorInfo();
+ auto inferredShapes = InferOutputShapes({ GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape() });
+
+ BOOST_ASSERT(inferredShapes.size() == 1);
ConditionalThrowIfNotEqual<LayerValidationException>(
"BatchNormalizationLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
GetOutputSlot(0).GetTensorInfo().GetShape(),
- info.GetShape());
+ inferredShapes[0]);
+
+}
+
+Layer::ConstantTensors BatchNormalizationLayer::GetConstantTensorsByRef()
+{
+ return {m_Mean, m_Variance, m_Beta, m_Gamma};
}
} // namespace armnn
diff --git a/src/armnn/layers/BatchNormalizationLayer.hpp b/src/armnn/layers/BatchNormalizationLayer.hpp
index d8082e5e98..9a1b5bccc8 100644
--- a/src/armnn/layers/BatchNormalizationLayer.hpp
+++ b/src/armnn/layers/BatchNormalizationLayer.hpp
@@ -29,6 +29,8 @@ public:
protected:
BatchNormalizationLayer(const BatchNormalizationDescriptor& param, const char* name);
~BatchNormalizationLayer() = default;
+
+ ConstantTensors GetConstantTensorsByRef() override;
};
} // namespace
diff --git a/src/armnn/layers/ConstantLayer.cpp b/src/armnn/layers/ConstantLayer.cpp
index 937d38a31d..2abc595605 100644
--- a/src/armnn/layers/ConstantLayer.cpp
+++ b/src/armnn/layers/ConstantLayer.cpp
@@ -13,9 +13,8 @@
namespace armnn
{
-ConstantLayer::ConstantLayer(const std::shared_ptr<ScopedCpuTensorHandle>& input, const char* name)
+ConstantLayer::ConstantLayer(const char* name)
: Layer(0, 1, LayerType::Constant, name)
- , m_LayerOutput(input)
{
}
@@ -29,13 +28,22 @@ std::unique_ptr<IWorkload> ConstantLayer::CreateWorkload(const Graph& graph,
ConstantLayer* ConstantLayer::Clone(Graph& graph) const
{
- // Cloned layers share the same layer output object
- return CloneBase<ConstantLayer>(graph, m_LayerOutput, GetName());
+ // Cloned layers share the same layer output object.
+ auto layer = CloneBase<ConstantLayer>(graph, GetName());
+
+ layer->m_LayerOutput = m_LayerOutput ? std::make_unique<ScopedCpuTensorHandle>(*m_LayerOutput) : nullptr;
+
+ return std::move(layer);
+}
+
+std::vector<TensorShape> ConstantLayer::InferOutputShapes(const std::vector<TensorShape>& inputShapes) const
+{
+ return std::vector<TensorShape>({ m_LayerOutput->GetTensorInfo().GetShape() });
}
void ConstantLayer::ValidateTensorShapesFromInputs()
{
- // get the output shape from the value of the constant layer
+ // Get the output shape from the value of the constant layer.
TensorShape const& outShape = m_LayerOutput->GetTensorInfo().GetShape();
ConditionalThrowIfNotEqual<LayerValidationException>(
"ConstantLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
diff --git a/src/armnn/layers/ConstantLayer.hpp b/src/armnn/layers/ConstantLayer.hpp
index e8e8d2298c..f215832eae 100644
--- a/src/armnn/layers/ConstantLayer.hpp
+++ b/src/armnn/layers/ConstantLayer.hpp
@@ -21,12 +21,18 @@ public:
void ValidateTensorShapesFromInputs() override;
+ std::vector<TensorShape> InferOutputShapes(const std::vector<TensorShape>& inputShapes) const override;
+
+ // Free up the constant source data
+ void ReleaseConstantData() override {};
+
+ std::unique_ptr<ScopedCpuTensorHandle> m_LayerOutput;
protected:
- ConstantLayer(const std::shared_ptr<ScopedCpuTensorHandle>& input, const char* name);
+ ConstantLayer(const char* name);
~ConstantLayer() = default;
-private:
- std::shared_ptr<ScopedCpuTensorHandle> m_LayerOutput;
+ ConstantTensors GetConstantTensorsByRef() override { return {m_LayerOutput}; }
+
};
} // namespace
diff --git a/src/armnn/layers/ConvertFp16ToFp32Layer.cpp b/src/armnn/layers/ConvertFp16ToFp32Layer.cpp
new file mode 100644
index 0000000000..80d981c267
--- /dev/null
+++ b/src/armnn/layers/ConvertFp16ToFp32Layer.cpp
@@ -0,0 +1,48 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// See LICENSE file in the project root for full license information.
+//
+
+#include "ConvertFp16ToFp32Layer.hpp"
+#include "LayerCloneBase.hpp"
+
+#include <armnn/TypesUtils.hpp>
+
+#include <backends/WorkloadData.hpp>
+#include <backends/WorkloadFactory.hpp>
+
+namespace armnn
+{
+
+ConvertFp16ToFp32Layer::ConvertFp16ToFp32Layer(const char* name)
+ : Layer(1, 1, LayerType::ConvertFp16ToFp32, name)
+{
+}
+
+std::unique_ptr<IWorkload> ConvertFp16ToFp32Layer::CreateWorkload(const Graph& graph,
+ const IWorkloadFactory& factory) const
+{
+ ConvertFp16ToFp32QueueDescriptor descriptor;
+ return factory.CreateConvertFp16ToFp32(descriptor, PrepInfoAndDesc(descriptor, graph));
+}
+
+ConvertFp16ToFp32Layer* ConvertFp16ToFp32Layer::Clone(Graph& graph) const
+{
+ return CloneBase<ConvertFp16ToFp32Layer>(graph, GetName());
+}
+
+void ConvertFp16ToFp32Layer::ValidateTensorShapesFromInputs()
+{
+ VerifyLayerConnections(1, CHECK_LOCATION());
+
+ auto inferredShapes = InferOutputShapes({ GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape() });
+
+ BOOST_ASSERT(inferredShapes.size() == 1);
+
+ ConditionalThrowIfNotEqual<LayerValidationException>(
+ "ConvertFp16ToFp32Layer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
+ GetOutputSlot(0).GetTensorInfo().GetShape(),
+ inferredShapes[0]);
+}
+
+} // namespace armnn
diff --git a/src/armnn/layers/ConvertFp16ToFp32Layer.hpp b/src/armnn/layers/ConvertFp16ToFp32Layer.hpp
new file mode 100644
index 0000000000..94f1fb8925
--- /dev/null
+++ b/src/armnn/layers/ConvertFp16ToFp32Layer.hpp
@@ -0,0 +1,28 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// See LICENSE file in the project root for full license information.
+//
+
+#pragma once
+
+#include <Layer.hpp>
+
+namespace armnn
+{
+
+class ConvertFp16ToFp32Layer : public Layer
+{
+public:
+ virtual std::unique_ptr<IWorkload> CreateWorkload(const Graph& graph,
+ const IWorkloadFactory& factory) const override;
+
+ ConvertFp16ToFp32Layer* Clone(Graph& graph) const override;
+
+ void ValidateTensorShapesFromInputs() override;
+
+protected:
+ ConvertFp16ToFp32Layer(const char* name);
+ ~ConvertFp16ToFp32Layer() = default;
+};
+
+} // namespace
diff --git a/src/armnn/layers/ConvertFp32ToFp16Layer.cpp b/src/armnn/layers/ConvertFp32ToFp16Layer.cpp
new file mode 100644
index 0000000000..70d6b668f8
--- /dev/null
+++ b/src/armnn/layers/ConvertFp32ToFp16Layer.cpp
@@ -0,0 +1,47 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// See LICENSE file in the project root for full license information.
+//
+#include "ConvertFp32ToFp16Layer.hpp"
+
+#include "LayerCloneBase.hpp"
+
+#include <armnn/TypesUtils.hpp>
+#include <backends/WorkloadData.hpp>
+#include <backends/WorkloadFactory.hpp>
+
+namespace armnn
+{
+
+ConvertFp32ToFp16Layer::ConvertFp32ToFp16Layer(const char* name)
+ : Layer(1, 1, LayerType::ConvertFp32ToFp16, name)
+{
+}
+
+std::unique_ptr<IWorkload> ConvertFp32ToFp16Layer::CreateWorkload(const Graph& graph,
+ const IWorkloadFactory& factory) const
+{
+ ConvertFp32ToFp16QueueDescriptor descriptor;
+ return factory.CreateConvertFp32ToFp16(descriptor, PrepInfoAndDesc(descriptor, graph));
+}
+
+ConvertFp32ToFp16Layer* ConvertFp32ToFp16Layer::Clone(Graph& graph) const
+{
+ return CloneBase<ConvertFp32ToFp16Layer>(graph, GetName());
+}
+
+void ConvertFp32ToFp16Layer::ValidateTensorShapesFromInputs()
+{
+ VerifyLayerConnections(1, CHECK_LOCATION());
+
+ auto inferredShapes = InferOutputShapes({ GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape() });
+
+ BOOST_ASSERT(inferredShapes.size() == 1);
+
+ ConditionalThrowIfNotEqual<LayerValidationException>(
+ "ConvertFp32ToFp16Layer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
+ GetOutputSlot(0).GetTensorInfo().GetShape(),
+ inferredShapes[0]);
+}
+
+} // namespace armnn
diff --git a/src/armnn/layers/ConvertFp32ToFp16Layer.hpp b/src/armnn/layers/ConvertFp32ToFp16Layer.hpp
new file mode 100644
index 0000000000..5c3883021d
--- /dev/null
+++ b/src/armnn/layers/ConvertFp32ToFp16Layer.hpp
@@ -0,0 +1,27 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// See LICENSE file in the project root for full license information.
+//
+#pragma once
+
+#include <Layer.hpp>
+
+namespace armnn
+{
+
+class ConvertFp32ToFp16Layer : public Layer
+{
+public:
+ virtual std::unique_ptr<IWorkload> CreateWorkload(const Graph& graph,
+ const IWorkloadFactory& factory) const override;
+
+ ConvertFp32ToFp16Layer* Clone(Graph& graph) const override;
+
+ void ValidateTensorShapesFromInputs() override;
+
+protected:
+ ConvertFp32ToFp16Layer(const char* name);
+ ~ConvertFp32ToFp16Layer() = default;
+};
+
+} // namespace
diff --git a/src/armnn/layers/Convolution2dLayer.cpp b/src/armnn/layers/Convolution2dLayer.cpp
index 3829f129bb..05c25bf3a0 100644
--- a/src/armnn/layers/Convolution2dLayer.cpp
+++ b/src/armnn/layers/Convolution2dLayer.cpp
@@ -20,11 +20,15 @@ Convolution2dLayer::Convolution2dLayer(const Convolution2dDescriptor& param, con
std::unique_ptr<IWorkload> Convolution2dLayer::CreateWorkload(const Graph& graph, const IWorkloadFactory& factory) const
{
+ // on this level constant data should not be released..
+ BOOST_ASSERT_MSG(m_Weight != nullptr, "Convolution2dLayer: Weights data should not be null.");
+
Convolution2dQueueDescriptor descriptor;
descriptor.m_Weight = m_Weight.get();
if (m_Param.m_BiasEnabled)
{
+ BOOST_ASSERT_MSG(m_Bias != nullptr, "Convolution2dLayer: Bias data should not be null.");
descriptor.m_Bias = m_Bias.get();
}
return factory.CreateConvolution2d(descriptor, PrepInfoAndDesc(descriptor, graph));
@@ -33,6 +37,7 @@ std::unique_ptr<IWorkload> Convolution2dLayer::CreateWorkload(const Graph& graph
Convolution2dLayer* Convolution2dLayer::Clone(Graph& graph) const
{
auto layer = CloneBase<Convolution2dLayer>(graph, m_Param, GetName());
+
layer->m_Weight = m_Weight ? std::make_unique<ScopedCpuTensorHandle>(*m_Weight) : nullptr;
if (layer->m_Param.m_BiasEnabled)
@@ -43,17 +48,11 @@ Convolution2dLayer* Convolution2dLayer::Clone(Graph& graph) const
return std::move(layer);
}
-void Convolution2dLayer::ValidateTensorShapesFromInputs()
+std::vector<TensorShape> Convolution2dLayer::InferOutputShapes(const std::vector<TensorShape>& inputShapes) const
{
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection() != nullptr,
- "Convolution2dLayer: InputSlot must be connected to an OutputSlot");
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection()->IsTensorInfoSet(),
- "Convolution2dLayer: TensorInfo must be set on connected OutputSlot.");
-
-
- IOutputSlot* input = GetInputSlot(0).GetConnection();
- const TensorShape& inputShape = input->GetTensorInfo().GetShape();
- const TensorShape filterShape = m_Weight->GetTensorInfo().GetShape();
+ BOOST_ASSERT(inputShapes.size() == 2);
+ const TensorShape& inputShape = inputShapes[0];
+ const TensorShape filterShape = inputShapes[1];
// If we support multiple batch dimensions in the future, then this assert will need to change.
BOOST_ASSERT_MSG(inputShape.GetNumDimensions() == 4, "Convolutions will always have 4D input.");
@@ -73,11 +72,31 @@ void Convolution2dLayer::ValidateTensorShapesFromInputs()
unsigned int outChannels = filterShape[0];
unsigned int outBatchSize = inBatchSize;
- TensorShape shapeOut({outBatchSize, outChannels, outHeight, outWidth});
+ return std::vector<TensorShape>({ TensorShape({outBatchSize, outChannels, outHeight, outWidth})});
+}
+
+void Convolution2dLayer::ValidateTensorShapesFromInputs()
+{
+ VerifyLayerConnections(1, CHECK_LOCATION());
+
+ // check if we m_Weight data is not nullptr
+ BOOST_ASSERT_MSG(m_Weight != nullptr, "Convolution2dLayer: Weights data should not be null.");
+
+ auto inferredShapes = InferOutputShapes({
+ GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(),
+ m_Weight->GetTensorInfo().GetShape() });
+
+ BOOST_ASSERT(inferredShapes.size() == 1);
+
ConditionalThrowIfNotEqual<LayerValidationException>(
"Convolution2dLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
GetOutputSlot(0).GetTensorInfo().GetShape(),
- shapeOut);
+ inferredShapes[0]);
+}
+
+Layer::ConstantTensors Convolution2dLayer::GetConstantTensorsByRef()
+{
+ return {m_Weight, m_Bias};
}
} // namespace armnn
diff --git a/src/armnn/layers/Convolution2dLayer.hpp b/src/armnn/layers/Convolution2dLayer.hpp
index 4d2c6505d3..8659fe540d 100644
--- a/src/armnn/layers/Convolution2dLayer.hpp
+++ b/src/armnn/layers/Convolution2dLayer.hpp
@@ -24,9 +24,13 @@ public:
void ValidateTensorShapesFromInputs() override;
+ std::vector<TensorShape> InferOutputShapes(const std::vector<TensorShape>& inputShapes) const override;
+
protected:
Convolution2dLayer(const Convolution2dDescriptor& param, const char* name);
~Convolution2dLayer() = default;
+
+ ConstantTensors GetConstantTensorsByRef() override;
};
} // namespace
diff --git a/src/armnn/layers/DepthwiseConvolution2dLayer.cpp b/src/armnn/layers/DepthwiseConvolution2dLayer.cpp
index 0442de6c60..471bf015a9 100644
--- a/src/armnn/layers/DepthwiseConvolution2dLayer.cpp
+++ b/src/armnn/layers/DepthwiseConvolution2dLayer.cpp
@@ -22,11 +22,15 @@ DepthwiseConvolution2dLayer::DepthwiseConvolution2dLayer(const DepthwiseConvolut
std::unique_ptr<IWorkload> DepthwiseConvolution2dLayer::CreateWorkload(const Graph& graph,
const IWorkloadFactory& factory) const
{
+ // on this level constant data should not be released..
+ BOOST_ASSERT_MSG(m_Weight != nullptr, "DepthwiseConvolution2dLayer: Weights data should not be null.");
+
DepthwiseConvolution2dQueueDescriptor descriptor;
descriptor.m_Weight = m_Weight.get();
if (m_Param.m_BiasEnabled)
{
+ BOOST_ASSERT_MSG(m_Bias != nullptr, "DepthwiseConvolution2dLayer: Bias data should not be null.");
descriptor.m_Bias = m_Bias.get();
}
return factory.CreateDepthwiseConvolution2d(descriptor, PrepInfoAndDesc(descriptor, graph));
@@ -45,16 +49,12 @@ DepthwiseConvolution2dLayer* DepthwiseConvolution2dLayer::Clone(Graph& graph) co
return std::move(layer);
}
-void DepthwiseConvolution2dLayer::ValidateTensorShapesFromInputs()
+std::vector<TensorShape>
+DepthwiseConvolution2dLayer::InferOutputShapes(const std::vector<TensorShape>& inputShapes) const
{
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection() != nullptr,
- "DepthwiseConvolution2dLayer: InputSlot must be connected to an OutputSlot");
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection()->IsTensorInfoSet(),
- "DepthwiseConvolution2dLayer: TensorInfo must be set on connected OutputSlot.");
-
- IOutputSlot* input = GetInputSlot(0).GetConnection();
- const TensorShape& inputShape = input->GetTensorInfo().GetShape();
- const TensorShape filterShape = m_Weight->GetTensorInfo().GetShape();
+ BOOST_ASSERT(inputShapes.size() == 2);
+ const TensorShape& inputShape = inputShapes[0];
+ const TensorShape filterShape = inputShapes[1];
BOOST_ASSERT_MSG(inputShape.GetNumDimensions() == 4, "Convolutions will always have 4D input.");
@@ -74,12 +74,32 @@ void DepthwiseConvolution2dLayer::ValidateTensorShapesFromInputs()
unsigned int outChannels = filterShape[1]*depthMultiplier;
unsigned int outBatchSize = inBatchSize;
- TensorShape outShape({outBatchSize, outChannels, outHeight, outWidth});
+ return std::vector<TensorShape>({ TensorShape({outBatchSize, outChannels, outHeight, outWidth})});
+}
+
+void DepthwiseConvolution2dLayer::ValidateTensorShapesFromInputs()
+{
+ VerifyLayerConnections(1, CHECK_LOCATION());
+
+ // on this level constant data should not be released..
+ BOOST_ASSERT_MSG(m_Weight != nullptr, "DepthwiseConvolution2dLayer: Weights data should not be null.");
+
+ auto inferredShapes = InferOutputShapes({
+ GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(),
+ m_Weight->GetTensorInfo().GetShape()
+ });
+
+ BOOST_ASSERT(inferredShapes.size() == 1);
+
ConditionalThrowIfNotEqual<LayerValidationException>(
- "DepthwiseConvolution2dLayer: "
- "TensorShape set on OutputSlot[0] does not match the inferred shape.",
+ "DepthwiseConvolution2dLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
GetOutputSlot(0).GetTensorInfo().GetShape(),
- outShape);
+ inferredShapes[0]);
+}
+
+Layer::ConstantTensors DepthwiseConvolution2dLayer::GetConstantTensorsByRef()
+{
+ return {m_Weight, m_Bias};
}
} // namespace armnn
diff --git a/src/armnn/layers/DepthwiseConvolution2dLayer.hpp b/src/armnn/layers/DepthwiseConvolution2dLayer.hpp
index 60691bf73c..e3be152432 100644
--- a/src/armnn/layers/DepthwiseConvolution2dLayer.hpp
+++ b/src/armnn/layers/DepthwiseConvolution2dLayer.hpp
@@ -24,9 +24,13 @@ public:
void ValidateTensorShapesFromInputs() override;
+ std::vector<TensorShape> InferOutputShapes(const std::vector<TensorShape>& inputShapes) const override;
+
protected:
DepthwiseConvolution2dLayer(const DepthwiseConvolution2dDescriptor& param, const char* name);
~DepthwiseConvolution2dLayer() = default;
+
+ ConstantTensors GetConstantTensorsByRef() override;
};
} // namespace
diff --git a/src/armnn/layers/FakeQuantizationLayer.cpp b/src/armnn/layers/FakeQuantizationLayer.cpp
index 24b53b2e37..7bda1c1f78 100644
--- a/src/armnn/layers/FakeQuantizationLayer.cpp
+++ b/src/armnn/layers/FakeQuantizationLayer.cpp
@@ -32,20 +32,16 @@ FakeQuantizationLayer* FakeQuantizationLayer::Clone(Graph& graph) const
void FakeQuantizationLayer::ValidateTensorShapesFromInputs()
{
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection() != nullptr,
- "FakeQuantizationLayer: InputSlot must be connected to an OutputSlot");
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection()->IsTensorInfoSet(),
- "FakeQuantizationLayer: TensorInfo must be set on connected OutputSlot.");
+ VerifyLayerConnections(1, CHECK_LOCATION());
+ auto inferredShapes = InferOutputShapes({ GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape() });
- IOutputSlot* input = GetInputSlot(0).GetConnection();
+ BOOST_ASSERT(inferredShapes.size() == 1);
- // input and output shapes are the same
- TensorShape const& outShape = input->GetTensorInfo().GetShape();
ConditionalThrowIfNotEqual<LayerValidationException>(
"FakeQuantizationLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
GetOutputSlot(0).GetTensorInfo().GetShape(),
- outShape);
+ inferredShapes[0]);
}
} // namespace armnn
diff --git a/src/armnn/layers/FloorLayer.cpp b/src/armnn/layers/FloorLayer.cpp
index a9ddcca60c..e88600b354 100644
--- a/src/armnn/layers/FloorLayer.cpp
+++ b/src/armnn/layers/FloorLayer.cpp
@@ -32,18 +32,16 @@ FloorLayer* FloorLayer::Clone(Graph& graph) const
void FloorLayer::ValidateTensorShapesFromInputs()
{
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection() != nullptr,
- "FloorLayer: InputSlot must be connected to an OutputSlot");
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection()->IsTensorInfoSet(),
- "FloorLayer: TensorInfo must be set on connected OutputSlot.");
-
- // input and output shapes are the same
- IOutputSlot* input = GetInputSlot(0).GetConnection();
- TensorShape const& outShape = input->GetTensorInfo().GetShape();
+ VerifyLayerConnections(1, CHECK_LOCATION());
+
+ auto inferredShapes = InferOutputShapes({ GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape() });
+
+ BOOST_ASSERT(inferredShapes.size() == 1);
+
ConditionalThrowIfNotEqual<LayerValidationException>(
"FloorLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
GetOutputSlot(0).GetTensorInfo().GetShape(),
- outShape);
+ inferredShapes[0]);
}
} // namespace armnn
diff --git a/src/armnn/layers/FullyConnectedLayer.cpp b/src/armnn/layers/FullyConnectedLayer.cpp
index 1597e8c2c3..8b8f010bdb 100644
--- a/src/armnn/layers/FullyConnectedLayer.cpp
+++ b/src/armnn/layers/FullyConnectedLayer.cpp
@@ -22,11 +22,15 @@ FullyConnectedLayer::FullyConnectedLayer(const FullyConnectedDescriptor& param,
std::unique_ptr<IWorkload> FullyConnectedLayer::CreateWorkload(const Graph& graph,
const IWorkloadFactory& factory) const
{
+ // on this level constant data should not be released..
+ BOOST_ASSERT_MSG(m_Weight != nullptr, "FullyConnectedLayer: Weights data should not be null.");
+
FullyConnectedQueueDescriptor descriptor;
descriptor.m_Weight = m_Weight.get();
if (m_Param.m_BiasEnabled)
{
+ BOOST_ASSERT_MSG(m_Bias != nullptr, "FullyConnectedLayer: Bias data should not be null.");
descriptor.m_Bias = m_Bias.get();
}
return factory.CreateFullyConnected(descriptor, PrepInfoAndDesc(descriptor, graph));
@@ -45,25 +49,41 @@ FullyConnectedLayer* FullyConnectedLayer::Clone(Graph& graph) const
return std::move(layer);
}
+std::vector<TensorShape> FullyConnectedLayer::InferOutputShapes(const std::vector<TensorShape>& inputShapes) const
+{
+ BOOST_ASSERT(inputShapes.size() == 2);
+ const TensorShape& inputShape = inputShapes[0];
+ const TensorShape weightShape = inputShapes[1];
+
+ // Output for FC is [1, w[1]].
+ unsigned int batches = inputShape[0];
+ unsigned int dimIdx = m_Param.m_TransposeWeightMatrix ? 0 : 1;
+
+ return std::vector<TensorShape>({ TensorShape({batches, weightShape[dimIdx]})});
+}
+
void FullyConnectedLayer::ValidateTensorShapesFromInputs()
{
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection() != nullptr,
- "FullyConnectedLayer: InputSlot must be connected to an OutputSlot");
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection()->IsTensorInfoSet(),
- "FullyConnectedLayer: TensorInfo must be set on connected OutputSlot.");
+ VerifyLayerConnections(1, CHECK_LOCATION());
+ // check if we m_Weight data is not nullptr
+ BOOST_ASSERT_MSG(m_Weight != nullptr, "FullyConnectedLayer: Weights data should not be null.");
- TensorShape const& weightShape = m_Weight->GetTensorInfo().GetShape();
+ auto inferredShapes = InferOutputShapes({
+ GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(),
+ m_Weight->GetTensorInfo().GetShape() });
- // output for FC is [1, w[1]]
- unsigned int batches = GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape()[0];
- unsigned int dimIdx = m_Param.m_TransposeWeightMatrix ? 0 : 1;
- TensorShape outShape({batches, weightShape[dimIdx]});
+ BOOST_ASSERT(inferredShapes.size() == 1);
ConditionalThrowIfNotEqual<LayerValidationException>(
"FullyConnectedLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
GetOutputSlot(0).GetTensorInfo().GetShape(),
- outShape);
+ inferredShapes[0]);
+}
+
+Layer::ConstantTensors FullyConnectedLayer::GetConstantTensorsByRef()
+{
+ return {m_Weight, m_Bias};
}
} // namespace armnn
diff --git a/src/armnn/layers/FullyConnectedLayer.hpp b/src/armnn/layers/FullyConnectedLayer.hpp
index 1d6cb7cf8d..6300cafd62 100644
--- a/src/armnn/layers/FullyConnectedLayer.hpp
+++ b/src/armnn/layers/FullyConnectedLayer.hpp
@@ -23,10 +23,13 @@ public:
FullyConnectedLayer* Clone(Graph& graph) const override;
void ValidateTensorShapesFromInputs() override;
+ std::vector<TensorShape> InferOutputShapes(const std::vector<TensorShape>& inputShapes) const override;
protected:
FullyConnectedLayer(const FullyConnectedDescriptor& param, const char* name);
~FullyConnectedLayer() = default;
+
+ ConstantTensors GetConstantTensorsByRef() override;
};
} // namespace
diff --git a/src/armnn/layers/L2NormalizationLayer.cpp b/src/armnn/layers/L2NormalizationLayer.cpp
index 07020bfdca..7249bc3b5c 100644
--- a/src/armnn/layers/L2NormalizationLayer.cpp
+++ b/src/armnn/layers/L2NormalizationLayer.cpp
@@ -32,19 +32,16 @@ L2NormalizationLayer* L2NormalizationLayer::Clone(Graph& graph) const
void L2NormalizationLayer::ValidateTensorShapesFromInputs()
{
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection() != nullptr,
- "L2NormalizationLayer: InputSlot must be connected to an OutputSlot");
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection()->IsTensorInfoSet(),
- "L2NormalizationLayer: TensorInfo must be set on connected OutputSlot.");
+ VerifyLayerConnections(1, CHECK_LOCATION());
- IOutputSlot* input = GetInputSlot(0).GetConnection();
+ auto inferredShapes = InferOutputShapes({ GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape() });
+
+ BOOST_ASSERT(inferredShapes.size() == 1);
- // input and output shapes are the same
- TensorShape const& outShape = input->GetTensorInfo().GetShape();
ConditionalThrowIfNotEqual<LayerValidationException>(
"L2NormalizationLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
GetOutputSlot(0).GetTensorInfo().GetShape(),
- outShape);
+ inferredShapes[0]);
}
} // namespace armnn
diff --git a/src/armnn/layers/LayerWithParameters.hpp b/src/armnn/layers/LayerWithParameters.hpp
index e3eb40a273..c071c15c21 100644
--- a/src/armnn/layers/LayerWithParameters.hpp
+++ b/src/armnn/layers/LayerWithParameters.hpp
@@ -18,7 +18,7 @@ public:
const Parameters& GetParameters() const { return m_Param; }
/// Helper to serialize the layer parameters to string
- /// (currently used in DotSerializer and company)
+ /// (currently used in DotSerializer and company).
void SerializeLayerParameters(ParameterStringifyFunction & fn) const
{
StringifyLayerParameters<Parameters>::Serialize(fn, m_Param);
@@ -37,7 +37,7 @@ protected:
~LayerWithParameters() = default;
- /// Helper function to reduce duplication in *Layer::CreateWorkload
+ /// Helper function to reduce duplication in *Layer::CreateWorkload.
template <typename QueueDescriptor>
WorkloadInfo PrepInfoAndDesc(QueueDescriptor& descriptor, const Graph& graph) const
{
@@ -45,7 +45,7 @@ protected:
return Layer::PrepInfoAndDesc(descriptor, graph);
}
- /// The parameters for the layer (not including tensor-valued weights etc.)
+ /// The parameters for the layer (not including tensor-valued weights etc.).
Parameters m_Param;
};
diff --git a/src/armnn/layers/LstmLayer.cpp b/src/armnn/layers/LstmLayer.cpp
new file mode 100644
index 0000000000..30c41bc9b8
--- /dev/null
+++ b/src/armnn/layers/LstmLayer.cpp
@@ -0,0 +1,259 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// See LICENSE file in the project root for full license information.
+//
+#include "LstmLayer.hpp"
+
+#include "LayerCloneBase.hpp"
+
+#include <armnn/TypesUtils.hpp>
+#include <backends/CpuTensorHandle.hpp>
+#include <backends/WorkloadFactory.hpp>
+
+namespace armnn
+{
+
+LstmLayer::LstmLayer(const LstmDescriptor& param, const char* name)
+ : LayerWithParameters(3, 4, LayerType::Lstm, param, name)
+{
+}
+
+std::unique_ptr<IWorkload> LstmLayer::CreateWorkload(const Graph& graph, const IWorkloadFactory& factory) const
+{
+ LstmQueueDescriptor descriptor;
+
+ // Basic parameters
+ descriptor.m_InputToForgetWeights = m_BasicParameters.m_InputToForgetWeights.get();
+ descriptor.m_InputToCellWeights = m_BasicParameters.m_InputToCellWeights.get();
+ descriptor.m_InputToOutputWeights = m_BasicParameters.m_InputToOutputWeights.get();
+ descriptor.m_RecurrentToForgetWeights = m_BasicParameters.m_RecurrentToForgetWeights.get();
+ descriptor.m_RecurrentToCellWeights = m_BasicParameters.m_RecurrentToCellWeights.get();
+ descriptor.m_RecurrentToOutputWeights = m_BasicParameters.m_RecurrentToOutputWeights.get();
+ descriptor.m_ForgetGateBias = m_BasicParameters.m_ForgetGateBias.get();
+ descriptor.m_CellBias = m_BasicParameters.m_CellBias.get();
+ descriptor.m_OutputGateBias = m_BasicParameters.m_OutputGateBias.get();
+
+ // Cifg parameters
+ if (!m_Param.m_CifgEnabled)
+ {
+ descriptor.m_InputToInputWeights = m_CifgParameters.m_InputToInputWeights.get();
+ descriptor.m_RecurrentToInputWeights = m_CifgParameters.m_RecurrentToInputWeights.get();
+ descriptor.m_CellToInputWeights = m_CifgParameters.m_CellToInputWeights.get();
+ descriptor.m_InputGateBias = m_CifgParameters.m_InputGateBias.get();
+ }
+
+ // Projection parameters
+ if (m_Param.m_ProjectionEnabled)
+ {
+ descriptor.m_ProjectionWeights = m_ProjectionParameters.m_ProjectionWeights.get();
+ descriptor.m_ProjectionBias = m_ProjectionParameters.m_ProjectionBias.get();
+ }
+
+ // Peephole parameters
+ if (m_Param.m_PeepholeEnabled)
+ {
+ descriptor.m_CellToForgetWeights = m_PeepholeParameters.m_CellToForgetWeights.get();
+ descriptor.m_CellToOutputWeights = m_PeepholeParameters.m_CellToOutputWeights.get();
+ }
+ return factory.CreateLstm(descriptor, PrepInfoAndDesc(descriptor, graph));
+}
+
+LstmLayer* LstmLayer::Clone(Graph& graph) const
+{
+ auto layer = CloneBase<LstmLayer>(graph, m_Param, GetName());
+
+ layer->m_BasicParameters.m_InputToForgetWeights = m_BasicParameters.m_InputToForgetWeights ?
+ std::make_unique<ScopedCpuTensorHandle>(*m_BasicParameters.m_InputToForgetWeights)
+ : nullptr;
+ layer->m_BasicParameters.m_InputToCellWeights = m_BasicParameters.m_InputToCellWeights ?
+ std::make_unique<ScopedCpuTensorHandle>(*m_BasicParameters.m_InputToCellWeights) : nullptr;
+ layer->m_BasicParameters.m_InputToOutputWeights = m_BasicParameters.m_InputToOutputWeights ?
+ std::make_unique<ScopedCpuTensorHandle>(*m_BasicParameters.m_InputToOutputWeights) : nullptr;
+ layer->m_BasicParameters.m_RecurrentToForgetWeights = m_BasicParameters.m_RecurrentToForgetWeights ?
+ std::make_unique<ScopedCpuTensorHandle>(*m_BasicParameters.m_RecurrentToForgetWeights) : nullptr;
+ layer->m_BasicParameters.m_RecurrentToCellWeights = m_BasicParameters.m_RecurrentToCellWeights ?
+ std::make_unique<ScopedCpuTensorHandle>(*m_BasicParameters.m_RecurrentToCellWeights) : nullptr;
+ layer->m_BasicParameters.m_RecurrentToOutputWeights = m_BasicParameters.m_RecurrentToOutputWeights ?
+ std::make_unique<ScopedCpuTensorHandle>(*m_BasicParameters.m_RecurrentToOutputWeights) : nullptr;
+ layer->m_BasicParameters.m_ForgetGateBias = m_BasicParameters.m_ForgetGateBias ?
+ std::make_unique<ScopedCpuTensorHandle>(*m_BasicParameters.m_ForgetGateBias) : nullptr;
+ layer->m_BasicParameters.m_CellBias = m_BasicParameters.m_CellBias ?
+ std::make_unique<ScopedCpuTensorHandle>(*m_BasicParameters.m_CellBias) : nullptr;
+ layer->m_BasicParameters.m_OutputGateBias = m_BasicParameters.m_OutputGateBias ?
+ std::make_unique<ScopedCpuTensorHandle>(*m_BasicParameters.m_OutputGateBias) : nullptr;
+
+ if (!m_Param.m_CifgEnabled)
+ {
+ layer->m_CifgParameters.m_InputToInputWeights = m_CifgParameters.m_InputToInputWeights ?
+ std::make_unique<ScopedCpuTensorHandle>(*m_CifgParameters.m_InputToInputWeights) : nullptr;
+ layer->m_CifgParameters.m_RecurrentToInputWeights = m_CifgParameters.m_RecurrentToInputWeights ?
+ std::make_unique<ScopedCpuTensorHandle>(*m_CifgParameters.m_RecurrentToInputWeights) : nullptr;
+ layer->m_CifgParameters.m_CellToInputWeights = m_CifgParameters.m_CellToInputWeights ?
+ std::make_unique<ScopedCpuTensorHandle>(*m_CifgParameters.m_CellToInputWeights) : nullptr;
+ layer->m_CifgParameters.m_InputGateBias = m_CifgParameters.m_InputGateBias ?
+ std::make_unique<ScopedCpuTensorHandle>(*m_CifgParameters.m_InputGateBias) : nullptr;
+ }
+
+ if (m_Param.m_ProjectionEnabled)
+ {
+ layer->m_ProjectionParameters.m_ProjectionWeights = m_ProjectionParameters.m_ProjectionWeights ?
+ std::make_unique<ScopedCpuTensorHandle>(*m_ProjectionParameters.m_ProjectionWeights) : nullptr;
+ layer->m_ProjectionParameters.m_ProjectionBias = m_ProjectionParameters.m_ProjectionBias ?
+ std::make_unique<ScopedCpuTensorHandle>(*m_ProjectionParameters.m_ProjectionBias) : nullptr;
+ }
+
+ if (m_Param.m_PeepholeEnabled)
+ {
+ layer->m_PeepholeParameters.m_CellToForgetWeights = m_PeepholeParameters.m_CellToForgetWeights ?
+ std::make_unique<ScopedCpuTensorHandle>(*m_PeepholeParameters.m_CellToForgetWeights) : nullptr;
+ layer->m_PeepholeParameters.m_CellToOutputWeights = m_PeepholeParameters.m_CellToOutputWeights ?
+ std::make_unique<ScopedCpuTensorHandle>(*m_PeepholeParameters.m_CellToOutputWeights) : nullptr;
+ }
+
+ return std::move(layer);
+}
+
+std::vector<TensorShape> LstmLayer::InferOutputShapes(const std::vector<TensorShape>& inputShapes) const
+{
+ BOOST_ASSERT(inputShapes.size() == 3);
+
+ // Get input values for validation
+ unsigned int batchSize = inputShapes[0][0];
+ unsigned int outputSize = inputShapes[1][1];
+ unsigned int numUnits = inputShapes[2][1];
+
+ std::vector<TensorShape> outShapes;
+ if (!m_Param.m_CifgEnabled)
+ {
+ outShapes.push_back(TensorShape({batchSize, numUnits*3}));
+ }
+ else
+ {
+ outShapes.push_back(TensorShape({batchSize, numUnits*4}));
+ }
+ outShapes.push_back(TensorShape({batchSize, outputSize}));
+ outShapes.push_back(TensorShape({batchSize, numUnits}));
+ outShapes.push_back(TensorShape({batchSize, outputSize}));
+
+ return outShapes;
+}
+
+void LstmLayer::ValidateTensorShapesFromInputs()
+{
+ VerifyLayerConnections(3, CHECK_LOCATION());
+
+ auto inferredShapes = InferOutputShapes( {
+ GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(),
+ GetInputSlot(1).GetConnection()->GetTensorInfo().GetShape(),
+ GetInputSlot(2).GetConnection()->GetTensorInfo().GetShape()}
+ );
+
+ BOOST_ASSERT(inferredShapes.size() == 4);
+
+ // Check if the weights are nullptr
+ BOOST_ASSERT_MSG(m_BasicParameters.m_InputToForgetWeights != nullptr,
+ "LstmLayer: m_BasicParameters.m_InputToForgetWeights should not be null.");
+ BOOST_ASSERT_MSG(m_BasicParameters.m_InputToCellWeights != nullptr,
+ "LstmLayer: m_BasicParameters.m_InputToCellWeights should not be null.");
+ BOOST_ASSERT_MSG(m_BasicParameters.m_InputToOutputWeights != nullptr,
+ "LstmLayer: m_BasicParameters.m_InputToOutputWeights should not be null.");
+ BOOST_ASSERT_MSG(m_BasicParameters.m_RecurrentToForgetWeights != nullptr,
+ "LstmLayer: m_BasicParameters.m_RecurrentToForgetWeights should not be null.");
+ BOOST_ASSERT_MSG(m_BasicParameters.m_RecurrentToCellWeights != nullptr,
+ "LstmLayer: m_BasicParameters.m_RecurrentToCellWeights should not be null.");
+ BOOST_ASSERT_MSG(m_BasicParameters.m_RecurrentToOutputWeights != nullptr,
+ "LstmLayer: m_BasicParameters.m_RecurrentToOutputWeights should not be null.");
+ BOOST_ASSERT_MSG(m_BasicParameters.m_ForgetGateBias != nullptr,
+ "LstmLayer: m_BasicParameters.m_ForgetGateBias should not be null.");
+ BOOST_ASSERT_MSG(m_BasicParameters.m_CellBias != nullptr,
+ "LstmLayer: m_BasicParameters.m_CellBias should not be null.");
+ BOOST_ASSERT_MSG(m_BasicParameters.m_OutputGateBias != nullptr,
+ "LstmLayer: m_BasicParameters.m_OutputGateBias should not be null.");
+
+ if (!m_Param.m_CifgEnabled)
+ {
+ BOOST_ASSERT_MSG(m_CifgParameters.m_InputToInputWeights != nullptr,
+ "LstmLayer: m_CifgParameters.m_InputToInputWeights should not be null.");
+ BOOST_ASSERT_MSG(m_CifgParameters.m_RecurrentToInputWeights != nullptr,
+ "LstmLayer: m_CifgParameters.m_RecurrentToInputWeights should not be null.");
+ BOOST_ASSERT_MSG(m_CifgParameters.m_InputGateBias != nullptr,
+ "LstmLayer: m_CifgParameters.m_InputGateBias should not be null.");
+
+ ConditionalThrowIfNotEqual<LayerValidationException>(
+ "LstmLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
+ GetOutputSlot(0).GetTensorInfo().GetShape(),
+ inferredShapes[0]);
+ }
+ else
+ {
+ BOOST_ASSERT_MSG(m_CifgParameters.m_InputToInputWeights == nullptr,
+ "LstmLayer: m_CifgParameters.m_InputToInputWeights should not have a value when CIFG is enabled.");
+ BOOST_ASSERT_MSG(m_CifgParameters.m_RecurrentToInputWeights == nullptr,
+ "LstmLayer: m_CifgParameters.m_RecurrentToInputWeights should not have a value when CIFG is enabled.");
+ BOOST_ASSERT_MSG(m_CifgParameters.m_CellToInputWeights == nullptr,
+ "LstmLayer: m_CifgParameters.m_CellToInputWeights should not have a value when CIFG is enabled.");
+ BOOST_ASSERT_MSG(m_CifgParameters.m_InputGateBias == nullptr,
+ "LstmLayer: m_CifgParameters.m_InputGateBias should not have a value when CIFG is enabled.");
+
+ ConditionalThrowIfNotEqual<LayerValidationException>(
+ "LstmLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
+ GetOutputSlot(0).GetTensorInfo().GetShape(),
+ inferredShapes[0]);
+ }
+
+ if (m_Param.m_ProjectionEnabled)
+ {
+ BOOST_ASSERT_MSG(m_ProjectionParameters.m_ProjectionWeights != nullptr,
+ "LstmLayer: m_ProjectionParameters.m_ProjectionWeights should not be null.");
+ }
+
+ if (m_Param.m_PeepholeEnabled)
+ {
+ BOOST_ASSERT_MSG(m_PeepholeParameters.m_CellToForgetWeights != nullptr,
+ "LstmLayer: m_PeepholeParameters.m_CellToForgetWeights should not be null.");
+ BOOST_ASSERT_MSG(m_PeepholeParameters.m_CellToOutputWeights != nullptr,
+ "LstmLayer: m_PeepholeParameters.m_CellToOutputWeights should not be null.");
+ }
+
+ ConditionalThrowIfNotEqual<LayerValidationException>(
+ "LstmLayer: TensorShape set on OutputSlot[1] does not match the inferred shape.",
+ GetOutputSlot(1).GetTensorInfo().GetShape(),
+ inferredShapes[1]);
+ ConditionalThrowIfNotEqual<LayerValidationException>(
+ "LstmLayer: TensorShape set on OutputSlot[2] does not match the inferred shape.",
+ GetOutputSlot(2).GetTensorInfo().GetShape(),
+ inferredShapes[2]);
+ ConditionalThrowIfNotEqual<LayerValidationException>(
+ "LstmLayer: TensorShape set on OutputSlot[3] does not match the inferred shape.",
+ GetOutputSlot(3).GetTensorInfo().GetShape(),
+ inferredShapes[3]);
+}
+
+Layer::ConstantTensors LstmLayer::GetConstantTensorsByRef()
+{
+ return {m_BasicParameters.m_InputToForgetWeights,
+ m_BasicParameters.m_InputToCellWeights,
+ m_BasicParameters.m_InputToOutputWeights,
+ m_BasicParameters.m_RecurrentToForgetWeights,
+ m_BasicParameters.m_RecurrentToCellWeights,
+ m_BasicParameters.m_RecurrentToOutputWeights,
+ m_BasicParameters.m_ForgetGateBias,
+ m_BasicParameters.m_CellBias,
+ m_BasicParameters.m_OutputGateBias,
+
+ // Cifg parameters
+ m_CifgParameters.m_InputToInputWeights,
+ m_CifgParameters.m_RecurrentToInputWeights,
+ m_CifgParameters.m_CellToInputWeights,
+ m_CifgParameters.m_InputGateBias,
+
+ // Projection parameters
+ m_ProjectionParameters.m_ProjectionWeights,
+ m_ProjectionParameters.m_ProjectionBias,
+
+ // Peephole parameters
+ m_PeepholeParameters.m_CellToForgetWeights,
+ m_PeepholeParameters.m_CellToOutputWeights};
+}
+
+} // namespace armnn
diff --git a/src/armnn/layers/LstmLayer.hpp b/src/armnn/layers/LstmLayer.hpp
new file mode 100644
index 0000000000..7133ad26a5
--- /dev/null
+++ b/src/armnn/layers/LstmLayer.hpp
@@ -0,0 +1,70 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// See LICENSE file in the project root for full license information.
+//
+#pragma once
+
+#include "LayerWithParameters.hpp"
+
+namespace armnn
+{
+
+class ScopedCpuTensorHandle;
+
+struct LstmOptCifgParameters
+{
+ std::unique_ptr<ScopedCpuTensorHandle> m_InputToInputWeights;
+ std::unique_ptr<ScopedCpuTensorHandle> m_RecurrentToInputWeights;
+ std::unique_ptr<ScopedCpuTensorHandle> m_CellToInputWeights;
+ std::unique_ptr<ScopedCpuTensorHandle> m_InputGateBias;
+};
+
+struct LstmOptProjectionParameters
+{
+ std::unique_ptr<ScopedCpuTensorHandle> m_ProjectionWeights;
+ std::unique_ptr<ScopedCpuTensorHandle> m_ProjectionBias;
+};
+
+struct LstmOptPeepholeParameters
+{
+ std::unique_ptr<ScopedCpuTensorHandle> m_CellToForgetWeights;
+ std::unique_ptr<ScopedCpuTensorHandle> m_CellToOutputWeights;
+};
+
+struct LstmBasicParameters
+{
+ std::unique_ptr<ScopedCpuTensorHandle> m_InputToForgetWeights;
+ std::unique_ptr<ScopedCpuTensorHandle> m_InputToCellWeights;
+ std::unique_ptr<ScopedCpuTensorHandle> m_InputToOutputWeights;
+ std::unique_ptr<ScopedCpuTensorHandle> m_RecurrentToForgetWeights;
+ std::unique_ptr<ScopedCpuTensorHandle> m_RecurrentToCellWeights;
+ std::unique_ptr<ScopedCpuTensorHandle> m_RecurrentToOutputWeights;
+ std::unique_ptr<ScopedCpuTensorHandle> m_ForgetGateBias;
+ std::unique_ptr<ScopedCpuTensorHandle> m_CellBias;
+ std::unique_ptr<ScopedCpuTensorHandle> m_OutputGateBias;
+};
+
+class LstmLayer : public LayerWithParameters<LstmDescriptor>
+{
+public:
+
+ LstmBasicParameters m_BasicParameters;
+ LstmOptCifgParameters m_CifgParameters;
+ LstmOptProjectionParameters m_ProjectionParameters;
+ LstmOptPeepholeParameters m_PeepholeParameters;
+
+ virtual std::unique_ptr<IWorkload> CreateWorkload(const Graph& graph,
+ const IWorkloadFactory& factory) const override;
+ LstmLayer* Clone(Graph& graph) const override;
+
+ void ValidateTensorShapesFromInputs() override;
+ std::vector<TensorShape> InferOutputShapes(const std::vector<TensorShape>& inputShapes) const override;
+
+protected:
+ LstmLayer(const LstmDescriptor& param, const char* name);
+ ~LstmLayer() = default;
+
+ Layer::ConstantTensors GetConstantTensorsByRef() override;
+};
+
+} // namespace
diff --git a/src/armnn/layers/MemCopyLayer.cpp b/src/armnn/layers/MemCopyLayer.cpp
index 973a756b21..83f77edf58 100644
--- a/src/armnn/layers/MemCopyLayer.cpp
+++ b/src/armnn/layers/MemCopyLayer.cpp
@@ -9,6 +9,7 @@
#include <armnn/TypesUtils.hpp>
#include <backends/WorkloadData.hpp>
#include <backends/WorkloadFactory.hpp>
+#include <backends/MemCopyWorkload.hpp>
namespace armnn
{
@@ -26,23 +27,23 @@ MemCopyLayer* MemCopyLayer::Clone(Graph& graph) const
std::unique_ptr<IWorkload> MemCopyLayer::CreateWorkload(const Graph& graph, const IWorkloadFactory& factory) const
{
MemCopyQueueDescriptor descriptor;
- return factory.CreateMemCopy(descriptor, PrepInfoAndDesc(descriptor, graph));
+
+ //This is different from other workloads. Does not get created by the workload factory.
+ return std::make_unique<CopyMemGenericWorkload>(descriptor, PrepInfoAndDesc(descriptor, graph));
}
void MemCopyLayer::ValidateTensorShapesFromInputs()
{
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection() != nullptr,
- "MemCopyLayer: InputSlot must be connected to an OutputSlot");
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection()->IsTensorInfoSet(),
- "MemCopyLayer: TensorInfo must be set on connected OutputSlot.");
+ VerifyLayerConnections(1, CHECK_LOCATION());
+ auto inferredShapes = InferOutputShapes({ GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape() });
- IOutputSlot* input = GetInputSlot(0).GetConnection();
+ BOOST_ASSERT(inferredShapes.size() == 1);
ConditionalThrowIfNotEqual<LayerValidationException>(
"MemCopyLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
GetOutputSlot(0).GetTensorInfo().GetShape(),
- input->GetTensorInfo().GetShape());
+ inferredShapes[0]);
}
} // namespace armnn
diff --git a/src/armnn/layers/MergerLayer.cpp b/src/armnn/layers/MergerLayer.cpp
index 065fc86a1b..e810b5e0bb 100644
--- a/src/armnn/layers/MergerLayer.cpp
+++ b/src/armnn/layers/MergerLayer.cpp
@@ -23,7 +23,7 @@ std::unique_ptr<IWorkload> MergerLayer::CreateWorkload(const Graph& graph, const
{
MergerQueueDescriptor descriptor;
- // copy the view origins to the descriptor
+ // Copies the view origins to the descriptor.
descriptor.m_ViewOrigins.reserve(m_Param.GetNumViews());
for (unsigned int i = 0; i < m_Param.GetNumViews(); ++i)
{
@@ -36,9 +36,9 @@ std::unique_ptr<IWorkload> MergerLayer::CreateWorkload(const Graph& graph, const
void MergerLayer::CreateTensorHandles(Graph& graph, const IWorkloadFactory& factory)
{
- //if sub tensors are supported than the merger
+ //If sub tensors are supported than the merger
//just needs to make sure that the outputs of the prev layer
- //are made subtensors of the output of the merger layer
+ //are made subtensors of the output of the merger layer.
m_OutputHandlers[0].CreateTensorHandles(factory);
if (factory.SupportsSubTensors())
{
@@ -76,33 +76,28 @@ MergerLayer* MergerLayer::Clone(Graph& graph) const
return CloneBase<MergerLayer>(graph, m_Param, GetName());
}
-void MergerLayer::ValidateTensorShapesFromInputs()
+std::vector<TensorShape> MergerLayer::InferOutputShapes(const std::vector<TensorShape>& inputShapes) const
{
- // Validate Merger layer
- ConditionalThrowIfNotEqual<LayerValidationException>(
- "MergerLayer: Num Inputs must match num views.",
- m_Param.GetNumViews(),
- GetNumInputSlots());
+ BOOST_ASSERT(inputShapes.size() == m_Param.GetNumViews());
unsigned int numDims = m_Param.GetNumDimensions();
- for (unsigned int i=0; i<GetNumInputSlots(); i++)
+ for (unsigned int i=0; i< inputShapes.size(); i++)
{
- auto& inputInfo = GetInputSlot(i).GetConnection()->GetTensorInfo();
+ auto& inputShape = inputShapes[i];
- boost::ignore_unused(inputInfo);
ConditionalThrowIfNotEqual<LayerValidationException>(
"MergerLayer: Num Dimensions must match all inputs.",
numDims,
- inputInfo.GetNumDimensions());
+ inputShape.GetNumDimensions());
}
- // Find the bounding box (extents) of all the views
+ // Finds the bounding box (extents) of all the views.
std::vector<unsigned int> extentMin(numDims);
std::vector<unsigned int> extentMax(numDims);
- for (unsigned int i = 0; i < GetNumInputSlots(); i++)
+ for (unsigned int i = 0; i < inputShapes.size(); i++)
{
const uint32_t* origin = m_Param.GetViewOrigin(i);
- const armnn::TensorShape& shape = GetInputSlot(i).GetConnection()->GetTensorInfo().GetShape();
+ const armnn::TensorShape& shape = inputShapes[i];
for (unsigned int d = 0; d < numDims; d++)
{
extentMin[d] = std::min(extentMin[d], origin[d]);
@@ -110,23 +105,23 @@ void MergerLayer::ValidateTensorShapesFromInputs()
}
}
- // Check that the bounding box starts at the origin
+ // Checks that the bounding box starts at the origin.
if (!std::all_of(extentMin.begin(), extentMin.end(), [](unsigned int s) { return s == 0; }))
{
throw LayerValidationException("MergerLayer: there is no view that starts at the origin");
}
- // Check that there are no overlaps of views (this would lead to undefined output at those locations).
- // Check each pair of views against each other
- // (and don't bother to check against self, or check the same pair both ways round)
- for (unsigned int a = 0; a < GetNumInputSlots(); a++)
+ // Checks that there are no overlaps of views (this would lead to undefined output at those locations).
+ // Checks each pair of views against each other
+ // (and doesn't bother to check against self, or check the same pair both ways round).
+ for (unsigned int a = 0; a < inputShapes.size(); a++)
{
const uint32_t* aOrigin = m_Param.GetViewOrigin(a);
- const armnn::TensorShape& aShape = GetInputSlot(a).GetConnection()->GetTensorInfo().GetShape();
+ const armnn::TensorShape& aShape = inputShapes[a];
for (unsigned int b = 0; b < a; b++)
{
const uint32_t* bOrigin = m_Param.GetViewOrigin(b);
- const armnn::TensorShape& bShape = GetInputSlot(b).GetConnection()->GetTensorInfo().GetShape();
+ const armnn::TensorShape& bShape = inputShapes[b];
bool allAxesOverlap = true;
for (unsigned int d = 0; d < numDims && allAxesOverlap; d++)
@@ -149,13 +144,13 @@ void MergerLayer::ValidateTensorShapesFromInputs()
}
}
- // Check that there are no "holes", i.e. regions of the output which is not covered by a view.
+ // Checks that there are no "holes", i.e. regions of the output which is not covered by a view.
// Because we already checked that there are no overlaps, this can be done simply by checking that
// the total 'volume' of the views is the same as the output.
unsigned int totalViewsVolume = 0;
- for (unsigned int i = 0; i < GetNumInputSlots(); i++)
+ for (unsigned int i = 0; i < inputShapes.size(); i++)
{
- totalViewsVolume += GetInputSlot(i).GetConnection()->GetTensorInfo().GetNumElements();
+ totalViewsVolume += inputShapes[i].GetNumElements();
}
unsigned int outputVolume = 1;
for (unsigned int d = 0; d < numDims; d++)
@@ -168,11 +163,33 @@ void MergerLayer::ValidateTensorShapesFromInputs()
totalViewsVolume,
outputVolume);
- TensorShape outShape(numDims, extentMax.data());
+ return std::vector<TensorShape>({ TensorShape({numDims, extentMax.data()}) });
+}
+
+void MergerLayer::ValidateTensorShapesFromInputs()
+{
+ // Validates Merger layer.
+ ConditionalThrowIfNotEqual<LayerValidationException>(
+ "MergerLayer: Num Inputs must match num views.",
+ m_Param.GetNumViews(),
+ GetNumInputSlots());
+
+ VerifyLayerConnections(m_Param.GetNumViews(), CHECK_LOCATION());
+
+ std::vector<TensorShape> inputShapes;
+ for (uint i = 0; i < GetNumInputSlots(); ++i)
+ {
+ inputShapes.push_back(GetInputSlot(i).GetConnection()->GetTensorInfo().GetShape());
+ }
+
+ auto inferredShapes = InferOutputShapes(inputShapes);
+
+ BOOST_ASSERT(inferredShapes.size() == 1);
+
ConditionalThrowIfNotEqual<LayerValidationException>(
"MergerLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
GetOutputSlot(0).GetTensorInfo().GetShape(),
- outShape);
+ inferredShapes[0]);
}
} // namespace armnn armnn
diff --git a/src/armnn/layers/MergerLayer.hpp b/src/armnn/layers/MergerLayer.hpp
index ad94cb5f3a..b6261027d4 100644
--- a/src/armnn/layers/MergerLayer.hpp
+++ b/src/armnn/layers/MergerLayer.hpp
@@ -19,6 +19,7 @@ public:
MergerLayer* Clone(Graph& graph) const override;
void ValidateTensorShapesFromInputs() override;
+ std::vector<TensorShape> InferOutputShapes(const std::vector<TensorShape>& inputShapes) const override;
protected:
MergerLayer(const OriginsDescriptor& param, const char* name);
diff --git a/src/armnn/layers/MultiplicationLayer.cpp b/src/armnn/layers/MultiplicationLayer.cpp
index af40a23007..ed7683da5f 100644
--- a/src/armnn/layers/MultiplicationLayer.cpp
+++ b/src/armnn/layers/MultiplicationLayer.cpp
@@ -31,41 +31,51 @@ MultiplicationLayer* MultiplicationLayer::Clone(Graph& graph) const
return CloneBase<MultiplicationLayer>(graph, GetName());
}
-void MultiplicationLayer::ValidateTensorShapesFromInputs()
+std::vector<TensorShape> MultiplicationLayer::InferOutputShapes(const std::vector<TensorShape>& inputShapes) const
{
- auto& input0 = GetInputSlot(0).GetConnection()->GetTensorInfo();
- auto& input1 = GetInputSlot(1).GetConnection()->GetTensorInfo();
+ BOOST_ASSERT(inputShapes.size() == 2);
+ auto& input0 = inputShapes[0];
+ auto& input1 = inputShapes[1];
- // Get the max of the inputs
+ // Get the max of the inputs.
BOOST_ASSERT(input0.GetNumDimensions() == input1.GetNumDimensions());
unsigned int numDims = input0.GetNumDimensions();
std::vector<unsigned int> dims(numDims);
- // validate inputs are broadcast compatible
-#if !NDEBUG
for (unsigned int i = 0; i < numDims; i++)
{
- unsigned int dim0 = input0.GetShape()[i];
- unsigned int dim1 = input1.GetShape()[i];
+ unsigned int dim0 = input0[i];
+ unsigned int dim1 = input1[i];
+
+ // Validates inputs are broadcast compatible.
+#if !NDEBUG
if (dim0 != dim1)
{
BOOST_ASSERT_MSG(dim0 == 1 || dim1 == 1, "Dimensions should either match or one should be of size 1.");
}
- }
#endif
- for (unsigned int i = 0; i < numDims; i++)
- {
- unsigned int dim0 = input0.GetShape()[i];
- unsigned int dim1 = input1.GetShape()[i];
dims[i] = std::max(dim0, dim1);
}
- TensorShape outShape(numDims, dims.data());
+ return std::vector<TensorShape>({ TensorShape(numDims, dims.data()) });
+}
+
+void MultiplicationLayer::ValidateTensorShapesFromInputs()
+{
+ VerifyLayerConnections(2, CHECK_LOCATION());
+
+ auto inferredShapes = InferOutputShapes({
+ GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(),
+ GetInputSlot(1).GetConnection()->GetTensorInfo().GetShape()
+ });
+
+ BOOST_ASSERT(inferredShapes.size() == 1);
+
ConditionalThrowIfNotEqual<LayerValidationException>(
"MultiplicationLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
GetOutputSlot(0).GetTensorInfo().GetShape(),
- outShape);
+ inferredShapes[0]);
}
} // namespace armnn
diff --git a/src/armnn/layers/MultiplicationLayer.hpp b/src/armnn/layers/MultiplicationLayer.hpp
index 48db9f4d01..bbfd1ee694 100644
--- a/src/armnn/layers/MultiplicationLayer.hpp
+++ b/src/armnn/layers/MultiplicationLayer.hpp
@@ -18,6 +18,7 @@ public:
MultiplicationLayer* Clone(Graph& graph) const override;
void ValidateTensorShapesFromInputs() override;
+ std::vector<TensorShape> InferOutputShapes(const std::vector<TensorShape>& inputShapes) const override;
protected:
MultiplicationLayer(const char* name);
diff --git a/src/armnn/layers/NormalizationLayer.cpp b/src/armnn/layers/NormalizationLayer.cpp
index cacd348444..261b16a307 100644
--- a/src/armnn/layers/NormalizationLayer.cpp
+++ b/src/armnn/layers/NormalizationLayer.cpp
@@ -31,14 +31,16 @@ NormalizationLayer* NormalizationLayer::Clone(Graph& graph) const
void NormalizationLayer::ValidateTensorShapesFromInputs()
{
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection() != nullptr,
- "NormalizationLayer: Input slot must be connected.");
+ VerifyLayerConnections(1, CHECK_LOCATION());
+
+ auto inferredShapes = InferOutputShapes({ GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape() });
+
+ BOOST_ASSERT(inferredShapes.size() == 1);
- const TensorShape& outShape = GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape();
ConditionalThrowIfNotEqual<LayerValidationException>(
"NormalizationLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
GetOutputSlot(0).GetTensorInfo().GetShape(),
- outShape);
+ inferredShapes[0]);
}
} // namespace armnn
diff --git a/src/armnn/layers/OutputLayer.cpp b/src/armnn/layers/OutputLayer.cpp
index cadcf2da2f..748f275d74 100644
--- a/src/armnn/layers/OutputLayer.cpp
+++ b/src/armnn/layers/OutputLayer.cpp
@@ -29,7 +29,7 @@ OutputLayer* OutputLayer::Clone(Graph& graph) const
void OutputLayer::ValidateTensorShapesFromInputs()
{
- // Just validate the input is connected
+ // Just validates that the input is connected.
ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection() != nullptr,
"OutputLayer: Input slot must be connected.");
}
diff --git a/src/armnn/layers/PermuteLayer.cpp b/src/armnn/layers/PermuteLayer.cpp
index 35692756a1..444de81320 100644
--- a/src/armnn/layers/PermuteLayer.cpp
+++ b/src/armnn/layers/PermuteLayer.cpp
@@ -31,19 +31,25 @@ PermuteLayer* PermuteLayer::Clone(Graph& graph) const
return CloneBase<PermuteLayer>(graph, m_Param, GetName());
}
+std::vector<TensorShape> PermuteLayer::InferOutputShapes(const std::vector<TensorShape>& inputShapes) const
+{
+ BOOST_ASSERT(inputShapes.size() == 1);
+ const TensorShape& inShape = inputShapes[0];
+ return std::vector<TensorShape> ({armnnUtils::Permuted(inShape, m_Param.m_DimMappings)});
+}
+
void PermuteLayer::ValidateTensorShapesFromInputs()
{
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection() != nullptr,
- "PermuteLayer: InputSlot must be connected to an OutputSlot");
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection()->IsTensorInfoSet(),
- "PermuteLayer: TensorInfo must be set on connected InputSlot.");
+ VerifyLayerConnections(1, CHECK_LOCATION());
+
+ auto inferredShapes = InferOutputShapes({ GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape() });
+
+ BOOST_ASSERT(inferredShapes.size() == 1);
- const TensorInfo& infoIn = GetInputSlot(0).GetConnection()->GetTensorInfo();
- TensorShape shapeOut = armnnUtils::Permuted(infoIn.GetShape(), m_Param.m_DimMappings);
ConditionalThrowIfNotEqual<LayerValidationException>(
"PermuteLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
GetOutputSlot(0).GetTensorInfo().GetShape(),
- shapeOut);
+ inferredShapes[0]);
}
} // namespace armnn
diff --git a/src/armnn/layers/PermuteLayer.hpp b/src/armnn/layers/PermuteLayer.hpp
index c060a16390..2700dd2c7b 100644
--- a/src/armnn/layers/PermuteLayer.hpp
+++ b/src/armnn/layers/PermuteLayer.hpp
@@ -18,6 +18,7 @@ public:
PermuteLayer* Clone(Graph& graph) const override;
void ValidateTensorShapesFromInputs() override;
+ std::vector<TensorShape> InferOutputShapes(const std::vector<TensorShape>& inputShapes) const override;
const PermutationVector& GetPermutation() const
{
diff --git a/src/armnn/layers/Pooling2dLayer.cpp b/src/armnn/layers/Pooling2dLayer.cpp
index ede37d7604..68049101e7 100644
--- a/src/armnn/layers/Pooling2dLayer.cpp
+++ b/src/armnn/layers/Pooling2dLayer.cpp
@@ -29,15 +29,10 @@ Pooling2dLayer* Pooling2dLayer::Clone(Graph& graph) const
return CloneBase<Pooling2dLayer>(graph, m_Param, GetName());
}
-void Pooling2dLayer::ValidateTensorShapesFromInputs()
+std::vector<TensorShape> Pooling2dLayer::InferOutputShapes(const std::vector<TensorShape>& inputShapes) const
{
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection() != nullptr,
- "Pooling2dLayer: InputSlot must be connected to an OutputSlot");
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection()->IsTensorInfoSet(),
- "Pooling2dLayer: TensorInfo must be set on connected InputSlot.");
-
- IOutputSlot* input = GetInputSlot(0).GetConnection();
- const TensorShape& inputShape = input->GetTensorInfo().GetShape();
+ BOOST_ASSERT(inputShapes.size() == 1);
+ const TensorShape& inputShape = inputShapes[0];
// If we support multiple batch dimensions in the future, then this assert will need to change.
BOOST_ASSERT_MSG(inputShape.GetNumDimensions() == 4, "Pooling2dLayer will always have 4D input.");
@@ -75,8 +70,8 @@ void Pooling2dLayer::ValidateTensorShapesFromInputs()
BOOST_ASSERT_MSG(false, "Unsupported Output Shape Rounding");
}
- // Make sure that border operations will start from inside the input and not the padded area
- // This is what both Caffe and CL does...
+ // MakeS sure that border operations will start from inside the input and not the padded area.
+ // This is what both Caffe and CL do...
if ((size - 1)*stride >= inSize + lowPad)
{
--size;
@@ -89,18 +84,25 @@ void Pooling2dLayer::ValidateTensorShapesFromInputs()
m_Param.m_PaddingMethod, m_Param.m_OutputShapeRounding);
outHeight= CalcSize(inHeight, m_Param.m_PadTop, m_Param.m_PadBottom, m_Param.m_PoolHeight, m_Param.m_StrideY,
m_Param.m_PaddingMethod, m_Param.m_OutputShapeRounding);
-
-
}
unsigned int outChannels = inChannels;
unsigned int outBatchSize = inBatchSize;
- TensorShape shapeOut({outBatchSize, outChannels, outHeight, outWidth});
+ return std::vector<TensorShape>({ TensorShape({outBatchSize, outChannels, outHeight, outWidth}) });
+}
+
+void Pooling2dLayer::ValidateTensorShapesFromInputs()
+{
+ VerifyLayerConnections(1, CHECK_LOCATION());
+
+ auto inferredShapes = InferOutputShapes({ GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape() });
+
+ BOOST_ASSERT(inferredShapes.size() == 1);
ConditionalThrowIfNotEqual<LayerValidationException>(
"Pooling2dLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
GetOutputSlot(0).GetTensorInfo().GetShape(),
- shapeOut);
+ inferredShapes[0]);
}
} // namespace armnn
diff --git a/src/armnn/layers/Pooling2dLayer.hpp b/src/armnn/layers/Pooling2dLayer.hpp
index af39dbb5ec..d5950d6ec3 100644
--- a/src/armnn/layers/Pooling2dLayer.hpp
+++ b/src/armnn/layers/Pooling2dLayer.hpp
@@ -9,19 +9,20 @@
namespace armnn
{
-class SoftmaxLayer : public LayerWithParameters<SoftmaxDescriptor>
+class Pooling2dLayer : public LayerWithParameters<Pooling2dDescriptor>
{
public:
virtual std::unique_ptr<IWorkload> CreateWorkload(const Graph& graph,
const IWorkloadFactory& factory) const override;
- SoftmaxLayer* Clone(Graph& graph) const override;
+ Pooling2dLayer* Clone(Graph& graph) const override;
void ValidateTensorShapesFromInputs() override;
+ std::vector<TensorShape> InferOutputShapes(const std::vector<TensorShape>& inputShapes) const override;
protected:
- SoftmaxLayer(const SoftmaxDescriptor& param, const char* name);
- ~SoftmaxLayer() = default;
+ Pooling2dLayer(const Pooling2dDescriptor& param, const char* name);
+ ~Pooling2dLayer() = default;
};
} // namespace
diff --git a/src/armnn/layers/ReshapeLayer.cpp b/src/armnn/layers/ReshapeLayer.cpp
index df5d9d5bb0..248a45c491 100644
--- a/src/armnn/layers/ReshapeLayer.cpp
+++ b/src/armnn/layers/ReshapeLayer.cpp
@@ -30,17 +30,23 @@ ReshapeLayer* ReshapeLayer::Clone(Graph& graph) const
return CloneBase<ReshapeLayer>(graph, m_Param, GetName());
}
+std::vector<TensorShape> ReshapeLayer::InferOutputShapes(const std::vector<TensorShape>& inputShapes) const
+{
+ return std::vector<TensorShape>({ m_Param.m_TargetShape });
+}
+
void ReshapeLayer::ValidateTensorShapesFromInputs()
{
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection() != nullptr,
- "ReshapeLayer: InputSlot must be connected to an OutputSlot");
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection()->IsTensorInfoSet(),
- "ReshapeLayer: TensorInfo must be set on connected OutputSlot.");
+ VerifyLayerConnections(1, CHECK_LOCATION());
+
+ auto inferredShapes = InferOutputShapes({ });
+
+ BOOST_ASSERT(inferredShapes.size() == 1);
ConditionalThrowIfNotEqual<LayerValidationException>(
"ReshapeLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
GetOutputSlot(0).GetTensorInfo().GetShape(),
- m_Param.m_TargetShape);
+ inferredShapes[0]);
}
} // namespace armnn
diff --git a/src/armnn/layers/ReshapeLayer.hpp b/src/armnn/layers/ReshapeLayer.hpp
index 8a3cf3a698..4435ba9bf8 100644
--- a/src/armnn/layers/ReshapeLayer.hpp
+++ b/src/armnn/layers/ReshapeLayer.hpp
@@ -18,6 +18,7 @@ public:
ReshapeLayer* Clone(Graph& graph) const override;
void ValidateTensorShapesFromInputs() override;
+ std::vector<TensorShape> InferOutputShapes(const std::vector<TensorShape>& inputShapes) const override;
bool IsEqual(const Layer& other) const
{
diff --git a/src/armnn/layers/ResizeBilinearLayer.cpp b/src/armnn/layers/ResizeBilinearLayer.cpp
index 204d5afae8..6477fa375a 100644
--- a/src/armnn/layers/ResizeBilinearLayer.cpp
+++ b/src/armnn/layers/ResizeBilinearLayer.cpp
@@ -30,23 +30,31 @@ ResizeBilinearLayer* ResizeBilinearLayer::Clone(Graph& graph) const
return CloneBase<ResizeBilinearLayer>(graph, m_Param, GetName());
}
-void ResizeBilinearLayer::ValidateTensorShapesFromInputs()
+std::vector<TensorShape> ResizeBilinearLayer::InferOutputShapes(const std::vector<TensorShape>& inputShapes) const
{
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection() != nullptr,
- "MemCopyLayer: InputSlot must be connected to an OutputSlot");
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection()->IsTensorInfoSet(),
- "MemCopyLayer: TensorInfo must be set on connected OutputSlot.");
+ BOOST_ASSERT(inputShapes.size() == 1);
+ const TensorShape& inputShape = inputShapes[0];
- const TensorShape& inputShape = GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape();
unsigned int outWidth = m_Param.m_TargetWidth;
unsigned int outHeight = m_Param.m_TargetHeight;
unsigned int outChannels = inputShape[1];
unsigned int outBatch = inputShape[0];
- TensorShape outShape({outBatch, outChannels, outHeight, outWidth});
+
+ return std::vector<TensorShape>({ TensorShape({outBatch, outChannels, outHeight, outWidth}) });
+}
+
+void ResizeBilinearLayer::ValidateTensorShapesFromInputs()
+{
+ VerifyLayerConnections(1, CHECK_LOCATION());
+
+ auto inferredShapes = InferOutputShapes({ GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape() });
+
+ BOOST_ASSERT(inferredShapes.size() == 1);
+
ConditionalThrowIfNotEqual<LayerValidationException>(
"ResizeBilinearLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
GetOutputSlot(0).GetTensorInfo().GetShape(),
- outShape);
+ inferredShapes[0]);
}
} // namespace armnn
diff --git a/src/armnn/layers/ResizeBilinearLayer.hpp b/src/armnn/layers/ResizeBilinearLayer.hpp
index 2cefedb0b8..e6798ce531 100644
--- a/src/armnn/layers/ResizeBilinearLayer.hpp
+++ b/src/armnn/layers/ResizeBilinearLayer.hpp
@@ -18,6 +18,7 @@ public:
ResizeBilinearLayer* Clone(Graph& graph) const override;
void ValidateTensorShapesFromInputs() override;
+ std::vector<TensorShape> InferOutputShapes(const std::vector<TensorShape>& inputShapes) const override;
protected:
ResizeBilinearLayer(const ResizeBilinearDescriptor& param, const char* name);
diff --git a/src/armnn/layers/SoftmaxLayer.cpp b/src/armnn/layers/SoftmaxLayer.cpp
index 2bd0c1d106..7c42b7a3c9 100644
--- a/src/armnn/layers/SoftmaxLayer.cpp
+++ b/src/armnn/layers/SoftmaxLayer.cpp
@@ -31,14 +31,16 @@ SoftmaxLayer* SoftmaxLayer::Clone(Graph& graph) const
void SoftmaxLayer::ValidateTensorShapesFromInputs()
{
- ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection() != nullptr,
- "SoftmaxLayer: Input slot must be connected.");
+ VerifyLayerConnections(1, CHECK_LOCATION());
+
+ auto inferredShapes = InferOutputShapes({ GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape() });
+
+ BOOST_ASSERT(inferredShapes.size() == 1);
- const TensorShape& outShape = GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape();
ConditionalThrowIfNotEqual<LayerValidationException>(
"SoftmaxLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
GetOutputSlot(0).GetTensorInfo().GetShape(),
- outShape);
+ inferredShapes[0]);
}
} // namespace armnn
diff --git a/src/armnn/layers/SoftmaxLayer.hpp b/src/armnn/layers/SoftmaxLayer.hpp
index ff60a08a91..af39dbb5ec 100644
--- a/src/armnn/layers/SoftmaxLayer.hpp
+++ b/src/armnn/layers/SoftmaxLayer.hpp
@@ -9,19 +9,19 @@
namespace armnn
{
-class Pooling2dLayer : public LayerWithParameters<Pooling2dDescriptor>
+class SoftmaxLayer : public LayerWithParameters<SoftmaxDescriptor>
{
public:
virtual std::unique_ptr<IWorkload> CreateWorkload(const Graph& graph,
const IWorkloadFactory& factory) const override;
- Pooling2dLayer* Clone(Graph& graph) const override;
+ SoftmaxLayer* Clone(Graph& graph) const override;
void ValidateTensorShapesFromInputs() override;
protected:
- Pooling2dLayer(const Pooling2dDescriptor& param, const char* name);
- ~Pooling2dLayer() = default;
+ SoftmaxLayer(const SoftmaxDescriptor& param, const char* name);
+ ~SoftmaxLayer() = default;
};
} // namespace
diff --git a/src/armnn/layers/SplitterLayer.cpp b/src/armnn/layers/SplitterLayer.cpp
index 630921e4d8..5e737a245e 100644
--- a/src/armnn/layers/SplitterLayer.cpp
+++ b/src/armnn/layers/SplitterLayer.cpp
@@ -22,7 +22,7 @@ std::unique_ptr<IWorkload> SplitterLayer::CreateWorkload(const Graph& graph, con
{
SplitterQueueDescriptor descriptor;
- // copy the window origins to the descriptor
+ // Copies the window origins to the descriptor.
for (unsigned int i = 0; i < m_Param.GetNumViews(); ++i)
{
descriptor.m_ViewOrigins.emplace_back(
@@ -34,14 +34,14 @@ std::unique_ptr<IWorkload> SplitterLayer::CreateWorkload(const Graph& graph, con
void SplitterLayer::CreateTensorHandles(Graph& graph, const IWorkloadFactory& factory)
{
- //if sub tensors are supported than all the "splitter" need to do is to
+ //If sub tensors are supported than all the "splitter" need to do is to
//set the outputs to be appropriate sub tensors of the input.
if (factory.SupportsSubTensors())
{
const OutputHandler& outputHandler = GetInputSlots()[0].GetConnectedOutputSlot()->GetOutputHandler();
ITensorHandle* inputData = outputHandler.GetData();
- //create the outputs as subtensors of the input
+ //Creates the outputs as subtensors of the input.
for (unsigned int i = 0; i < m_Param.GetNumViews(); ++i)
{
m_OutputHandlers[i].SetData(factory.CreateSubTensorHandle(*inputData,
@@ -63,18 +63,38 @@ SplitterLayer* SplitterLayer::Clone(Graph& graph) const
return CloneBase<SplitterLayer>(graph, m_Param, GetName());
}
-void SplitterLayer::ValidateTensorShapesFromInputs()
+std::vector<TensorShape> SplitterLayer::InferOutputShapes(const std::vector<TensorShape>& inputShapes) const
{
+ BOOST_ASSERT(inputShapes.size() == m_Param.GetNumViews());
+ std::vector<TensorShape> outShapes;
//Output shapes must match View shapes.
for (unsigned int viewIdx = 0; viewIdx < m_Param.GetNumViews(); viewIdx++)
{
const uint32_t* sizes = m_Param.GetViewSizes(viewIdx);
+ outShapes.push_back(TensorShape(m_Param.GetNumDimensions(), sizes));
+ }
+ return outShapes;
+}
+
+void SplitterLayer::ValidateTensorShapesFromInputs()
+{
+ std::vector<TensorShape> views;
+ for (unsigned int viewIdx = 0; viewIdx < m_Param.GetNumViews(); viewIdx++)
+ {
+ const uint32_t* sizes = m_Param.GetViewSizes(viewIdx);
+ views.push_back(TensorShape(m_Param.GetNumDimensions(), sizes));
+ }
+
+ auto inferredShapes = InferOutputShapes(views);
- TensorShape outShape(m_Param.GetNumDimensions(), sizes);
+ BOOST_ASSERT(inferredShapes.size() == m_Param.GetNumViews());
+
+ for (unsigned int viewIdx = 0; viewIdx < m_Param.GetNumViews(); viewIdx++)
+ {
ConditionalThrowIfNotEqual<LayerValidationException>(
"SplitterLayer: View sizes must match output tensor shapes.",
GetOutputSlot(viewIdx).GetTensorInfo().GetShape(),
- outShape);
+ inferredShapes[viewIdx]);
}
}
diff --git a/src/armnn/layers/SplitterLayer.hpp b/src/armnn/layers/SplitterLayer.hpp
index 7e5bbd2668..8e361b4d5c 100644
--- a/src/armnn/layers/SplitterLayer.hpp
+++ b/src/armnn/layers/SplitterLayer.hpp
@@ -19,6 +19,7 @@ public:
SplitterLayer* Clone(Graph& graph) const override;
void ValidateTensorShapesFromInputs() override;
+ std::vector<TensorShape> InferOutputShapes(const std::vector<TensorShape>& inputShapes) const override;
protected:
SplitterLayer(const ViewsDescriptor& param, const char* name);