ArmNN
 25.11
Loading...
Searching...
No Matches
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)
std::string GenerateUniqueOutputName(const Layer &layer, uint32_t layerSlot=0)
const std::string mainName
DType ArmNNToDType(const DataType &type)
std::string GenerateUniqueInputName(const armnn::InputSlot &slot)
std::string GetUniqueTosaMappingID()
std::vector< int32_t > GetTosaTensorShape(const TensorShape &shape)
Base class for all ArmNN exceptions so that users can filter to just those.
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, ArmNNToDType(), GenerateUniqueInputName(), GenerateUniqueOutputName(), Layer::GetInputSlot(), GetTosaTensorShape(), GetUniqueTosaMappingID(), SpaceToBatchNdDescriptor::m_BlockShape, SpaceToBatchNdDescriptor::m_PadList, and mainName.

Referenced by GetTosaMapping().