ArmNN
 21.08
FoldPadIntoLayer2d.hpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #pragma once
7 
8 #include "Optimization.hpp"
9 
10 #include <QuantizeHelper.hpp>
11 
14 
15 namespace armnn
16 {
17 namespace optimizations
18 {
19 namespace pad_fold
20 {
21 inline float GetZeroElement(const TensorInfo& tensorInfo)
22 {
23  return static_cast<float>(tensorInfo.IsQuantized() ? tensorInfo.GetQuantizationOffset() : 0);
24 }
25 
26 inline float GetLowestElement(const TensorInfo& tensorInfo)
27 {
28  constexpr float negativeInfinity = -std::numeric_limits<float>::infinity();
29  const float scale = tensorInfo.GetQuantizationScale();
30  const int32_t offset = tensorInfo.GetQuantizationOffset();
31 
32  switch (tensorInfo.GetDataType())
33  {
34  case DataType::Float16:
35  return armnnUtils::SelectiveQuantize<armnn::Half>(negativeInfinity, scale, offset);
36  case DataType::Float32:
37  return armnnUtils::SelectiveQuantize<float>(negativeInfinity, scale, offset);
38  case DataType::QAsymmU8:
39  return armnnUtils::SelectiveQuantize<uint8_t>(negativeInfinity, scale, offset);
40  case DataType::QSymmS16:
41  return armnnUtils::SelectiveQuantize<int16_t>(negativeInfinity, scale, offset);
42  case DataType::QSymmS8:
43  // Fall-through
44  case DataType::QAsymmS8:
45  return armnnUtils::SelectiveQuantize<int8_t>(negativeInfinity, scale, offset);
46  case DataType::BFloat16:
47  return armnnUtils::SelectiveQuantize<armnn::BFloat16>(negativeInfinity, scale, offset);
48  default:
49  {
50  ARMNN_ASSERT_MSG(false, "Unsupported DataType");
51  return NAN;
52  }
53  }
54 }
55 
56 inline bool IsNeutralElement(const Convolution2dDescriptor&, const TensorInfo& tensorInfo, const float tensorValue)
57 {
58  return tensorValue == GetZeroElement(tensorInfo);
59 }
60 
62  const TensorInfo& tensorInfo,
63  const float tensorValue)
64 {
65  return tensorValue == GetZeroElement(tensorInfo);
66 }
67 
68 inline bool IsNeutralElement(
69  const Pooling2dDescriptor& descriptor, const TensorInfo& tensorInfo, const float tensorValue)
70 {
71  return (descriptor.m_PoolType == PoolingAlgorithm::Max)
72  ? tensorValue <= GetLowestElement(tensorInfo)
73  : tensorValue == GetZeroElement(tensorInfo);
74 }
75 
76 template <typename Descriptor>
78  const PadDescriptor& padDescriptor, Descriptor& layerDescriptor, const TensorInfo& tensorInfo)
79 {
80  armnnUtils::DataLayoutIndexed layout = armnnUtils::DataLayoutIndexed(layerDescriptor.m_DataLayout);
81  constexpr unsigned int batchIndex = 0;
82 
83  constexpr auto noPad = std::make_pair(0U, 0U);
84 
85  if ((!IsNeutralElement(layerDescriptor, tensorInfo, padDescriptor.m_PadValue)) ||
86  (padDescriptor.m_PadList[batchIndex] != noPad) || (padDescriptor.m_PadList[layout.GetChannelsIndex()] != noPad))
87  {
88  return false;
89  }
90 
91  const auto& padList = padDescriptor.m_PadList;
92 
93  // In Convolution2dDescriptor/Pooling2dDescriptor, padLeft and padRight are defined as paddings
94  // on width dimension whereas padTop and padBottom - paddings on height dimension, so updating
95  // these according to data layout
96  layerDescriptor.m_PadLeft += padList[layout.GetWidthIndex()].first;
97  layerDescriptor.m_PadRight += padList[layout.GetWidthIndex()].second;
98  layerDescriptor.m_PadTop += padList[layout.GetHeightIndex()].first;
99  layerDescriptor.m_PadBottom += padList[layout.GetHeightIndex()].second;
100 
101  return true;
102 }
103 
105  const PadDescriptor& padDescriptor, Pooling2dDescriptor& poolDescriptor, const TensorInfo& tensorInfo)
106 {
107  const auto poolingPadValues = std::make_tuple(poolDescriptor.m_PadLeft, poolDescriptor.m_PadRight,
108  poolDescriptor.m_PadTop, poolDescriptor.m_PadBottom);
109  bool poolHasPadding = false;
110  if (poolingPadValues != std::make_tuple(0U, 0U, 0U, 0U))
111  {
112  poolHasPadding = true;
113  }
114 
115  // We cannot fold Average or L2 pooling if there's is already padding and that padding method is Exclude.
116  if (poolDescriptor.m_PoolType != PoolingAlgorithm::Max) // PoolingAlgorithm::Average or PoolingAlgorithm::L2
117  {
118  if ((poolHasPadding) && (poolDescriptor.m_PaddingMethod == PaddingMethod::Exclude))
119  {
120  return false;
121  }
122  }
124 
125  return TryFoldPadIntoLayer2d<Pooling2dDescriptor>(padDescriptor, poolDescriptor, tensorInfo);
126 }
127 
128 template <typename Layer2dT>
129 Layer2dT* FoldPadIntoLayer2dImpl(Graph& graph, InputSlot& connection)
130 {
131  PadLayer& padLayer = *PolymorphicDowncast<PadLayer*>(&connection.GetConnectedOutputSlot()->GetOwningLayer());
132  Layer2dT& layer2d = *PolymorphicDowncast<Layer2dT*>(&connection.GetOwningLayer());
133 
134  const PadDescriptor& padDescriptor = padLayer.GetParameters();
135  auto newLayer2dDescriptor = layer2d.GetParameters();
136 
137  if (!TryFoldPadIntoLayer2d(padDescriptor, newLayer2dDescriptor, padLayer.GetOutputSlot().GetTensorInfo()))
138  {
139  return nullptr;
140  }
141 
142  // Save original parent output slot of the pad layer
143  OutputSlot& parentSlot = *padLayer.GetInputSlot(0).GetConnectedOutputSlot();
144 
145  // Insert new layer2d layer between the pad layer an its parent layer.
146  const std::string name = std::string("folded-") + padLayer.GetName() + "-into-" + layer2d.GetName();
147  auto& newLayer2d = *graph.InsertNewLayer<Layer2dT>(padLayer.GetInputSlot(0), newLayer2dDescriptor, name.c_str());
148 
149  // Reconnect the pad layer with its original parent.
150  newLayer2d.GetOutputSlot().MoveAllConnections(parentSlot);
151 
152  // Moves connections in old layer2d layer output to new layer.
153  // Old layer2d layer will be removed as it's left unconnected.
154  // Pad layer will be removed if left unconnected.
155  layer2d.GetOutputSlot().MoveAllConnections(newLayer2d.GetOutputSlot());
156 
157  return &newLayer2d;
158 }
159 
161 {
162 public:
163  void Run(Graph& graph, InputSlot& connection) const
164  {
165  const auto newConv2dLayer = FoldPadIntoLayer2dImpl<Convolution2dLayer>(graph, connection);
166 
167  if (newConv2dLayer != nullptr)
168  {
169  const auto conv2dLayer = PolymorphicDowncast<Convolution2dLayer*>(&connection.GetOwningLayer());
170  // Copy weights and bias to the new convolution layer
171  ARMNN_ASSERT_MSG(conv2dLayer->m_Weight != nullptr,
172  "FoldPadIntoConvolution2d: Weights data should not be null.");
173  newConv2dLayer->m_Weight = std::move(conv2dLayer->m_Weight);
174 
175  if (conv2dLayer->GetParameters().m_BiasEnabled)
176  {
177  ARMNN_ASSERT_MSG(conv2dLayer->m_Bias != nullptr,
178  "FoldPadIntoConvolution2d: Bias data should not be null if bias is enabled.");
179  newConv2dLayer->m_Bias = std::move(conv2dLayer->m_Bias);
180  }
181  }
182  }
183 
184 protected:
185  FoldPadIntoConvolution2dImpl() = default;
186  ~FoldPadIntoConvolution2dImpl() = default;
187 };
188 
190 {
191 public:
192  void Run(Graph& graph, InputSlot& connection) const
193  {
194  const auto newConv2dLayer = FoldPadIntoLayer2dImpl<DepthwiseConvolution2dLayer>(graph, connection);
195 
196  if (newConv2dLayer != nullptr)
197  {
198  const auto conv2dLayer = PolymorphicDowncast<DepthwiseConvolution2dLayer*>(&connection.GetOwningLayer());
199  // Copy weights and bias to the new convolution layer
200  ARMNN_ASSERT_MSG(conv2dLayer->m_Weight != nullptr,
201  "FoldPadIntoDepthwiseConvolution2d: Weights data should not be null.");
202  newConv2dLayer->m_Weight = std::move(conv2dLayer->m_Weight);
203 
204  if (conv2dLayer->GetParameters().m_BiasEnabled)
205  {
206  ARMNN_ASSERT_MSG(conv2dLayer->m_Bias != nullptr,
207  "FoldPadIntoDepthwiseConvolution2d: Bias data should not be null if bias is enabled.");
208  newConv2dLayer->m_Bias = std::move(conv2dLayer->m_Bias);
209  }
210  }
211  }
212 
213 protected:
216 };
217 
219 {
220 public:
221  void Run(Graph& graph, InputSlot& connection) const
222  {
223  FoldPadIntoLayer2dImpl<Pooling2dLayer>(graph, connection);
224  }
225 
226 protected:
227  FoldPadIntoPooling2dImpl() = default;
228  ~FoldPadIntoPooling2dImpl() = default;
229 };
230 } // namespace pad_fold
231 
238 using FoldPadIntoPooling2d =
240 
241 } // namespace optimizations
242 } // namespace armnn
243 
244 
uint32_t m_PadBottom
Padding bottom value in the height dimension.
const Parameters & GetParameters() const
unsigned int GetWidthIndex() const
uint32_t m_PadLeft
Padding left value in the width dimension.
void Run(Graph &graph, InputSlot &connection) const
float m_PadValue
Optional value to use for padding, defaults to 0.
Layer2dT * FoldPadIntoLayer2dImpl(Graph &graph, InputSlot &connection)
void Run(Graph &graph, InputSlot &connection) const
This layer represents a depthwise convolution 2d operation.
A Convolution2dDescriptor for the Convolution2dLayer.
Layer & GetOwningLayer() const
Definition: Layer.hpp:115
The padding fields don&#39;t count and are ignored.
PaddingMethod m_PaddingMethod
The padding method to be used. (Exclude, IgnoreValue).
uint32_t m_PadTop
Padding top value in the height dimension.
std::vector< std::pair< unsigned int, unsigned int > > m_PadList
Specifies the padding for input dimension.
Copyright (c) 2021 ARM Limited and Contributors.
This layer represents a pad operation.
Definition: PadLayer.hpp:14
unsigned int GetHeightIndex() const
A PadDescriptor for the PadLayer.
const InputSlot & GetInputSlot(unsigned int index) const override
Get a const input slot handle by slot index.
Definition: Layer.hpp:316
bool TryFoldPadIntoLayer2d(const PadDescriptor &padDescriptor, Descriptor &layerDescriptor, const TensorInfo &tensorInfo)
uint32_t m_PadRight
Padding right value in the width dimension.
#define ARMNN_ASSERT_MSG(COND, MSG)
Definition: Assert.hpp:15
int32_t GetQuantizationOffset() const
Definition: Tensor.cpp:480
float GetQuantizationScale() const
Definition: Tensor.cpp:463
Provides access to the appropriate indexes for Channels, Height and Width based on DataLayout...
DataType GetDataType() const
Definition: Tensor.hpp:198
const OutputSlot * GetConnectedOutputSlot() const
Definition: Layer.hpp:55
float GetZeroElement(const TensorInfo &tensorInfo)
Layer & GetOwningLayer() const
Definition: Layer.hpp:52
PoolingAlgorithm m_PoolType
The pooling algorithm to use (Max. Average, L2).
The padding fields count, but are ignored.
float GetLowestElement(const TensorInfo &tensorInfo)
const OutputSlot & GetOutputSlot(unsigned int index=0) const override
Get the const output slot handle by slot index.
Definition: Layer.hpp:318
const char * GetName() const override
Returns the name of the layer.
Definition: Layer.hpp:311
A Pooling2dDescriptor for the Pooling2dLayer.
LayerT * InsertNewLayer(InputSlot &insertBefore, Args &&... args)
Inserts a new layer between the output slot currently connected to insertBefore and insertBefore itse...
Definition: Graph.hpp:416
const TensorInfo & GetTensorInfo() const override
Definition: Layer.cpp:63
unsigned int GetChannelsIndex() const
bool IsQuantized() const
Definition: Tensor.cpp:506
A DepthwiseConvolution2dDescriptor for the DepthwiseConvolution2dLayer.
bool IsNeutralElement(const Convolution2dDescriptor &, const TensorInfo &tensorInfo, const float tensorValue)