aboutsummaryrefslogtreecommitdiff
path: root/src/armnn/Layers.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/armnn/Layers.cpp')
-rw-r--r--src/armnn/Layers.cpp986
1 files changed, 986 insertions, 0 deletions
diff --git a/src/armnn/Layers.cpp b/src/armnn/Layers.cpp
new file mode 100644
index 0000000000..ddbc7d222c
--- /dev/null
+++ b/src/armnn/Layers.cpp
@@ -0,0 +1,986 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// See LICENSE file in the project root for full license information.
+//
+#include "Layers.hpp"
+#include "Graph.hpp"
+
+#include "backends/CpuTensorHandle.hpp"
+#include "backends/Workload.hpp"
+#include "backends/WorkloadFactory.hpp"
+
+#include "Permute.hpp"
+
+
+namespace armnn
+{
+
+template <typename LayerType, typename ... Params>
+LayerType* Layer::CloneBase(Graph& graph, Params&& ... params) const
+{
+ LayerType* const layer = graph.AddLayer<LayerType>(std::forward<Params>(params)...);
+
+ layer->SetComputeDevice(m_ComputeDevice);
+
+ return layer;
+}
+
+ActivationLayer::ActivationLayer(const ActivationDescriptor& param, const char* name)
+ : LayerWithParameters(1, 1, LayerType::Activation, param, name)
+{
+}
+
+std::unique_ptr<IWorkload> 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<ActivationLayer>(graph, m_Param, GetName());
+}
+
+void ActivationLayer::ValidateTensorShapesFromInputs()
+{
+ auto& info = GetInputSlot(0).GetConnection()->GetTensorInfo();
+ ConditionalThrow<LayerValidationException>(GetOutputSlot(0).ValidateTensorShape(info.GetShape()),
+ "ActivationLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.");
+}
+
+AdditionLayer::AdditionLayer(const char* name)
+ : Layer(2, 1, LayerType::Addition, name)
+{
+}
+
+std::unique_ptr<IWorkload> 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<AdditionLayer>(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<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];
+ if (dim0 != dim1)
+ {
+ BOOST_ASSERT_MSG(dim0 == 1 || dim1 == 1, "Dimensions should either match or one should be one length");
+ }
+ }
+#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());
+ ConditionalThrow<LayerValidationException>(GetOutputSlot(0).ValidateTensorShape(outShape),
+ "AdditionLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.");
+}
+
+BatchNormalizationLayer::BatchNormalizationLayer(const armnn::BatchNormalizationDescriptor& param, const char* name)
+ : LayerWithParameters(1, 1, LayerType::BatchNormalization, param, name)
+{
+}
+
+std::unique_ptr<IWorkload> 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<BatchNormalizationLayer>(graph, m_Param, GetName());
+
+ layer->m_Mean = m_Mean ? std::make_unique<ScopedCpuTensorHandle>(*m_Mean) : nullptr;
+ layer->m_Variance = m_Variance ? std::make_unique<ScopedCpuTensorHandle>(*m_Variance) : nullptr;
+ layer->m_Beta = m_Beta ? std::make_unique<ScopedCpuTensorHandle>(*m_Beta) : nullptr;
+ layer->m_Gamma = m_Gamma ? std::make_unique<ScopedCpuTensorHandle>(*m_Gamma) : nullptr;
+
+ return std::move(layer);
+}
+
+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.");
+
+ auto& info = GetInputSlot(0).GetConnection()->GetTensorInfo();
+ ConditionalThrow<LayerValidationException>(GetOutputSlot(0).ValidateTensorShape(info.GetShape()),
+ "BatchNormalizationLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.");
+}
+
+Convolution2dLayer::Convolution2dLayer(const Convolution2dDescriptor& param, const char* name)
+ : LayerWithParameters(1, 1, LayerType::Convolution2d, param, name)
+{
+}
+
+std::unique_ptr<IWorkload> 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<Convolution2dLayer>(graph, m_Param, GetName());
+ layer->m_Weight = m_Weight ? std::make_unique<ScopedCpuTensorHandle>(*m_Weight) : nullptr;
+
+ if (layer->m_Param.m_BiasEnabled)
+ {
+ layer->m_Bias = m_Bias ? std::make_unique<ScopedCpuTensorHandle>(*m_Bias) : nullptr;
+ }
+
+ return std::move(layer);
+}
+
+void Convolution2dLayer::ValidateTensorShapesFromInputs()
+{
+ 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();
+
+ // 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});
+ ConditionalThrow<LayerValidationException>(GetOutputSlot(0).ValidateTensorShape(shapeOut),
+ "Convolution2dLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.");
+}
+
+
+DepthwiseConvolution2dLayer::DepthwiseConvolution2dLayer(const DepthwiseConvolution2dDescriptor& param,
+ const char* name)
+ : LayerWithParameters(1, 1, LayerType::DepthwiseConvolution2d, param, name)
+{
+}
+
+std::unique_ptr<IWorkload> 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<DepthwiseConvolution2dLayer>(graph, m_Param, GetName());
+ layer->m_Weight = m_Weight ? std::make_unique<ScopedCpuTensorHandle>(*m_Weight) : nullptr;
+
+ if (layer->m_Param.m_BiasEnabled)
+ {
+ layer->m_Bias = m_Bias ? std::make_unique<ScopedCpuTensorHandle>(*m_Bias) : nullptr;
+ }
+
+ return std::move(layer);
+}
+
+void DepthwiseConvolution2dLayer::ValidateTensorShapesFromInputs()
+{
+ 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_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});
+ ConditionalThrow<LayerValidationException>(GetOutputSlot(0).ValidateTensorShape(outShape),
+ "DepthwiseConvolution2dLayer: "
+ "TensorShape set on OutputSlot[0] does not match the inferred shape.");
+}
+
+FakeQuantizationLayer::FakeQuantizationLayer(const FakeQuantizationDescriptor& param, const char* name)
+: LayerWithParameters(1, 1, LayerType::FakeQuantization, param, name)
+{
+}
+
+std::unique_ptr<IWorkload> 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<FakeQuantizationLayer>(graph, m_Param, GetName());
+}
+
+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.");
+
+
+ IOutputSlot* input = GetInputSlot(0).GetConnection();
+
+ // input and output shapes are the same
+ TensorShape const& outShape = input->GetTensorInfo().GetShape();
+ ConditionalThrow<LayerValidationException>(GetOutputSlot(0).ValidateTensorShape(outShape),
+ "FakeQuantizationLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.");
+}
+
+FloorLayer::FloorLayer(const char* name)
+ : Layer(1, 1, LayerType::Floor, name)
+{
+}
+
+std::unique_ptr<IWorkload> 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<FloorLayer>(graph, GetName());
+}
+
+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();
+ ConditionalThrow<LayerValidationException>(GetOutputSlot(0).ValidateTensorShape(outShape),
+ "FloorLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.");
+}
+
+FullyConnectedLayer::FullyConnectedLayer(const FullyConnectedDescriptor& param, const char* name)
+ : LayerWithParameters(1, 1, LayerType::FullyConnected, param, name)
+{
+}
+
+std::unique_ptr<IWorkload> 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<FullyConnectedLayer>(graph, m_Param, GetName());
+
+ layer->m_Weight = m_Weight ? std::make_unique<ScopedCpuTensorHandle>(*m_Weight) : nullptr;
+ if (layer->m_Param.m_BiasEnabled)
+ {
+ layer->m_Bias = m_Bias ? std::make_unique<ScopedCpuTensorHandle>(*m_Bias) : nullptr;
+ }
+
+ return std::move(layer);
+}
+
+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.");
+
+
+ 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]});
+
+ ConditionalThrow<LayerValidationException>(GetOutputSlot(0).ValidateTensorShape(outShape),
+ "FullyConnectedLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.");
+}
+
+InputLayer::InputLayer(LayerBindingId id, const char* name)
+ : BindableLayer(0, 1, LayerType::Input, name, id)
+{
+}
+
+std::unique_ptr<IWorkload> InputLayer::CreateWorkload(const Graph& graph, const IWorkloadFactory& factory) const
+{
+ return nullptr;
+}
+
+InputLayer* InputLayer::Clone(Graph& graph) const
+{
+ return CloneBase<InputLayer>(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<LayerValidationException>(GetOutputSlot(0).IsTensorInfoSet(),
+ "InputLayer should already have the TensorInfo set.");
+}
+
+
+MergerLayer::MergerLayer(const OriginsDescriptor& param, const char* name)
+ : LayerWithParameters(param.GetNumViews(), 1, LayerType::Merger, param, name)
+{
+}
+
+std::unique_ptr<IWorkload> 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<unsigned int>(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())
+ {
+ const unsigned int numInputSlots = GetNumInputSlots();
+ for (unsigned int i = 0; i < numInputSlots; ++i)
+ {
+ OutputHandler& outputHandler = GetInputSlot(i).GetConnectedOutputSlot()->GetOutputHandler();
+
+ outputHandler.SetData(factory.CreateSubTensorHandle(*m_OutputHandlers[0].GetData(),
+ outputHandler.GetTensorInfo().GetShape(),
+ m_Param.GetViewOrigin(i)));
+ }
+ }
+}
+
+MergerLayer* MergerLayer::Clone(Graph& graph) const
+{
+ return CloneBase<MergerLayer>(graph, m_Param, GetName());
+}
+
+void MergerLayer::ValidateTensorShapesFromInputs()
+{
+ // Validate Merger layer
+ ConditionalThrow<LayerValidationException>(m_Param.GetNumViews() == GetNumInputSlots(),
+ "MergerLayer: Num Inputs must match num views.");
+
+ unsigned int numDims = m_Param.GetNumDimensions();
+ for (unsigned int i=0; i<GetNumInputSlots(); i++)
+ {
+ auto& inputInfo = GetInputSlot(i).GetConnection()->GetTensorInfo();
+
+ boost::ignore_unused(inputInfo);
+ ConditionalThrow<LayerValidationException>(numDims == inputInfo.GetNumDimensions(),
+ "MergerLayer: Num Dimensions must match all inputs.");
+ }
+
+ // Find 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++)
+ {
+ 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]);
+ }
+ if (totalViewsVolume != outputVolume)
+ {
+ throw LayerValidationException("MergerLayer: there are some gaps between views");
+ }
+
+ TensorShape outShape(numDims, extentMax.data());
+ ConditionalThrow<LayerValidationException>(GetOutputSlot(0).ValidateTensorShape(outShape),
+ "MergerLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.");
+}
+
+MultiplicationLayer::MultiplicationLayer(const char* name)
+ : Layer(2, 1, LayerType::Multiplication, name)
+{
+}
+
+std::unique_ptr<IWorkload> 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<MultiplicationLayer>(graph, GetName());
+}
+
+void MultiplicationLayer::ValidateTensorShapesFromInputs()
+{
+ ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape() ==
+ GetInputSlot(1).GetConnection()->GetTensorInfo().GetShape(),
+ "MultiplicationLayer: Inputs must match");
+
+ TensorInfo infoOut(GetInputSlot(0).GetConnection()->GetTensorInfo());
+ ConditionalThrow<LayerValidationException>(GetOutputSlot(0).ValidateTensorShape(infoOut.GetShape()),
+ "MultiplicationLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.");
+}
+
+NormalizationLayer::NormalizationLayer(const NormalizationDescriptor& param, const char* name)
+ : LayerWithParameters(1, 1, LayerType::Normalization, param, name)
+{
+}
+
+std::unique_ptr<IWorkload> 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<NormalizationLayer>(graph, m_Param, GetName());
+}
+
+void NormalizationLayer::ValidateTensorShapesFromInputs()
+{
+ ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection() != nullptr,
+ "NormalizationLayer: Input slot must be connected.");
+
+ const TensorShape& outShape = GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape();
+ ConditionalThrow<LayerValidationException>(GetOutputSlot(0).ValidateTensorShape(outShape),
+ "NormalizationLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.");
+}
+
+OutputLayer::OutputLayer(LayerBindingId id, const char* name)
+ : BindableLayer(1, 0, LayerType::Output, name, id)
+{
+}
+
+std::unique_ptr<IWorkload> OutputLayer::CreateWorkload(const Graph& graph, const IWorkloadFactory& factory) const
+{
+ return nullptr;
+}
+
+OutputLayer* OutputLayer::Clone(Graph& graph) const
+{
+ return CloneBase<OutputLayer>(graph, GetBindingId(), GetName());
+}
+
+void OutputLayer::ValidateTensorShapesFromInputs()
+{
+ // Just validate the input is connected
+ ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection() != nullptr,
+ "OutputLayer: Input slot must be connected.");
+}
+
+PermuteLayer::PermuteLayer(const PermuteDescriptor& param, const char* name)
+ : LayerWithParameters(1, 1, LayerType::Permute, param, name)
+{
+}
+
+std::unique_ptr<IWorkload> 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<PermuteLayer>(graph, m_Param, GetName());
+}
+
+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.");
+
+ const TensorInfo& infoIn = GetInputSlot(0).GetConnection()->GetTensorInfo();
+ TensorShape shapeOut = armnnUtils::Permuted(infoIn.GetShape(), m_Param.m_DimMappings);
+ ConditionalThrow<LayerValidationException>(GetOutputSlot(0).ValidateTensorShape(shapeOut),
+ "PermuteLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.");
+}
+
+Pooling2dLayer::Pooling2dLayer(const Pooling2dDescriptor& param, const char* name)
+ : LayerWithParameters(1, 1, LayerType::Pooling2d, param, name)
+{
+}
+
+std::unique_ptr<IWorkload> 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<Pooling2dLayer>(graph, m_Param, GetName());
+}
+
+void Pooling2dLayer::ValidateTensorShapesFromInputs()
+{
+ 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();
+
+ // 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<float>(readSize) / static_cast<float>(stride);
+
+ unsigned int size = 0;
+ switch (outputShapeRounding)
+ {
+ case OutputShapeRounding::Ceiling:
+ size = static_cast<unsigned int>(ceil(div)) + 1;
+ break;
+ case OutputShapeRounding ::Floor:
+ size = static_cast<unsigned int>(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});
+
+ ConditionalThrow<LayerValidationException>(GetOutputSlot(0).ValidateTensorShape(shapeOut),
+ "Pooling2dLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.");
+}
+
+SoftmaxLayer::SoftmaxLayer(const SoftmaxDescriptor &param, const char* name)
+ : LayerWithParameters(1, 1, LayerType::Softmax, param, name)
+{
+}
+
+std::unique_ptr<IWorkload> 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<SoftmaxLayer>(graph, m_Param, GetName());
+}
+
+void SoftmaxLayer::ValidateTensorShapesFromInputs()
+{
+ ConditionalThrow<LayerValidationException>(GetInputSlot(0).GetConnection() != nullptr,
+ "SoftmaxLayer: Input slot must be connected.");
+ const TensorShape& outShape = GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape();
+ ConditionalThrow<LayerValidationException>(GetOutputSlot(0).ValidateTensorShape(outShape),
+ "SoftmaxLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.");
+}
+
+SplitterLayer::SplitterLayer(const ViewsDescriptor& param, const char* name)
+ : LayerWithParameters(1, param.GetNumViews(), LayerType::Splitter, param, name)
+{
+}
+
+std::unique_ptr<IWorkload> 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<unsigned int>(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<SplitterLayer>(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);
+ ConditionalThrow<LayerValidationException>(GetOutputSlot(viewIdx).ValidateTensorShape(outShape),
+ "SplitterLayer: View sizes must match output tensor shapes.");
+ }
+}
+
+MemCopyLayer::MemCopyLayer(const char* name)
+ : Layer(1, 1, LayerType::MemCopy, name)
+{
+}
+
+MemCopyLayer* MemCopyLayer::Clone(Graph& graph) const
+{
+ return CloneBase<MemCopyLayer>(graph, GetName());
+}
+
+std::unique_ptr<IWorkload> MemCopyLayer::CreateWorkload(const Graph& graph, const IWorkloadFactory& factory) const
+{
+ MemCopyQueueDescriptor descriptor;
+ return factory.CreateMemCopy(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.");
+
+
+ IOutputSlot* input = GetInputSlot(0).GetConnection();
+
+ // input and output shapes are the same
+ TensorShape const& outShape = input->GetTensorInfo().GetShape();
+ ConditionalThrow<LayerValidationException>(GetOutputSlot(0).ValidateTensorShape(outShape),
+ "MemCopyLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.");
+}
+
+ResizeBilinearLayer::ResizeBilinearLayer(const ResizeBilinearDescriptor& param, const char* name)
+ : LayerWithParameters(1, 1, LayerType::ResizeBilinear, param, name)
+{
+}
+
+std::unique_ptr<IWorkload> 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<ResizeBilinearLayer>(graph, m_Param, GetName());
+}
+
+void ResizeBilinearLayer::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.");
+
+ 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});
+ ConditionalThrow<LayerValidationException>(GetOutputSlot(0).ValidateTensorShape(outShape),
+ "ResizeBilinearLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.");
+}
+
+L2NormalizationLayer::L2NormalizationLayer(const char* name)
+ : Layer(1, 1, LayerType::L2Normalization, name)
+{
+}
+
+std::unique_ptr<IWorkload> 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<L2NormalizationLayer>(graph, GetName());
+}
+
+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.");
+
+ IOutputSlot* input = GetInputSlot(0).GetConnection();
+
+ // input and output shapes are the same
+ TensorShape const& outShape = input->GetTensorInfo().GetShape();
+ ConditionalThrow<LayerValidationException>(GetOutputSlot(0).ValidateTensorShape(outShape),
+ "L2NormalizationLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.");
+}
+
+ConstantLayer::ConstantLayer(const std::shared_ptr<ScopedCpuTensorHandle>& input, const char* name)
+ : Layer(0, 1, LayerType::Constant, name)
+ , m_LayerOutput(input)
+{
+}
+
+std::unique_ptr<IWorkload> 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<ConstantLayer>(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();
+ ConditionalThrow<LayerValidationException>(GetOutputSlot(0).ValidateTensorShape(outShape),
+ "ConstantLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.");
+}
+
+ReshapeLayer::ReshapeLayer(const ReshapeDescriptor& param, const char* name)
+ : LayerWithParameters(1, 1, LayerType::Reshape, param, name)
+{
+}
+
+std::unique_ptr<IWorkload> 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<ReshapeLayer>(graph, m_Param, GetName());
+}
+
+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.");
+ ConditionalThrow<LayerValidationException>(GetOutputSlot(0).ValidateTensorShape(m_Param.m_TargetShape),
+ "ReshapeLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.");
+}
+
+}