ArmNN
 26.07
SpaceToBatchOperator.hpp File Reference
Include dependency graph for SpaceToBatchOperator.hpp:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

TosaSerializationBasicBlock * ConvertSpaceToBatchToTosaOperator (const Layer *layer, const std::vector< const TensorInfo * > &inputs, const std::vector< const TensorInfo * > &outputs, const SpaceToBatchNdDescriptor *spaceToBatchDescriptor)
 

Function Documentation

◆ ConvertSpaceToBatchToTosaOperator()

TosaSerializationBasicBlock* ConvertSpaceToBatchToTosaOperator ( const Layer layer,
const std::vector< const TensorInfo * > &  inputs,
const std::vector< const TensorInfo * > &  outputs,
const SpaceToBatchNdDescriptor spaceToBatchDescriptor 
)

Definition at line 12 of file SpaceToBatchOperator.cpp.

16 {
17  /*
18  * SpaceToBatchND - TOSA Lowering Overview
19  * --------------------------------------
20  * This operation takes a tensor for example one shaped like [B, D1, D2,- DN, C]
21  * and moves data from the spatial dimensions (D1-DN) into batch dimension.
22  *
23  * List of the steps involved:
24  *
25  * 1. Pad
26  * - Padding is applied in all cases whether there is 0 padding or not
27  * The reason padding is required is so that the reshape can work properly
28  * The input spatial dimensions in the tensor have to be evenly divisible by the block size
29  * so for example if you had a tensor that was shaped [1,5,5,1] with a block size of [2,2] that means you would
30  * need to pad with at least a value of [1,1] to allow the operation to proceed
31  *
32  * 2. Reshape (plus padding):
33  * - For each spatial dimension and its block size we split it in two: [Di / bi, bi].
34  * - After doing that across all spatial dims, the tensor ends up looking like:
35  * [B, D1 / b1, b1, D2 / b2, b2, ..., DN / bN, bN, C]
36  * e.g. input tensor [1,4,4,1] with block size [2,2] padding [0,0]
37  * would become [1, 2, 2, 2, 2, 1]
38  *
39  * 3. Transpose:
40  * - We move data around so that the block dimensions (b1...bN) are at the beginning.
41  * - Batch (B) moves after the block dims, followed by the reduced spatial dims
42  * and whatever else is left after that (usually the channels).
43  * - The transpose permutation vector at this point looks something like:
44  * [block_dims..., B, spatial_dims..., remainder]
45  * e.g. following on from the last example the previous input of [1, 2, 2, 2, 2, 1] would transpose
46  * to [2, 2, 1, 2, 2, 1]
47  *
48  * 4. Final Reshape:
49  * - We fold all the block dims into the batch.
50  * So new_batch = B * b1 * b2 * ... * bN.
51  * - The final shape becomes:
52  * [new_batch, D1 / b1, D2 / b2, ..., DN / bN, C]
53  * [2, 2, 1, 2, 2, 1] -> [4, 2, 2, 1]
54  */
55 
56 
57  ARMNN_THROW_INVALIDARG_MSG_IF_FALSE(inputs.size() == 1,
58  "ConvertSpaceToBatchToTosaOperator: SpaceToBatch must have only one input");
59 
60  ARMNN_THROW_INVALIDARG_MSG_IF_FALSE(outputs.size() == 1,
61  "ConvertSpaceToBatchToTosaOperator: SpaceToBatch must have only one output");
62 
63  std::string inputName = "input_";
64  std::string outputNamePad = "layer_intermediate1_" + GetUniqueTosaMappingID();
65  std::string outputNameReshape1 = "layer_intermediate2_" + GetUniqueTosaMappingID();
66  std::string outputNameTranspose = "layer_intermediate3_" + GetUniqueTosaMappingID();
67  std::string outputName = "output0_";
68  std::string blockName = "Op_SPACETOBATCH_block_" + GetUniqueTosaMappingID();
69 
70  if (layer != nullptr)
71  {
72  inputName = GenerateUniqueInputName(layer->GetInputSlot(0));
73  outputName = GenerateUniqueOutputName(*layer);
74  }
75 
76  const auto& paddings = spaceToBatchDescriptor->m_PadList;
77  const auto& blockShape = spaceToBatchDescriptor->m_BlockShape;
78  const unsigned int inputRank = inputs[0]->GetShape().GetNumDimensions();
79  const unsigned int blockRank = static_cast<unsigned int>(blockShape.size());
80  std::vector<int32_t> inputShape = GetTosaTensorShape(inputs[0]->GetShape());
81 
82  if (inputRank <= blockRank)
83  {
84  throw armnn::Exception("ConvertSpaceToBatchToTosaOperator: input rank must be greater than block rank");
85  }
86 
87  std::vector<TosaSerializationTensor*> tensors;
88  std::vector<TosaSerializationOperator*> operators;
89 
90  // create a padding vector which is double the size of the inputRank
91  // each dimension requires two values lo and hi padding
92  std::vector<int32_t> a0Pad(2 * inputRank, 0);
93  std::vector<int32_t> paddedShape = inputShape;
94 
95  DType inputDType = ArmNNToDType(inputs[0]->GetDataType());
96 
97  if (inputName.find("input_") != std::string::npos)
98  {
99  tensors.push_back(new TosaSerializationTensor(inputName, inputShape, inputDType, {}));
100  }
101  // Build up the padding for the pad operation
102  for (size_t i = 0; i < blockShape.size(); ++i)
103  {
104  int32_t loPad = static_cast<int32_t>(paddings[i].first);
105  int32_t hiPad = static_cast<int32_t>(paddings[i].second);
106  size_t dimIndex = i + 1;
107  a0Pad[2 * dimIndex] = loPad;
108  a0Pad[2 * dimIndex + 1] = hiPad;
109  paddedShape[dimIndex] = inputShape[dimIndex] + loPad + hiPad;
110  }
111 
112  std::string padOutput = outputNamePad + "_padded";
113 
114  tensors.push_back(new TosaSerializationTensor(padOutput, paddedShape, inputDType, {}));
115 
116  // handle pad value if input is quantized
117  float padValue = 0.0f;
118  if (inputs[0]->IsQuantized())
119  {
120  padValue = static_cast<float>(inputs[0]->GetQuantizationOffset()) * inputs[0]->GetQuantizationScale();
121  }
122 
123  TosaPadAttribute padAttr(a0Pad, 0, padValue);
124  operators.push_back(new TosaSerializationOperator(Op_PAD,
125  Attribute_PadAttribute,
126  &padAttr,
127  {inputName},
128  {padOutput}));
129 
130  // setup the first reshape operation
131  std::vector<int32_t> reshape1;
132  // add the original batch dimension
133  reshape1.push_back(inputShape[0]);
134 
135  // setup a variable to keep track of the total block multiplier
136  int32_t blockNumElems = 1;
137 
138  // iterate over the rest of the spatial dimensions i.e. H, W, D
139  for (size_t i = 0; i < blockShape.size(); ++i)
140  {
141  int32_t paddedDim = paddedShape[i + 1]; // padded spatial dimension
142  int32_t blockDim = static_cast<int32_t>(blockShape[i]); // block dimension to be transposed into batch
143  if (paddedDim % blockDim != 0)
144  {
145  throw armnn::Exception("ConvertSpaceToBatchToTosaOperator: padded spatial dim not divisible by block size");
146  }
147  reshape1.push_back(paddedDim / blockDim);
148  reshape1.push_back(blockDim);
149 
150  blockNumElems *= blockDim;
151  }
152 
153  // append any remaining non spatial dimensions as is
154  for (size_t i = 1 + blockShape.size(); i < inputShape.size(); ++i)
155  {
156  reshape1.push_back(inputShape[i]);
157  }
158 
159  tensors.push_back(new TosaSerializationTensor(outputNameReshape1, reshape1, inputDType, {}));
160  TosaReshapeAttribute reshapeAttr(reshape1);
161  operators.push_back(new TosaSerializationOperator(Op_RESHAPE,
162  Attribute_ReshapeAttribute,
163  &reshapeAttr,
164  {padOutput},
165  {outputNameReshape1}));
166 
167  std::vector<int32_t> transposeVec;
168 
169  // move all the block dimensions to the front before the batch dimension
170  for (size_t i = 0; i < blockShape.size(); ++i)
171  {
172  transposeVec.push_back(static_cast<int32_t>(1 + 2 * i + 1));
173  }
174  // add the original batch dimensions (always located at pos 0 of the previously reshaped data)
175  transposeVec.push_back(0);
176 
177  // add the spatial dimensions
178  for (size_t i = 0; i < blockShape.size(); ++i)
179  {
180  transposeVec.push_back(static_cast<int32_t>(1 + 2 * i));
181  }
182 
183  // add any remaining dimensions
184  for (size_t i = 1 + 2 * blockShape.size(); i < reshape1.size(); ++i)
185  {
186  transposeVec.push_back(static_cast<int32_t>(i));
187  }
188  // copy the reshaped1 value to begin applying the transpose to it
189  std::vector<int32_t> transposeShape(transposeVec.size());
190  for (size_t i = 0; i < transposeVec.size(); ++i)
191  {
192  transposeShape[i] = reshape1[static_cast<size_t>(transposeVec[i])];
193  }
194  tensors.push_back(new TosaSerializationTensor(outputNameTranspose, transposeShape, inputDType, {}));
195 
196  TosaTransposeAttribute transposeAttr(transposeVec);
197 
198  operators.push_back(new TosaSerializationOperator(Op_TRANSPOSE,
199  Attribute_TransposeAttribute,
200  &transposeAttr,
201  {outputNameReshape1},
202  {outputNameTranspose}));
203 
204  // setup vector to hold final reshape information
205  std::vector<int32_t> reshape2;
206  // determine the new batch size, which is the total number of block elements multiplied by the original batch
207  const int32_t newBatch = static_cast<int32_t>(inputShape[0]) * static_cast<int32_t>(blockNumElems);
208  reshape2.push_back(newBatch);
209 
210  // Add spatial dims each of which is reduced by its corresponding block i.e. padded / block
211  for (size_t i = 0; i < blockShape.size(); ++i)
212  {
213  int32_t paddedDim = paddedShape[i + 1];
214  int32_t blockDim = static_cast<int32_t>(blockShape[i]);
215 
216  if (blockDim == 0 || paddedDim % blockDim != 0)
217  {
218  throw armnn::Exception("ConvertSpaceToBatchToTosaOperator: Invalid block Shape or padding in final reshape");
219  }
220 
221  reshape2.push_back(paddedDim / blockDim);
222  }
223 
224  // Add remaining dims
225  reshape2.push_back(inputShape.back());
226  tensors.push_back(new TosaSerializationTensor(outputName, reshape2, inputDType, {}));
227 
228  TosaReshapeAttribute reshape2Attr(reshape2);
229  operators.push_back(new TosaSerializationOperator(Op_RESHAPE,
230  Attribute_ReshapeAttribute,
231  &reshape2Attr,
232  {outputNameTranspose},
233  {outputName}));
234 
235  std::vector<int32_t> expectedShape = GetTosaTensorShape(outputs[0]->GetShape());
236 
237  if (reshape2 != expectedShape)
238  {
239  throw armnn::Exception("ConvertSpaceToBatchToTosaOperator: Mismatch expected output and generated shape differ");
240  }
241 
242  return new TosaSerializationBasicBlock(blockName, mainName, operators, tensors, {inputName}, {outputName});
243 }
#define ARMNN_THROW_INVALIDARG_MSG_IF_FALSE(_cond, _str)
Definition: Exceptions.hpp:210
std::string GenerateUniqueOutputName(const Layer &layer, uint32_t layerSlot=0)
const std::string mainName
DType ArmNNToDType(const DataType &type)
std::vector< int32_t > GetTosaTensorShape(const TensorShape &shape)
std::string GenerateUniqueInputName(const armnn::InputSlot &slot)
std::string GetUniqueTosaMappingID()
Base class for all ArmNN exceptions so that users can filter to just those.
Definition: Exceptions.hpp:47
const InputSlot & GetInputSlot(unsigned int index) const override
Get a const input slot handle by slot index.
Definition: Layer.hpp:337
std::vector< unsigned int > m_BlockShape
Block shape value.
std::vector< std::pair< unsigned int, unsigned int > > m_PadList
Specifies the padding values for the input dimension: heightPad{top, bottom} widthPad{left,...

References ARMNN_THROW_INVALIDARG_MSG_IF_FALSE, GenerateUniqueInputName(), GenerateUniqueOutputName(), Layer::GetInputSlot(), GetTosaTensorShape(), GetUniqueTosaMappingID(), SpaceToBatchNdDescriptor::m_BlockShape, and SpaceToBatchNdDescriptor::m_PadList.

Referenced by GetTosaMapping().