From 3537c2ca7ebf31c1673b9ec2bb0c17b0406bbae0 Mon Sep 17 00:00:00 2001 From: surmeh01 Date: Fri, 18 May 2018 16:31:43 +0100 Subject: Release 18.05 --- src/armnn/layers/ActivationLayer.cpp | 41 ++++++ src/armnn/layers/ActivationLayer.hpp | 27 ++++ src/armnn/layers/AdditionLayer.cpp | 68 +++++++++ src/armnn/layers/AdditionLayer.hpp | 27 ++++ src/armnn/layers/BatchNormalizationLayer.cpp | 60 ++++++++ src/armnn/layers/BatchNormalizationLayer.hpp | 34 +++++ src/armnn/layers/ConstantLayer.cpp | 46 ++++++ src/armnn/layers/ConstantLayer.hpp | 32 ++++ src/armnn/layers/Convolution2dLayer.cpp | 83 +++++++++++ src/armnn/layers/Convolution2dLayer.hpp | 32 ++++ src/armnn/layers/DepthwiseConvolution2dLayer.cpp | 85 +++++++++++ src/armnn/layers/DepthwiseConvolution2dLayer.hpp | 32 ++++ src/armnn/layers/FakeQuantizationLayer.cpp | 51 +++++++ src/armnn/layers/FakeQuantizationLayer.hpp | 27 ++++ src/armnn/layers/FloorLayer.cpp | 49 +++++++ src/armnn/layers/FloorLayer.hpp | 27 ++++ src/armnn/layers/FullyConnectedLayer.cpp | 69 +++++++++ src/armnn/layers/FullyConnectedLayer.hpp | 32 ++++ src/armnn/layers/InputLayer.cpp | 37 +++++ src/armnn/layers/InputLayer.hpp | 27 ++++ src/armnn/layers/L2NormalizationLayer.cpp | 50 +++++++ src/armnn/layers/L2NormalizationLayer.hpp | 27 ++++ src/armnn/layers/LayerCloneBase.hpp | 24 +++ src/armnn/layers/LayerWithParameters.hpp | 52 +++++++ src/armnn/layers/MemCopyLayer.cpp | 48 ++++++ src/armnn/layers/MemCopyLayer.hpp | 27 ++++ src/armnn/layers/MergerLayer.cpp | 178 +++++++++++++++++++++++ src/armnn/layers/MergerLayer.hpp | 28 ++++ src/armnn/layers/MultiplicationLayer.cpp | 71 +++++++++ src/armnn/layers/MultiplicationLayer.hpp | 27 ++++ src/armnn/layers/NormalizationLayer.cpp | 44 ++++++ src/armnn/layers/NormalizationLayer.hpp | 27 ++++ src/armnn/layers/OutputLayer.cpp | 37 +++++ src/armnn/layers/OutputLayer.hpp | 31 ++++ src/armnn/layers/PermuteLayer.cpp | 49 +++++++ src/armnn/layers/PermuteLayer.hpp | 44 ++++++ src/armnn/layers/Pooling2dLayer.cpp | 106 ++++++++++++++ src/armnn/layers/Pooling2dLayer.hpp | 27 ++++ src/armnn/layers/ReshapeLayer.cpp | 46 ++++++ src/armnn/layers/ReshapeLayer.hpp | 33 +++++ src/armnn/layers/ResizeBilinearLayer.cpp | 52 +++++++ src/armnn/layers/ResizeBilinearLayer.hpp | 27 ++++ src/armnn/layers/SoftmaxLayer.cpp | 44 ++++++ src/armnn/layers/SoftmaxLayer.hpp | 27 ++++ src/armnn/layers/SplitterLayer.cpp | 81 +++++++++++ src/armnn/layers/SplitterLayer.hpp | 28 ++++ 46 files changed, 2121 insertions(+) create mode 100644 src/armnn/layers/ActivationLayer.cpp create mode 100644 src/armnn/layers/ActivationLayer.hpp create mode 100644 src/armnn/layers/AdditionLayer.cpp create mode 100644 src/armnn/layers/AdditionLayer.hpp create mode 100644 src/armnn/layers/BatchNormalizationLayer.cpp create mode 100644 src/armnn/layers/BatchNormalizationLayer.hpp create mode 100644 src/armnn/layers/ConstantLayer.cpp create mode 100644 src/armnn/layers/ConstantLayer.hpp create mode 100644 src/armnn/layers/Convolution2dLayer.cpp create mode 100644 src/armnn/layers/Convolution2dLayer.hpp create mode 100644 src/armnn/layers/DepthwiseConvolution2dLayer.cpp create mode 100644 src/armnn/layers/DepthwiseConvolution2dLayer.hpp create mode 100644 src/armnn/layers/FakeQuantizationLayer.cpp create mode 100644 src/armnn/layers/FakeQuantizationLayer.hpp create mode 100644 src/armnn/layers/FloorLayer.cpp create mode 100644 src/armnn/layers/FloorLayer.hpp create mode 100644 src/armnn/layers/FullyConnectedLayer.cpp create mode 100644 src/armnn/layers/FullyConnectedLayer.hpp create mode 100644 src/armnn/layers/InputLayer.cpp create mode 100644 src/armnn/layers/InputLayer.hpp create mode 100644 src/armnn/layers/L2NormalizationLayer.cpp create mode 100644 src/armnn/layers/L2NormalizationLayer.hpp create mode 100644 src/armnn/layers/LayerCloneBase.hpp create mode 100644 src/armnn/layers/LayerWithParameters.hpp create mode 100644 src/armnn/layers/MemCopyLayer.cpp create mode 100644 src/armnn/layers/MemCopyLayer.hpp create mode 100644 src/armnn/layers/MergerLayer.cpp create mode 100644 src/armnn/layers/MergerLayer.hpp create mode 100644 src/armnn/layers/MultiplicationLayer.cpp create mode 100644 src/armnn/layers/MultiplicationLayer.hpp create mode 100644 src/armnn/layers/NormalizationLayer.cpp create mode 100644 src/armnn/layers/NormalizationLayer.hpp create mode 100644 src/armnn/layers/OutputLayer.cpp create mode 100644 src/armnn/layers/OutputLayer.hpp create mode 100644 src/armnn/layers/PermuteLayer.cpp create mode 100644 src/armnn/layers/PermuteLayer.hpp create mode 100644 src/armnn/layers/Pooling2dLayer.cpp create mode 100644 src/armnn/layers/Pooling2dLayer.hpp create mode 100644 src/armnn/layers/ReshapeLayer.cpp create mode 100644 src/armnn/layers/ReshapeLayer.hpp create mode 100644 src/armnn/layers/ResizeBilinearLayer.cpp create mode 100644 src/armnn/layers/ResizeBilinearLayer.hpp create mode 100644 src/armnn/layers/SoftmaxLayer.cpp create mode 100644 src/armnn/layers/SoftmaxLayer.hpp create mode 100644 src/armnn/layers/SplitterLayer.cpp create mode 100644 src/armnn/layers/SplitterLayer.hpp (limited to 'src/armnn/layers') diff --git a/src/armnn/layers/ActivationLayer.cpp b/src/armnn/layers/ActivationLayer.cpp new file mode 100644 index 0000000000..2371eaa97c --- /dev/null +++ b/src/armnn/layers/ActivationLayer.cpp @@ -0,0 +1,41 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "ActivationLayer.hpp" +#include "LayerCloneBase.hpp" + +#include +#include +#include + +namespace armnn +{ + +ActivationLayer::ActivationLayer(const ActivationDescriptor& param, const char* name) + : LayerWithParameters(1, 1, LayerType::Activation, param, name) +{ +} + +std::unique_ptr ActivationLayer::CreateWorkload(const Graph& graph, const IWorkloadFactory& factory) const +{ + ActivationQueueDescriptor descriptor; + return factory.CreateActivation(descriptor, PrepInfoAndDesc(descriptor, graph)); +} + +ActivationLayer* ActivationLayer::Clone(Graph& graph) const +{ + return CloneBase(graph, m_Param, GetName()); +} + +void ActivationLayer::ValidateTensorShapesFromInputs() +{ + auto& info = GetInputSlot(0).GetConnection()->GetTensorInfo(); + + ConditionalThrowIfNotEqual( + "ActivationLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.", + GetOutputSlot(0).GetTensorInfo().GetShape(), + info.GetShape()); +} + +} // namespace armnn diff --git a/src/armnn/layers/ActivationLayer.hpp b/src/armnn/layers/ActivationLayer.hpp new file mode 100644 index 0000000000..93714c6e85 --- /dev/null +++ b/src/armnn/layers/ActivationLayer.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 "LayerWithParameters.hpp" + +namespace armnn +{ + +class ActivationLayer : public LayerWithParameters +{ +public: + virtual std::unique_ptr CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const override; + + ActivationLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + +protected: + ActivationLayer(const ActivationDescriptor ¶m, const char* name); + ~ActivationLayer() = default; +}; + +} // namespace diff --git a/src/armnn/layers/AdditionLayer.cpp b/src/armnn/layers/AdditionLayer.cpp new file mode 100644 index 0000000000..85d12eabcb --- /dev/null +++ b/src/armnn/layers/AdditionLayer.cpp @@ -0,0 +1,68 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "AdditionLayer.hpp" +#include "LayerCloneBase.hpp" + +#include +#include +#include + +namespace armnn +{ + +AdditionLayer::AdditionLayer(const char* name) + : Layer(2, 1, LayerType::Addition, name) +{ +} + +std::unique_ptr AdditionLayer::CreateWorkload(const Graph& graph, const IWorkloadFactory& factory) const +{ + AdditionQueueDescriptor descriptor; + return factory.CreateAddition(descriptor, PrepInfoAndDesc(descriptor, graph)); +} + +AdditionLayer* AdditionLayer::Clone(Graph& graph) const +{ + return CloneBase(graph, GetName()); +} + +void AdditionLayer::ValidateTensorShapesFromInputs() +{ + auto& input0 = GetInputSlot(0).GetConnection()->GetTensorInfo(); + auto& input1 = GetInputSlot(1).GetConnection()->GetTensorInfo(); + + // Get the max of the inputs + BOOST_ASSERT(input0.GetNumDimensions() == input1.GetNumDimensions()); + unsigned int numDims = input0.GetNumDimensions(); + std::vector 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]; + 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()); + ConditionalThrowIfNotEqual( + "AdditionLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.", + GetOutputSlot(0).GetTensorInfo().GetShape(), + outShape); +} + +} // namespace armnn diff --git a/src/armnn/layers/AdditionLayer.hpp b/src/armnn/layers/AdditionLayer.hpp new file mode 100644 index 0000000000..c48c027763 --- /dev/null +++ b/src/armnn/layers/AdditionLayer.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 + +namespace armnn +{ + +class AdditionLayer : public Layer +{ +public: + virtual std::unique_ptr CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const override; + + AdditionLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + +protected: + AdditionLayer(const char* name); + ~AdditionLayer() = default; +}; + +} // namespace diff --git a/src/armnn/layers/BatchNormalizationLayer.cpp b/src/armnn/layers/BatchNormalizationLayer.cpp new file mode 100644 index 0000000000..ebb8954ea7 --- /dev/null +++ b/src/armnn/layers/BatchNormalizationLayer.cpp @@ -0,0 +1,60 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "BatchNormalizationLayer.hpp" + +#include "LayerCloneBase.hpp" + +#include +#include +#include + +namespace armnn +{ + +BatchNormalizationLayer::BatchNormalizationLayer(const armnn::BatchNormalizationDescriptor& param, const char* name) + : LayerWithParameters(1, 1, LayerType::BatchNormalization, param, name) +{ +} + +std::unique_ptr BatchNormalizationLayer::CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const +{ + 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)); +} + +BatchNormalizationLayer* BatchNormalizationLayer::Clone(Graph& graph) const +{ + auto layer = CloneBase(graph, m_Param, GetName()); + + layer->m_Mean = m_Mean ? std::make_unique(*m_Mean) : nullptr; + layer->m_Variance = m_Variance ? std::make_unique(*m_Variance) : nullptr; + layer->m_Beta = m_Beta ? std::make_unique(*m_Beta) : nullptr; + layer->m_Gamma = m_Gamma ? std::make_unique(*m_Gamma) : nullptr; + + return std::move(layer); +} + +void BatchNormalizationLayer::ValidateTensorShapesFromInputs() +{ + ConditionalThrow(GetInputSlot(0).GetConnection() != nullptr, + "BatchNormalizationLayer: InputSlot must be connected to an OutputSlot"); + ConditionalThrow(GetInputSlot(0).GetConnection()->IsTensorInfoSet(), + "BatchNormalizationLayer: TensorInfo must be set on connected OutputSlot."); + + auto& info = GetInputSlot(0).GetConnection()->GetTensorInfo(); + + ConditionalThrowIfNotEqual( + "BatchNormalizationLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.", + GetOutputSlot(0).GetTensorInfo().GetShape(), + info.GetShape()); +} + +} // namespace armnn diff --git a/src/armnn/layers/BatchNormalizationLayer.hpp b/src/armnn/layers/BatchNormalizationLayer.hpp new file mode 100644 index 0000000000..d8082e5e98 --- /dev/null +++ b/src/armnn/layers/BatchNormalizationLayer.hpp @@ -0,0 +1,34 @@ +// +// 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; + +class BatchNormalizationLayer : public LayerWithParameters +{ +public: + std::unique_ptr m_Mean; + std::unique_ptr m_Variance; + std::unique_ptr m_Beta; + std::unique_ptr m_Gamma; + + virtual std::unique_ptr CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const override; + + BatchNormalizationLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + +protected: + BatchNormalizationLayer(const BatchNormalizationDescriptor& param, const char* name); + ~BatchNormalizationLayer() = default; +}; + +} // namespace diff --git a/src/armnn/layers/ConstantLayer.cpp b/src/armnn/layers/ConstantLayer.cpp new file mode 100644 index 0000000000..937d38a31d --- /dev/null +++ b/src/armnn/layers/ConstantLayer.cpp @@ -0,0 +1,46 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "ConstantLayer.hpp" +#include "LayerCloneBase.hpp" + +#include +#include +#include +#include + +namespace armnn +{ + +ConstantLayer::ConstantLayer(const std::shared_ptr& input, const char* name) + : Layer(0, 1, LayerType::Constant, name) + , m_LayerOutput(input) +{ +} + +std::unique_ptr ConstantLayer::CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const +{ + ConstantQueueDescriptor descriptor; + descriptor.m_LayerOutput = m_LayerOutput.get(); + return factory.CreateConstant(descriptor, PrepInfoAndDesc(descriptor, graph)); +} + +ConstantLayer* ConstantLayer::Clone(Graph& graph) const +{ + // Cloned layers share the same layer output object + return CloneBase(graph, m_LayerOutput, GetName()); +} + +void ConstantLayer::ValidateTensorShapesFromInputs() +{ + // get the output shape from the value of the constant layer + TensorShape const& outShape = m_LayerOutput->GetTensorInfo().GetShape(); + ConditionalThrowIfNotEqual( + "ConstantLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.", + GetOutputSlot(0).GetTensorInfo().GetShape(), + outShape); +} + +} // namespace armnn diff --git a/src/armnn/layers/ConstantLayer.hpp b/src/armnn/layers/ConstantLayer.hpp new file mode 100644 index 0000000000..e8e8d2298c --- /dev/null +++ b/src/armnn/layers/ConstantLayer.hpp @@ -0,0 +1,32 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#pragma once + +#include + +namespace armnn +{ + +class ScopedCpuTensorHandle; + +class ConstantLayer : public Layer +{ +public: + virtual std::unique_ptr CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const override; + + ConstantLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + +protected: + ConstantLayer(const std::shared_ptr& input, const char* name); + ~ConstantLayer() = default; + +private: + std::shared_ptr m_LayerOutput; +}; + +} // namespace diff --git a/src/armnn/layers/Convolution2dLayer.cpp b/src/armnn/layers/Convolution2dLayer.cpp new file mode 100644 index 0000000000..3829f129bb --- /dev/null +++ b/src/armnn/layers/Convolution2dLayer.cpp @@ -0,0 +1,83 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "Convolution2dLayer.hpp" + +#include "LayerCloneBase.hpp" + +#include +#include +#include + +namespace armnn +{ + +Convolution2dLayer::Convolution2dLayer(const Convolution2dDescriptor& param, const char* name) + : LayerWithParameters(1, 1, LayerType::Convolution2d, param, name) +{ +} + +std::unique_ptr Convolution2dLayer::CreateWorkload(const Graph& graph, const IWorkloadFactory& factory) const +{ + Convolution2dQueueDescriptor descriptor; + + descriptor.m_Weight = m_Weight.get(); + if (m_Param.m_BiasEnabled) + { + descriptor.m_Bias = m_Bias.get(); + } + return factory.CreateConvolution2d(descriptor, PrepInfoAndDesc(descriptor, graph)); +} + +Convolution2dLayer* Convolution2dLayer::Clone(Graph& graph) const +{ + auto layer = CloneBase(graph, m_Param, GetName()); + layer->m_Weight = m_Weight ? std::make_unique(*m_Weight) : nullptr; + + if (layer->m_Param.m_BiasEnabled) + { + layer->m_Bias = m_Bias ? std::make_unique(*m_Bias) : nullptr; + } + + return std::move(layer); +} + +void Convolution2dLayer::ValidateTensorShapesFromInputs() +{ + ConditionalThrow(GetInputSlot(0).GetConnection() != nullptr, + "Convolution2dLayer: InputSlot must be connected to an OutputSlot"); + ConditionalThrow(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(); + + // 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."); + + unsigned int inWidth = inputShape[3]; + unsigned int inHeight = inputShape[2]; + unsigned int inBatchSize = inputShape[0]; + + unsigned int filterWidth = filterShape[3]; + unsigned int readWidth = (inWidth + m_Param.m_PadLeft + m_Param.m_PadRight) - (filterWidth); + unsigned int outWidth = 1+(readWidth / m_Param.m_StrideX); + + unsigned int filterHeight = filterShape[2]; + unsigned int readHeight = (inHeight + m_Param.m_PadTop + m_Param.m_PadBottom) - (filterHeight); + unsigned int outHeight = 1+(readHeight / m_Param.m_StrideY); + + unsigned int outChannels = filterShape[0]; + unsigned int outBatchSize = inBatchSize; + + TensorShape shapeOut({outBatchSize, outChannels, outHeight, outWidth}); + ConditionalThrowIfNotEqual( + "Convolution2dLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.", + GetOutputSlot(0).GetTensorInfo().GetShape(), + shapeOut); +} + +} // namespace armnn diff --git a/src/armnn/layers/Convolution2dLayer.hpp b/src/armnn/layers/Convolution2dLayer.hpp new file mode 100644 index 0000000000..4d2c6505d3 --- /dev/null +++ b/src/armnn/layers/Convolution2dLayer.hpp @@ -0,0 +1,32 @@ +// +// 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; + +class Convolution2dLayer : public LayerWithParameters +{ +public: + std::unique_ptr m_Weight; + std::unique_ptr m_Bias; + + virtual std::unique_ptr CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const override; + + Convolution2dLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + +protected: + Convolution2dLayer(const Convolution2dDescriptor& param, const char* name); + ~Convolution2dLayer() = default; +}; + +} // namespace diff --git a/src/armnn/layers/DepthwiseConvolution2dLayer.cpp b/src/armnn/layers/DepthwiseConvolution2dLayer.cpp new file mode 100644 index 0000000000..0442de6c60 --- /dev/null +++ b/src/armnn/layers/DepthwiseConvolution2dLayer.cpp @@ -0,0 +1,85 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "DepthwiseConvolution2dLayer.hpp" + +#include "LayerCloneBase.hpp" + +#include +#include +#include + +namespace armnn +{ + +DepthwiseConvolution2dLayer::DepthwiseConvolution2dLayer(const DepthwiseConvolution2dDescriptor& param, + const char* name) + : LayerWithParameters(1, 1, LayerType::DepthwiseConvolution2d, param, name) +{ +} + +std::unique_ptr DepthwiseConvolution2dLayer::CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const +{ + DepthwiseConvolution2dQueueDescriptor descriptor; + + descriptor.m_Weight = m_Weight.get(); + if (m_Param.m_BiasEnabled) + { + descriptor.m_Bias = m_Bias.get(); + } + return factory.CreateDepthwiseConvolution2d(descriptor, PrepInfoAndDesc(descriptor, graph)); +} + +DepthwiseConvolution2dLayer* DepthwiseConvolution2dLayer::Clone(Graph& graph) const +{ + auto layer = CloneBase(graph, m_Param, GetName()); + layer->m_Weight = m_Weight ? std::make_unique(*m_Weight) : nullptr; + + if (layer->m_Param.m_BiasEnabled) + { + layer->m_Bias = m_Bias ? std::make_unique(*m_Bias) : nullptr; + } + + return std::move(layer); +} + +void DepthwiseConvolution2dLayer::ValidateTensorShapesFromInputs() +{ + ConditionalThrow(GetInputSlot(0).GetConnection() != nullptr, + "DepthwiseConvolution2dLayer: InputSlot must be connected to an OutputSlot"); + ConditionalThrow(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_MSG(inputShape.GetNumDimensions() == 4, "Convolutions will always have 4D input."); + + unsigned int inWidth = inputShape[3]; + unsigned int inHeight = inputShape[2]; + unsigned int inBatchSize = inputShape[0]; + + unsigned int filterWidth = filterShape[3]; + unsigned int readWidth = (inWidth + m_Param.m_PadLeft + m_Param.m_PadRight) - (filterWidth); + unsigned int outWidth = 1+(readWidth / m_Param.m_StrideX); + + unsigned int filterHeight = filterShape[2]; + unsigned int readHeight = (inHeight + m_Param.m_PadTop + m_Param.m_PadBottom) - (filterHeight); + unsigned int outHeight = 1+(readHeight / m_Param.m_StrideY); + unsigned int depthMultiplier = filterShape[0]; + + unsigned int outChannels = filterShape[1]*depthMultiplier; + unsigned int outBatchSize = inBatchSize; + + TensorShape outShape({outBatchSize, outChannels, outHeight, outWidth}); + ConditionalThrowIfNotEqual( + "DepthwiseConvolution2dLayer: " + "TensorShape set on OutputSlot[0] does not match the inferred shape.", + GetOutputSlot(0).GetTensorInfo().GetShape(), + outShape); +} + +} // namespace armnn diff --git a/src/armnn/layers/DepthwiseConvolution2dLayer.hpp b/src/armnn/layers/DepthwiseConvolution2dLayer.hpp new file mode 100644 index 0000000000..60691bf73c --- /dev/null +++ b/src/armnn/layers/DepthwiseConvolution2dLayer.hpp @@ -0,0 +1,32 @@ +// +// 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; + +class DepthwiseConvolution2dLayer : public LayerWithParameters +{ +public: + std::unique_ptr m_Weight; + std::unique_ptr m_Bias; + + virtual std::unique_ptr CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const override; + + DepthwiseConvolution2dLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + +protected: + DepthwiseConvolution2dLayer(const DepthwiseConvolution2dDescriptor& param, const char* name); + ~DepthwiseConvolution2dLayer() = default; +}; + +} // namespace diff --git a/src/armnn/layers/FakeQuantizationLayer.cpp b/src/armnn/layers/FakeQuantizationLayer.cpp new file mode 100644 index 0000000000..24b53b2e37 --- /dev/null +++ b/src/armnn/layers/FakeQuantizationLayer.cpp @@ -0,0 +1,51 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "FakeQuantizationLayer.hpp" + +#include "LayerCloneBase.hpp" + +#include +#include +#include + +namespace armnn +{ + +FakeQuantizationLayer::FakeQuantizationLayer(const FakeQuantizationDescriptor& param, const char* name) +: LayerWithParameters(1, 1, LayerType::FakeQuantization, param, name) +{ +} + +std::unique_ptr FakeQuantizationLayer::CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const +{ + FakeQuantizationQueueDescriptor descriptor; + return factory.CreateFakeQuantization(descriptor, PrepInfoAndDesc(descriptor, graph) ); +} + +FakeQuantizationLayer* FakeQuantizationLayer::Clone(Graph& graph) const +{ + return CloneBase(graph, m_Param, GetName()); +} + +void FakeQuantizationLayer::ValidateTensorShapesFromInputs() +{ + ConditionalThrow(GetInputSlot(0).GetConnection() != nullptr, + "FakeQuantizationLayer: InputSlot must be connected to an OutputSlot"); + ConditionalThrow(GetInputSlot(0).GetConnection()->IsTensorInfoSet(), + "FakeQuantizationLayer: TensorInfo must be set on connected OutputSlot."); + + + IOutputSlot* input = GetInputSlot(0).GetConnection(); + + // input and output shapes are the same + TensorShape const& outShape = input->GetTensorInfo().GetShape(); + ConditionalThrowIfNotEqual( + "FakeQuantizationLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.", + GetOutputSlot(0).GetTensorInfo().GetShape(), + outShape); +} + +} // namespace armnn diff --git a/src/armnn/layers/FakeQuantizationLayer.hpp b/src/armnn/layers/FakeQuantizationLayer.hpp new file mode 100644 index 0000000000..d64ea58312 --- /dev/null +++ b/src/armnn/layers/FakeQuantizationLayer.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 "LayerWithParameters.hpp" + +namespace armnn +{ + +class FakeQuantizationLayer : public LayerWithParameters +{ +public: + virtual std::unique_ptr CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const override; + + FakeQuantizationLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + +protected: + FakeQuantizationLayer(const FakeQuantizationDescriptor& descriptor, const char* name); + ~FakeQuantizationLayer() = default; +}; + +} // namespace diff --git a/src/armnn/layers/FloorLayer.cpp b/src/armnn/layers/FloorLayer.cpp new file mode 100644 index 0000000000..a9ddcca60c --- /dev/null +++ b/src/armnn/layers/FloorLayer.cpp @@ -0,0 +1,49 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "FloorLayer.hpp" + +#include "LayerCloneBase.hpp" + +#include +#include +#include + +namespace armnn +{ + +FloorLayer::FloorLayer(const char* name) + : Layer(1, 1, LayerType::Floor, name) +{ +} + +std::unique_ptr FloorLayer::CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const +{ + FloorQueueDescriptor descriptor; + return factory.CreateFloor(descriptor, PrepInfoAndDesc(descriptor, graph)); +} + +FloorLayer* FloorLayer::Clone(Graph& graph) const +{ + return CloneBase(graph, GetName()); +} + +void FloorLayer::ValidateTensorShapesFromInputs() +{ + ConditionalThrow(GetInputSlot(0).GetConnection() != nullptr, + "FloorLayer: InputSlot must be connected to an OutputSlot"); + ConditionalThrow(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(); + ConditionalThrowIfNotEqual( + "FloorLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.", + GetOutputSlot(0).GetTensorInfo().GetShape(), + outShape); +} + +} // namespace armnn diff --git a/src/armnn/layers/FloorLayer.hpp b/src/armnn/layers/FloorLayer.hpp new file mode 100644 index 0000000000..aa7f892915 --- /dev/null +++ b/src/armnn/layers/FloorLayer.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 + +namespace armnn +{ + +class FloorLayer : public Layer +{ +public: + virtual std::unique_ptr CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const override; + + FloorLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + +protected: + FloorLayer(const char* name); + ~FloorLayer() = default; +}; + +} // namespace diff --git a/src/armnn/layers/FullyConnectedLayer.cpp b/src/armnn/layers/FullyConnectedLayer.cpp new file mode 100644 index 0000000000..1597e8c2c3 --- /dev/null +++ b/src/armnn/layers/FullyConnectedLayer.cpp @@ -0,0 +1,69 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "FullyConnectedLayer.hpp" + +#include "LayerCloneBase.hpp" + +#include +#include +#include +#include + +namespace armnn +{ + +FullyConnectedLayer::FullyConnectedLayer(const FullyConnectedDescriptor& param, const char* name) + : LayerWithParameters(1, 1, LayerType::FullyConnected, param, name) +{ +} + +std::unique_ptr FullyConnectedLayer::CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const +{ + FullyConnectedQueueDescriptor descriptor; + + descriptor.m_Weight = m_Weight.get(); + if (m_Param.m_BiasEnabled) + { + descriptor.m_Bias = m_Bias.get(); + } + return factory.CreateFullyConnected(descriptor, PrepInfoAndDesc(descriptor, graph)); +} + +FullyConnectedLayer* FullyConnectedLayer::Clone(Graph& graph) const +{ + auto layer = CloneBase(graph, m_Param, GetName()); + + layer->m_Weight = m_Weight ? std::make_unique(*m_Weight) : nullptr; + if (layer->m_Param.m_BiasEnabled) + { + layer->m_Bias = m_Bias ? std::make_unique(*m_Bias) : nullptr; + } + + return std::move(layer); +} + +void FullyConnectedLayer::ValidateTensorShapesFromInputs() +{ + ConditionalThrow(GetInputSlot(0).GetConnection() != nullptr, + "FullyConnectedLayer: InputSlot must be connected to an OutputSlot"); + ConditionalThrow(GetInputSlot(0).GetConnection()->IsTensorInfoSet(), + "FullyConnectedLayer: TensorInfo must be set on connected OutputSlot."); + + + TensorShape const& weightShape = 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]}); + + ConditionalThrowIfNotEqual( + "FullyConnectedLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.", + GetOutputSlot(0).GetTensorInfo().GetShape(), + outShape); +} + +} // namespace armnn diff --git a/src/armnn/layers/FullyConnectedLayer.hpp b/src/armnn/layers/FullyConnectedLayer.hpp new file mode 100644 index 0000000000..1d6cb7cf8d --- /dev/null +++ b/src/armnn/layers/FullyConnectedLayer.hpp @@ -0,0 +1,32 @@ +// +// 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; + +class FullyConnectedLayer : public LayerWithParameters +{ +public: + std::unique_ptr m_Weight; + std::unique_ptr m_Bias; + + virtual std::unique_ptr CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const override; + + FullyConnectedLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + +protected: + FullyConnectedLayer(const FullyConnectedDescriptor& param, const char* name); + ~FullyConnectedLayer() = default; +}; + +} // namespace diff --git a/src/armnn/layers/InputLayer.cpp b/src/armnn/layers/InputLayer.cpp new file mode 100644 index 0000000000..96f1b773f4 --- /dev/null +++ b/src/armnn/layers/InputLayer.cpp @@ -0,0 +1,37 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "InputLayer.hpp" + +#include "LayerCloneBase.hpp" + +#include +#include + +namespace armnn +{ + +InputLayer::InputLayer(LayerBindingId id, const char* name) + : BindableLayer(0, 1, LayerType::Input, name, id) +{ +} + +std::unique_ptr InputLayer::CreateWorkload(const Graph& graph, const IWorkloadFactory& factory) const +{ + return nullptr; +} + +InputLayer* InputLayer::Clone(Graph& graph) const +{ + return CloneBase(graph, GetBindingId(), GetName()); +} + +void InputLayer::ValidateTensorShapesFromInputs() +{ + //The input layer should already have it's inputs set during graph building phase in the driver/parser. + ConditionalThrow(GetOutputSlot(0).IsTensorInfoSet(), + "InputLayer should already have the TensorInfo set."); +} + +} // namespace diff --git a/src/armnn/layers/InputLayer.hpp b/src/armnn/layers/InputLayer.hpp new file mode 100644 index 0000000000..24202255cb --- /dev/null +++ b/src/armnn/layers/InputLayer.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 + +namespace armnn +{ + +class InputLayer : public BindableLayer +{ +public: + virtual std::unique_ptr CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const override; + + InputLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + +protected: + InputLayer(LayerBindingId id, const char* name); + ~InputLayer() = default; +}; + +} // namespace diff --git a/src/armnn/layers/L2NormalizationLayer.cpp b/src/armnn/layers/L2NormalizationLayer.cpp new file mode 100644 index 0000000000..07020bfdca --- /dev/null +++ b/src/armnn/layers/L2NormalizationLayer.cpp @@ -0,0 +1,50 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "L2NormalizationLayer.hpp" + +#include "LayerCloneBase.hpp" + +#include +#include +#include + +namespace armnn +{ + +L2NormalizationLayer::L2NormalizationLayer(const char* name) + : Layer(1, 1, LayerType::L2Normalization, name) +{ +} + +std::unique_ptr L2NormalizationLayer::CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const +{ + L2NormalizationQueueDescriptor descriptor; + return factory.CreateL2Normalization(descriptor, PrepInfoAndDesc(descriptor, graph)); +} + +L2NormalizationLayer* L2NormalizationLayer::Clone(Graph& graph) const +{ + return CloneBase(graph, GetName()); +} + +void L2NormalizationLayer::ValidateTensorShapesFromInputs() +{ + ConditionalThrow(GetInputSlot(0).GetConnection() != nullptr, + "L2NormalizationLayer: InputSlot must be connected to an OutputSlot"); + ConditionalThrow(GetInputSlot(0).GetConnection()->IsTensorInfoSet(), + "L2NormalizationLayer: TensorInfo must be set on connected OutputSlot."); + + IOutputSlot* input = GetInputSlot(0).GetConnection(); + + // input and output shapes are the same + TensorShape const& outShape = input->GetTensorInfo().GetShape(); + ConditionalThrowIfNotEqual( + "L2NormalizationLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.", + GetOutputSlot(0).GetTensorInfo().GetShape(), + outShape); +} + +} // namespace armnn diff --git a/src/armnn/layers/L2NormalizationLayer.hpp b/src/armnn/layers/L2NormalizationLayer.hpp new file mode 100644 index 0000000000..3bea177a78 --- /dev/null +++ b/src/armnn/layers/L2NormalizationLayer.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 + +namespace armnn +{ + +class L2NormalizationLayer : public Layer +{ +public: + virtual std::unique_ptr CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const override; + + L2NormalizationLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + +protected: + L2NormalizationLayer(const char* name); + ~L2NormalizationLayer() = default; +}; + +} // namespace diff --git a/src/armnn/layers/LayerCloneBase.hpp b/src/armnn/layers/LayerCloneBase.hpp new file mode 100644 index 0000000000..fbd8629c94 --- /dev/null +++ b/src/armnn/layers/LayerCloneBase.hpp @@ -0,0 +1,24 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#pragma once + +#include +#include + +namespace armnn +{ + +template +LayerType* Layer::CloneBase(Graph& graph, Params&& ... params) const +{ + LayerType* const layer = graph.AddLayer(std::forward(params)...); + + layer->SetComputeDevice(m_ComputeDevice); + layer->SetGuid(GetGuid()); + + return layer; +} + +} // namespace diff --git a/src/armnn/layers/LayerWithParameters.hpp b/src/armnn/layers/LayerWithParameters.hpp new file mode 100644 index 0000000000..e3eb40a273 --- /dev/null +++ b/src/armnn/layers/LayerWithParameters.hpp @@ -0,0 +1,52 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#pragma once + +#include + +namespace armnn +{ + +template +class LayerWithParameters : public Layer +{ +public: + using DescriptorType = Parameters; + + const Parameters& GetParameters() const { return m_Param; } + + /// Helper to serialize the layer parameters to string + /// (currently used in DotSerializer and company) + void SerializeLayerParameters(ParameterStringifyFunction & fn) const + { + StringifyLayerParameters::Serialize(fn, m_Param); + } + +protected: + LayerWithParameters(unsigned int numInputSlots, + unsigned int numOutputSlots, + LayerType type, + const Parameters& param, + const char* name) + : Layer(numInputSlots, numOutputSlots, type, name) + , m_Param(param) + { + } + + ~LayerWithParameters() = default; + + /// Helper function to reduce duplication in *Layer::CreateWorkload + template + WorkloadInfo PrepInfoAndDesc(QueueDescriptor& descriptor, const Graph& graph) const + { + descriptor.m_Parameters = m_Param; + return Layer::PrepInfoAndDesc(descriptor, graph); + } + + /// The parameters for the layer (not including tensor-valued weights etc.) + Parameters m_Param; +}; + +} // namespace diff --git a/src/armnn/layers/MemCopyLayer.cpp b/src/armnn/layers/MemCopyLayer.cpp new file mode 100644 index 0000000000..973a756b21 --- /dev/null +++ b/src/armnn/layers/MemCopyLayer.cpp @@ -0,0 +1,48 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "MemCopyLayer.hpp" + +#include "LayerCloneBase.hpp" + +#include +#include +#include + +namespace armnn +{ + +MemCopyLayer::MemCopyLayer(const char* name) + : Layer(1, 1, LayerType::MemCopy, name) +{ +} + +MemCopyLayer* MemCopyLayer::Clone(Graph& graph) const +{ + return CloneBase(graph, GetName()); +} + +std::unique_ptr MemCopyLayer::CreateWorkload(const Graph& graph, const IWorkloadFactory& factory) const +{ + MemCopyQueueDescriptor descriptor; + return factory.CreateMemCopy(descriptor, PrepInfoAndDesc(descriptor, graph)); +} + +void MemCopyLayer::ValidateTensorShapesFromInputs() +{ + ConditionalThrow(GetInputSlot(0).GetConnection() != nullptr, + "MemCopyLayer: InputSlot must be connected to an OutputSlot"); + ConditionalThrow(GetInputSlot(0).GetConnection()->IsTensorInfoSet(), + "MemCopyLayer: TensorInfo must be set on connected OutputSlot."); + + + IOutputSlot* input = GetInputSlot(0).GetConnection(); + + ConditionalThrowIfNotEqual( + "MemCopyLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.", + GetOutputSlot(0).GetTensorInfo().GetShape(), + input->GetTensorInfo().GetShape()); +} + +} // namespace armnn diff --git a/src/armnn/layers/MemCopyLayer.hpp b/src/armnn/layers/MemCopyLayer.hpp new file mode 100644 index 0000000000..cc227b1c74 --- /dev/null +++ b/src/armnn/layers/MemCopyLayer.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 + +namespace armnn +{ + +class MemCopyLayer : public Layer +{ +public: + virtual std::unique_ptr + CreateWorkload(const Graph& graph, const IWorkloadFactory& factory) const override; + + MemCopyLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + +protected: + MemCopyLayer(const char* name); + ~MemCopyLayer() = default; +}; + +} // namespace diff --git a/src/armnn/layers/MergerLayer.cpp b/src/armnn/layers/MergerLayer.cpp new file mode 100644 index 0000000000..065fc86a1b --- /dev/null +++ b/src/armnn/layers/MergerLayer.cpp @@ -0,0 +1,178 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "MergerLayer.hpp" +#include "LayerCloneBase.hpp" + +#include +#include +#include + +#include + +namespace armnn +{ + +MergerLayer::MergerLayer(const OriginsDescriptor& param, const char* name) + : LayerWithParameters(param.GetNumViews(), 1, LayerType::Merger, param, name) +{ +} + +std::unique_ptr MergerLayer::CreateWorkload(const Graph& graph, const IWorkloadFactory& factory) const +{ + MergerQueueDescriptor descriptor; + + // copy the view origins to the descriptor + descriptor.m_ViewOrigins.reserve(m_Param.GetNumViews()); + for (unsigned int i = 0; i < m_Param.GetNumViews(); ++i) + { + descriptor.m_ViewOrigins.emplace_back( + std::vector(m_Param.GetViewOrigin(i), m_Param.GetViewOrigin(i) + m_Param.GetNumDimensions())); + } + + return factory.CreateMerger(descriptor, PrepInfoAndDesc(descriptor, graph)); +} + +void MergerLayer::CreateTensorHandles(Graph& graph, const IWorkloadFactory& factory) +{ + //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 + m_OutputHandlers[0].CreateTensorHandles(factory); + if (factory.SupportsSubTensors()) + { + std::queue m_MergerLayers; + + m_MergerLayers.push(this); + while (!m_MergerLayers.empty()) + { + MergerLayer* currentLayer = m_MergerLayers.front(); + ITensorHandle* parentTensor = currentLayer->GetOutputHandler(0).GetData(); + + m_MergerLayers.pop(); + + const unsigned int numInputSlots = currentLayer->GetNumInputSlots(); + for (unsigned int i = 0; i < numInputSlots; ++i) + { + OutputSlot* slot = currentLayer->GetInputSlot(i).GetConnectedOutputSlot(); + OutputHandler& outputHandler = slot->GetOutputHandler(); + outputHandler.SetData(factory.CreateSubTensorHandle(*parentTensor, + outputHandler.GetTensorInfo().GetShape(), + currentLayer->m_Param.GetViewOrigin(i))); + + Layer& inputLayer = slot->GetOwningLayer(); + if (inputLayer.GetType() == LayerType::Merger) + { + m_MergerLayers.push(boost::polymorphic_downcast(&inputLayer)); + } + } + } + } +} + +MergerLayer* MergerLayer::Clone(Graph& graph) const +{ + return CloneBase(graph, m_Param, GetName()); +} + +void MergerLayer::ValidateTensorShapesFromInputs() +{ + // Validate Merger layer + ConditionalThrowIfNotEqual( + "MergerLayer: Num Inputs must match num views.", + m_Param.GetNumViews(), + GetNumInputSlots()); + + unsigned int numDims = m_Param.GetNumDimensions(); + for (unsigned int i=0; iGetTensorInfo(); + + boost::ignore_unused(inputInfo); + ConditionalThrowIfNotEqual( + "MergerLayer: Num Dimensions must match all inputs.", + numDims, + inputInfo.GetNumDimensions()); + } + + // Find the bounding box (extents) of all the views + std::vector extentMin(numDims); + std::vector extentMax(numDims); + for (unsigned int i = 0; i < GetNumInputSlots(); i++) + { + const uint32_t* origin = m_Param.GetViewOrigin(i); + const armnn::TensorShape& shape = GetInputSlot(i).GetConnection()->GetTensorInfo().GetShape(); + for (unsigned int d = 0; d < numDims; d++) + { + extentMin[d] = std::min(extentMin[d], origin[d]); + extentMax[d] = std::max(extentMax[d], origin[d] + shape[d]); + } + } + + // Check 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++) + { + const uint32_t* aOrigin = m_Param.GetViewOrigin(a); + const armnn::TensorShape& aShape = GetInputSlot(a).GetConnection()->GetTensorInfo().GetShape(); + 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(); + + bool allAxesOverlap = true; + for (unsigned int d = 0; d < numDims && allAxesOverlap; d++) + { + unsigned int a1 = aOrigin[d]; + unsigned int a2 = aOrigin[d] + aShape[d]; + + unsigned int b1 = bOrigin[d]; + unsigned int b2 = bOrigin[d] + bShape[d]; + + if (a2 <= b1 || b2 <= a1) + { + allAxesOverlap = false; + } + } + if (allAxesOverlap) + { + throw LayerValidationException("MergerLayer: Some views overlap."); + } + } + } + + // Check 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++) + { + totalViewsVolume += GetInputSlot(i).GetConnection()->GetTensorInfo().GetNumElements(); + } + unsigned int outputVolume = 1; + for (unsigned int d = 0; d < numDims; d++) + { + outputVolume *= (extentMax[d] - extentMin[d]); + } + + ConditionalThrowIfNotEqual( + "MergerLayer: there are some gaps between views", + totalViewsVolume, + outputVolume); + + TensorShape outShape(numDims, extentMax.data()); + ConditionalThrowIfNotEqual( + "MergerLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.", + GetOutputSlot(0).GetTensorInfo().GetShape(), + outShape); +} + +} // namespace armnn armnn diff --git a/src/armnn/layers/MergerLayer.hpp b/src/armnn/layers/MergerLayer.hpp new file mode 100644 index 0000000000..ad94cb5f3a --- /dev/null +++ b/src/armnn/layers/MergerLayer.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 "LayerWithParameters.hpp" + +namespace armnn +{ + +class MergerLayer : public LayerWithParameters +{ +public: + virtual std::unique_ptr CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const override; + virtual void CreateTensorHandles(Graph& graph, const IWorkloadFactory& factory) override; + + MergerLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + +protected: + MergerLayer(const OriginsDescriptor& param, const char* name); + ~MergerLayer() = default; +}; + +} // namespace diff --git a/src/armnn/layers/MultiplicationLayer.cpp b/src/armnn/layers/MultiplicationLayer.cpp new file mode 100644 index 0000000000..af40a23007 --- /dev/null +++ b/src/armnn/layers/MultiplicationLayer.cpp @@ -0,0 +1,71 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "MultiplicationLayer.hpp" + +#include "LayerCloneBase.hpp" + +#include +#include +#include + +namespace armnn +{ + +MultiplicationLayer::MultiplicationLayer(const char* name) + : Layer(2, 1, LayerType::Multiplication, name) +{ +} + +std::unique_ptr MultiplicationLayer::CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const +{ + MultiplicationQueueDescriptor descriptor; + + return factory.CreateMultiplication(descriptor, PrepInfoAndDesc(descriptor, graph)); +} + +MultiplicationLayer* MultiplicationLayer::Clone(Graph& graph) const +{ + return CloneBase(graph, GetName()); +} + +void MultiplicationLayer::ValidateTensorShapesFromInputs() +{ + auto& input0 = GetInputSlot(0).GetConnection()->GetTensorInfo(); + auto& input1 = GetInputSlot(1).GetConnection()->GetTensorInfo(); + + // Get the max of the inputs + BOOST_ASSERT(input0.GetNumDimensions() == input1.GetNumDimensions()); + unsigned int numDims = input0.GetNumDimensions(); + std::vector 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]; + 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()); + ConditionalThrowIfNotEqual( + "MultiplicationLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.", + GetOutputSlot(0).GetTensorInfo().GetShape(), + outShape); +} + +} // namespace armnn diff --git a/src/armnn/layers/MultiplicationLayer.hpp b/src/armnn/layers/MultiplicationLayer.hpp new file mode 100644 index 0000000000..48db9f4d01 --- /dev/null +++ b/src/armnn/layers/MultiplicationLayer.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 + +namespace armnn +{ + +class MultiplicationLayer : public Layer +{ +public: + virtual std::unique_ptr CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const override; + + MultiplicationLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + +protected: + MultiplicationLayer(const char* name); + ~MultiplicationLayer() = default; +}; + +} // namespace diff --git a/src/armnn/layers/NormalizationLayer.cpp b/src/armnn/layers/NormalizationLayer.cpp new file mode 100644 index 0000000000..cacd348444 --- /dev/null +++ b/src/armnn/layers/NormalizationLayer.cpp @@ -0,0 +1,44 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "NormalizationLayer.hpp" + +#include "LayerCloneBase.hpp" + +#include +#include +#include + +namespace armnn +{ + +NormalizationLayer::NormalizationLayer(const NormalizationDescriptor& param, const char* name) + : LayerWithParameters(1, 1, LayerType::Normalization, param, name) +{ +} + +std::unique_ptr NormalizationLayer::CreateWorkload(const Graph& graph, const IWorkloadFactory& factory) const +{ + NormalizationQueueDescriptor descriptor; + return factory.CreateNormalization(descriptor, PrepInfoAndDesc(descriptor, graph)); +} + +NormalizationLayer* NormalizationLayer::Clone(Graph& graph) const +{ + return CloneBase(graph, m_Param, GetName()); +} + +void NormalizationLayer::ValidateTensorShapesFromInputs() +{ + ConditionalThrow(GetInputSlot(0).GetConnection() != nullptr, + "NormalizationLayer: Input slot must be connected."); + + const TensorShape& outShape = GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(); + ConditionalThrowIfNotEqual( + "NormalizationLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.", + GetOutputSlot(0).GetTensorInfo().GetShape(), + outShape); +} + +} // namespace armnn diff --git a/src/armnn/layers/NormalizationLayer.hpp b/src/armnn/layers/NormalizationLayer.hpp new file mode 100644 index 0000000000..c87fbe6451 --- /dev/null +++ b/src/armnn/layers/NormalizationLayer.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 "LayerWithParameters.hpp" + +namespace armnn +{ + +class NormalizationLayer : public LayerWithParameters +{ +public: + virtual std::unique_ptr CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const override; + + NormalizationLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + +protected: + NormalizationLayer(const NormalizationDescriptor& param, const char* name); + ~NormalizationLayer() = default; +}; + +} // namespace diff --git a/src/armnn/layers/OutputLayer.cpp b/src/armnn/layers/OutputLayer.cpp new file mode 100644 index 0000000000..cadcf2da2f --- /dev/null +++ b/src/armnn/layers/OutputLayer.cpp @@ -0,0 +1,37 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "OutputLayer.hpp" + +#include "LayerCloneBase.hpp" + +#include +#include + +namespace armnn +{ + +OutputLayer::OutputLayer(LayerBindingId id, const char* name) + : BindableLayer(1, 0, LayerType::Output, name, id) +{ +} + +std::unique_ptr OutputLayer::CreateWorkload(const Graph& graph, const IWorkloadFactory& factory) const +{ + return nullptr; +} + +OutputLayer* OutputLayer::Clone(Graph& graph) const +{ + return CloneBase(graph, GetBindingId(), GetName()); +} + +void OutputLayer::ValidateTensorShapesFromInputs() +{ + // Just validate the input is connected + ConditionalThrow(GetInputSlot(0).GetConnection() != nullptr, + "OutputLayer: Input slot must be connected."); +} + +} // namespace armnn diff --git a/src/armnn/layers/OutputLayer.hpp b/src/armnn/layers/OutputLayer.hpp new file mode 100644 index 0000000000..a2e11e5d26 --- /dev/null +++ b/src/armnn/layers/OutputLayer.hpp @@ -0,0 +1,31 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#pragma once + +#include + +namespace armnn +{ + +class OutputLayer : public BindableLayer +{ +public: + virtual std::unique_ptr CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const override; + virtual void CreateTensorHandles(Graph& graph, const IWorkloadFactory& factory) override + { + boost::ignore_unused(graph, factory); + } + + OutputLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + +protected: + OutputLayer(LayerBindingId id, const char* name); + ~OutputLayer() = default; +}; + +} // namespace diff --git a/src/armnn/layers/PermuteLayer.cpp b/src/armnn/layers/PermuteLayer.cpp new file mode 100644 index 0000000000..35692756a1 --- /dev/null +++ b/src/armnn/layers/PermuteLayer.cpp @@ -0,0 +1,49 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "PermuteLayer.hpp" + +#include "LayerCloneBase.hpp" + +#include +#include +#include + +#include + +namespace armnn +{ + +PermuteLayer::PermuteLayer(const PermuteDescriptor& param, const char* name) + : LayerWithParameters(1, 1, LayerType::Permute, param, name) +{ +} + +std::unique_ptr PermuteLayer::CreateWorkload(const Graph& graph, const IWorkloadFactory& factory) const +{ + PermuteQueueDescriptor descriptor; + return factory.CreatePermute(descriptor, PrepInfoAndDesc(descriptor, graph)); +} + +PermuteLayer* PermuteLayer::Clone(Graph& graph) const +{ + return CloneBase(graph, m_Param, GetName()); +} + +void PermuteLayer::ValidateTensorShapesFromInputs() +{ + ConditionalThrow(GetInputSlot(0).GetConnection() != nullptr, + "PermuteLayer: InputSlot must be connected to an OutputSlot"); + ConditionalThrow(GetInputSlot(0).GetConnection()->IsTensorInfoSet(), + "PermuteLayer: TensorInfo must be set on connected InputSlot."); + + const TensorInfo& infoIn = GetInputSlot(0).GetConnection()->GetTensorInfo(); + TensorShape shapeOut = armnnUtils::Permuted(infoIn.GetShape(), m_Param.m_DimMappings); + ConditionalThrowIfNotEqual( + "PermuteLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.", + GetOutputSlot(0).GetTensorInfo().GetShape(), + shapeOut); +} + +} // namespace armnn diff --git a/src/armnn/layers/PermuteLayer.hpp b/src/armnn/layers/PermuteLayer.hpp new file mode 100644 index 0000000000..c060a16390 --- /dev/null +++ b/src/armnn/layers/PermuteLayer.hpp @@ -0,0 +1,44 @@ +// +// 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 PermuteLayer : public LayerWithParameters +{ +public: + virtual std::unique_ptr CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const override; + + PermuteLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + + const PermutationVector& GetPermutation() const + { + return m_Param.m_DimMappings; + } + + bool IsInverse(const Layer& other) const + { + return (other.GetType() == LayerType::Permute) && + GetPermutation().IsInverse(boost::polymorphic_downcast(&other)->GetPermutation()); + } + + bool IsEqual(const Layer& other) const + { + return (other.GetType() == LayerType::Permute) && + GetPermutation().IsEqual(boost::polymorphic_downcast(&other)->GetPermutation()); + } + +protected: + PermuteLayer(const PermuteDescriptor& param, const char* name); + ~PermuteLayer() = default; +}; + +} // namespace diff --git a/src/armnn/layers/Pooling2dLayer.cpp b/src/armnn/layers/Pooling2dLayer.cpp new file mode 100644 index 0000000000..ede37d7604 --- /dev/null +++ b/src/armnn/layers/Pooling2dLayer.cpp @@ -0,0 +1,106 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "Pooling2dLayer.hpp" + +#include "LayerCloneBase.hpp" + +#include +#include +#include + +namespace armnn +{ + +Pooling2dLayer::Pooling2dLayer(const Pooling2dDescriptor& param, const char* name) + : LayerWithParameters(1, 1, LayerType::Pooling2d, param, name) +{ +} + +std::unique_ptr Pooling2dLayer::CreateWorkload(const Graph& graph, const IWorkloadFactory& factory) const +{ + Pooling2dQueueDescriptor descriptor; + return factory.CreatePooling2d(descriptor, PrepInfoAndDesc(descriptor, graph)); +} + +Pooling2dLayer* Pooling2dLayer::Clone(Graph& graph) const +{ + return CloneBase(graph, m_Param, GetName()); +} + +void Pooling2dLayer::ValidateTensorShapesFromInputs() +{ + ConditionalThrow(GetInputSlot(0).GetConnection() != nullptr, + "Pooling2dLayer: InputSlot must be connected to an OutputSlot"); + ConditionalThrow(GetInputSlot(0).GetConnection()->IsTensorInfoSet(), + "Pooling2dLayer: TensorInfo must be set on connected InputSlot."); + + IOutputSlot* input = GetInputSlot(0).GetConnection(); + const TensorShape& inputShape = input->GetTensorInfo().GetShape(); + + // 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."); + + + unsigned int inWidth = inputShape[3]; + unsigned int inHeight = inputShape[2]; + unsigned int inChannels = inputShape[1]; + unsigned int inBatchSize = inputShape[0]; + + bool isGlobalPooling = (m_Param.m_StrideX==0 && m_Param.m_StrideY==0); + unsigned int outWidth = 1; + unsigned int outHeight = 1; + if (!isGlobalPooling) + { + BOOST_ASSERT_MSG(m_Param.m_StrideX!=0 && m_Param.m_StrideY!=0, + "Stride can only be zero when performing global pooling"); + + auto CalcSize = [](auto inSize, auto lowPad, auto highPad, auto poolSize, auto stride, auto padMethod, + auto outputShapeRounding) + { + unsigned int readSize = inSize + lowPad + highPad - poolSize; + float div = static_cast(readSize) / static_cast(stride); + + unsigned int size = 0; + switch (outputShapeRounding) + { + case OutputShapeRounding::Ceiling: + size = static_cast(ceil(div)) + 1; + break; + case OutputShapeRounding ::Floor: + size = static_cast(floor(div)) + 1; + break; + default: + 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... + if ((size - 1)*stride >= inSize + lowPad) + { + --size; + } + + return size; + }; + + outWidth = CalcSize(inWidth, m_Param.m_PadLeft, m_Param.m_PadRight, m_Param.m_PoolWidth, m_Param.m_StrideX, + 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}); + + ConditionalThrowIfNotEqual( + "Pooling2dLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.", + GetOutputSlot(0).GetTensorInfo().GetShape(), + shapeOut); +} + +} // namespace armnn diff --git a/src/armnn/layers/Pooling2dLayer.hpp b/src/armnn/layers/Pooling2dLayer.hpp new file mode 100644 index 0000000000..af39dbb5ec --- /dev/null +++ b/src/armnn/layers/Pooling2dLayer.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 "LayerWithParameters.hpp" + +namespace armnn +{ + +class SoftmaxLayer : public LayerWithParameters +{ +public: + virtual std::unique_ptr CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const override; + + SoftmaxLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + +protected: + SoftmaxLayer(const SoftmaxDescriptor& param, const char* name); + ~SoftmaxLayer() = default; +}; + +} // namespace diff --git a/src/armnn/layers/ReshapeLayer.cpp b/src/armnn/layers/ReshapeLayer.cpp new file mode 100644 index 0000000000..df5d9d5bb0 --- /dev/null +++ b/src/armnn/layers/ReshapeLayer.cpp @@ -0,0 +1,46 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "ReshapeLayer.hpp" + +#include "LayerCloneBase.hpp" + +#include +#include +#include + +namespace armnn +{ + +ReshapeLayer::ReshapeLayer(const ReshapeDescriptor& param, const char* name) + : LayerWithParameters(1, 1, LayerType::Reshape, param, name) +{ +} + +std::unique_ptr ReshapeLayer::CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const +{ + ReshapeQueueDescriptor descriptor; + return factory.CreateReshape(descriptor, PrepInfoAndDesc(descriptor, graph)); +} + +ReshapeLayer* ReshapeLayer::Clone(Graph& graph) const +{ + return CloneBase(graph, m_Param, GetName()); +} + +void ReshapeLayer::ValidateTensorShapesFromInputs() +{ + ConditionalThrow(GetInputSlot(0).GetConnection() != nullptr, + "ReshapeLayer: InputSlot must be connected to an OutputSlot"); + ConditionalThrow(GetInputSlot(0).GetConnection()->IsTensorInfoSet(), + "ReshapeLayer: TensorInfo must be set on connected OutputSlot."); + + ConditionalThrowIfNotEqual( + "ReshapeLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.", + GetOutputSlot(0).GetTensorInfo().GetShape(), + m_Param.m_TargetShape); +} + +} // namespace armnn diff --git a/src/armnn/layers/ReshapeLayer.hpp b/src/armnn/layers/ReshapeLayer.hpp new file mode 100644 index 0000000000..8a3cf3a698 --- /dev/null +++ b/src/armnn/layers/ReshapeLayer.hpp @@ -0,0 +1,33 @@ +// +// 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 ReshapeLayer : public LayerWithParameters +{ +public: + virtual std::unique_ptr CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const override; + + ReshapeLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + + bool IsEqual(const Layer& other) const + { + return (other.GetType() == LayerType::Reshape) && + m_Param.m_TargetShape == boost::polymorphic_downcast(&other)->m_Param.m_TargetShape; + } + +protected: + ReshapeLayer(const ReshapeDescriptor& desc, const char* name); + ~ReshapeLayer() = default; +}; + +} // namespace diff --git a/src/armnn/layers/ResizeBilinearLayer.cpp b/src/armnn/layers/ResizeBilinearLayer.cpp new file mode 100644 index 0000000000..204d5afae8 --- /dev/null +++ b/src/armnn/layers/ResizeBilinearLayer.cpp @@ -0,0 +1,52 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "ResizeBilinearLayer.hpp" + +#include "LayerCloneBase.hpp" + +#include +#include +#include + +namespace armnn +{ + +ResizeBilinearLayer::ResizeBilinearLayer(const ResizeBilinearDescriptor& param, const char* name) + : LayerWithParameters(1, 1, LayerType::ResizeBilinear, param, name) +{ +} + +std::unique_ptr ResizeBilinearLayer::CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const +{ + ResizeBilinearQueueDescriptor descriptor; + return factory.CreateResizeBilinear(descriptor, PrepInfoAndDesc(descriptor, graph)); +} + +ResizeBilinearLayer* ResizeBilinearLayer::Clone(Graph& graph) const +{ + return CloneBase(graph, m_Param, GetName()); +} + +void ResizeBilinearLayer::ValidateTensorShapesFromInputs() +{ + ConditionalThrow(GetInputSlot(0).GetConnection() != nullptr, + "MemCopyLayer: InputSlot must be connected to an OutputSlot"); + ConditionalThrow(GetInputSlot(0).GetConnection()->IsTensorInfoSet(), + "MemCopyLayer: TensorInfo must be set on connected OutputSlot."); + + 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}); + ConditionalThrowIfNotEqual( + "ResizeBilinearLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.", + GetOutputSlot(0).GetTensorInfo().GetShape(), + outShape); +} + +} // namespace armnn diff --git a/src/armnn/layers/ResizeBilinearLayer.hpp b/src/armnn/layers/ResizeBilinearLayer.hpp new file mode 100644 index 0000000000..2cefedb0b8 --- /dev/null +++ b/src/armnn/layers/ResizeBilinearLayer.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 "LayerWithParameters.hpp" + +namespace armnn +{ + +class ResizeBilinearLayer : public LayerWithParameters +{ +public: + virtual std::unique_ptr + CreateWorkload(const Graph& graph, const IWorkloadFactory& factory) const override; + + ResizeBilinearLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + +protected: + ResizeBilinearLayer(const ResizeBilinearDescriptor& param, const char* name); + ~ResizeBilinearLayer() = default; +}; + +} // namespace diff --git a/src/armnn/layers/SoftmaxLayer.cpp b/src/armnn/layers/SoftmaxLayer.cpp new file mode 100644 index 0000000000..2bd0c1d106 --- /dev/null +++ b/src/armnn/layers/SoftmaxLayer.cpp @@ -0,0 +1,44 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "SoftmaxLayer.hpp" + +#include "LayerCloneBase.hpp" + +#include +#include +#include + +namespace armnn +{ + +SoftmaxLayer::SoftmaxLayer(const SoftmaxDescriptor ¶m, const char* name) + : LayerWithParameters(1, 1, LayerType::Softmax, param, name) +{ +} + +std::unique_ptr SoftmaxLayer::CreateWorkload(const Graph& graph, const IWorkloadFactory& factory) const +{ + SoftmaxQueueDescriptor descriptor; + return factory.CreateSoftmax(descriptor, PrepInfoAndDesc(descriptor, graph)); +} + +SoftmaxLayer* SoftmaxLayer::Clone(Graph& graph) const +{ + return CloneBase(graph, m_Param, GetName()); +} + +void SoftmaxLayer::ValidateTensorShapesFromInputs() +{ + ConditionalThrow(GetInputSlot(0).GetConnection() != nullptr, + "SoftmaxLayer: Input slot must be connected."); + + const TensorShape& outShape = GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(); + ConditionalThrowIfNotEqual( + "SoftmaxLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.", + GetOutputSlot(0).GetTensorInfo().GetShape(), + outShape); +} + +} // namespace armnn diff --git a/src/armnn/layers/SoftmaxLayer.hpp b/src/armnn/layers/SoftmaxLayer.hpp new file mode 100644 index 0000000000..ff60a08a91 --- /dev/null +++ b/src/armnn/layers/SoftmaxLayer.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 "LayerWithParameters.hpp" + +namespace armnn +{ + +class Pooling2dLayer : public LayerWithParameters +{ +public: + virtual std::unique_ptr CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const override; + + Pooling2dLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + +protected: + Pooling2dLayer(const Pooling2dDescriptor& param, const char* name); + ~Pooling2dLayer() = default; +}; + +} // namespace diff --git a/src/armnn/layers/SplitterLayer.cpp b/src/armnn/layers/SplitterLayer.cpp new file mode 100644 index 0000000000..630921e4d8 --- /dev/null +++ b/src/armnn/layers/SplitterLayer.cpp @@ -0,0 +1,81 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// See LICENSE file in the project root for full license information. +// +#include "SplitterLayer.hpp" + +#include "LayerCloneBase.hpp" + +#include +#include +#include + +namespace armnn +{ + +SplitterLayer::SplitterLayer(const ViewsDescriptor& param, const char* name) + : LayerWithParameters(1, param.GetNumViews(), LayerType::Splitter, param, name) +{ +} + +std::unique_ptr SplitterLayer::CreateWorkload(const Graph& graph, const IWorkloadFactory& factory) const +{ + SplitterQueueDescriptor descriptor; + + // copy the window origins to the descriptor + for (unsigned int i = 0; i < m_Param.GetNumViews(); ++i) + { + descriptor.m_ViewOrigins.emplace_back( + std::vector(m_Param.GetViewOrigin(i), m_Param.GetViewOrigin(i) + m_Param.GetNumDimensions())); + } + + return factory.CreateSplitter(descriptor, PrepInfoAndDesc(descriptor, graph)); +} + +void SplitterLayer::CreateTensorHandles(Graph& graph, const IWorkloadFactory& factory) +{ + //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 + for (unsigned int i = 0; i < m_Param.GetNumViews(); ++i) + { + m_OutputHandlers[i].SetData(factory.CreateSubTensorHandle(*inputData, + m_OutputHandlers[i].GetTensorInfo().GetShape(), + m_Param.GetViewOrigin(i))); + } + } + else + { + for (unsigned int i = 0; i < m_Param.GetNumViews(); ++i) + { + m_OutputHandlers[i].CreateTensorHandles(factory); + } + } +} + +SplitterLayer* SplitterLayer::Clone(Graph& graph) const +{ + return CloneBase(graph, m_Param, GetName()); +} + +void SplitterLayer::ValidateTensorShapesFromInputs() +{ + //Output shapes must match View shapes. + for (unsigned int viewIdx = 0; viewIdx < m_Param.GetNumViews(); viewIdx++) + { + const uint32_t* sizes = m_Param.GetViewSizes(viewIdx); + + TensorShape outShape(m_Param.GetNumDimensions(), sizes); + ConditionalThrowIfNotEqual( + "SplitterLayer: View sizes must match output tensor shapes.", + GetOutputSlot(viewIdx).GetTensorInfo().GetShape(), + outShape); + } +} + +} // namespace armnn diff --git a/src/armnn/layers/SplitterLayer.hpp b/src/armnn/layers/SplitterLayer.hpp new file mode 100644 index 0000000000..7e5bbd2668 --- /dev/null +++ b/src/armnn/layers/SplitterLayer.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 "LayerWithParameters.hpp" + +namespace armnn +{ + +class SplitterLayer : public LayerWithParameters +{ +public: + virtual std::unique_ptr CreateWorkload(const Graph& graph, + const IWorkloadFactory& factory) const override; + virtual void CreateTensorHandles(Graph& graph, const IWorkloadFactory& factory) override; + + SplitterLayer* Clone(Graph& graph) const override; + + void ValidateTensorShapesFromInputs() override; + +protected: + SplitterLayer(const ViewsDescriptor& param, const char* name); + ~SplitterLayer() = default; +}; + +} // namespace -- cgit v1.2.1