• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright © 2017-2023 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include <armnnUtils/TensorUtils.hpp>
7 
8 #include <armnn/backends/ITensorHandle.hpp>
9 #include <armnn/utility/Assert.hpp>
10 #include <armnn/utility/NumericCast.hpp>
11 
12 #include <fmt/format.h>
13 
14 using namespace armnn;
15 
16 namespace armnnUtils
17 {
18 
GetTensorShape(unsigned int numberOfBatches,unsigned int numberOfChannels,unsigned int height,unsigned int width,const DataLayout dataLayout)19 TensorShape GetTensorShape(unsigned int numberOfBatches,
20                                   unsigned int numberOfChannels,
21                                   unsigned int height,
22                                   unsigned int width,
23                                   const DataLayout dataLayout)
24 {
25     switch (dataLayout)
26     {
27         case DataLayout::NCHW:
28             return TensorShape({numberOfBatches, numberOfChannels, height, width});
29         case DataLayout::NHWC:
30             return TensorShape({numberOfBatches, height, width, numberOfChannels});
31         default:
32             throw InvalidArgumentException("Unknown data layout ["
33                                                   + std::to_string(static_cast<int>(dataLayout)) +
34                                                   "]", CHECK_LOCATION());
35     }
36 }
37 
GetTensorInfo(unsigned int numberOfBatches,unsigned int numberOfChannels,unsigned int height,unsigned int width,const DataLayout dataLayout,const DataType dataType)38 TensorInfo GetTensorInfo(unsigned int numberOfBatches,
39                                 unsigned int numberOfChannels,
40                                 unsigned int height,
41                                 unsigned int width,
42                                 const DataLayout dataLayout,
43                                 const DataType dataType)
44 {
45     switch (dataLayout)
46     {
47         case DataLayout::NCHW:
48             return TensorInfo({numberOfBatches, numberOfChannels, height, width}, dataType);
49         case DataLayout::NHWC:
50             return TensorInfo({numberOfBatches, height, width, numberOfChannels}, dataType);
51         default:
52             throw InvalidArgumentException("Unknown data layout ["
53                                                   + std::to_string(static_cast<int>(dataLayout)) +
54                                                   "]", CHECK_LOCATION());
55     }
56 }
57 
GetTensorInfo(unsigned int numberOfBatches,unsigned int numberOfChannels,unsigned int depth,unsigned int height,unsigned int width,const DataLayout dataLayout,const DataType dataType)58 TensorInfo GetTensorInfo(unsigned int numberOfBatches,
59                                 unsigned int numberOfChannels,
60                                 unsigned int depth,
61                                 unsigned int height,
62                                 unsigned int width,
63                                 const DataLayout dataLayout,
64                                 const DataType dataType)
65 {
66     switch (dataLayout)
67     {
68         case DataLayout::NDHWC:
69             return TensorInfo({numberOfBatches, depth, height, width, numberOfChannels}, dataType);
70         case DataLayout::NCDHW:
71             return TensorInfo({numberOfBatches, numberOfChannels, depth, height, width}, dataType);
72         default:
73             throw InvalidArgumentException("Unknown data layout ["
74                                                   + std::to_string(static_cast<int>(dataLayout)) +
75                                                   "]", CHECK_LOCATION());
76     }
77 }
78 
FindMinMax(ITensorHandle * tensorHandle)79 std::pair<float, float> FindMinMax(ITensorHandle* tensorHandle)
80 {
81     auto tensor_data = static_cast<const float *>(tensorHandle->Map(true));
82     auto tensor_size = tensorHandle->GetShape().GetNumElements();
83 
84     // Set min/max initially to first value in tensor
85     float min = tensor_data[0];
86     float max = tensor_data[0];
87 
88     // Loop over rest of tensor and update min/max if necessary
89     for (unsigned int val = 1; val < tensor_size; val++)
90     {
91         if (tensor_data[val] < min)
92         {
93             min = tensor_data[val];
94         }
95         else if (tensor_data[val] > max)
96         {
97             max = tensor_data[val];
98         }
99     }
100 
101     tensorHandle->Unmap();
102 
103     return std::make_pair(min, max);
104 }
105 
ReduceDims(const TensorShape & tensorShape,unsigned int dimensions)106 TensorShape ReduceDims(const TensorShape& tensorShape, unsigned int dimensions)
107 {
108     if (tensorShape.GetNumDimensions() <= dimensions)
109     {
110         return tensorShape;
111     }
112     std::vector<unsigned int> newShape;
113 
114     unsigned int dimsToSkip = tensorShape.GetNumDimensions() - dimensions;
115     unsigned int dimsSkipped = 0;
116     bool insertRemainder = false;
117 
118     for (unsigned int i = 0; i < tensorShape.GetNumDimensions(); ++i)
119     {
120         if (tensorShape[i] == 1 && dimsSkipped < dimsToSkip && !insertRemainder)
121         {
122             ++dimsSkipped;
123             continue;
124         }
125         newShape.push_back(tensorShape[i]);
126         // Once we insert the first dimension we can't skip any more
127         insertRemainder = true;
128     }
129     return TensorShape(static_cast<unsigned int>(newShape.size()), newShape.data());
130 }
131 
ReduceDims(const TensorInfo & tensorInfo,unsigned int dimensions)132 TensorInfo ReduceDims(const TensorInfo& tensorInfo, unsigned int dimensions)
133 {
134     TensorInfo strippedTensor(tensorInfo);
135     TensorShape strippedShape = ReduceDims(tensorInfo.GetShape(), dimensions);
136     strippedTensor.SetShape(strippedShape);
137     return strippedTensor;
138 }
139 
ExpandDims(const TensorShape & tensorShape,int axis)140 TensorShape ExpandDims(const TensorShape& tensorShape, int axis)
141 {
142     unsigned int outputDim = tensorShape.GetNumDimensions() + 1;
143 
144     if (axis < -armnn::numeric_cast<int>(outputDim) || axis > armnn::numeric_cast<int>(tensorShape.GetNumDimensions()))
145     {
146         throw InvalidArgumentException(fmt::format("Invalid expansion axis {} for {}D input tensor. {}",
147                                                    axis,
148                                                    tensorShape.GetNumDimensions(),
149                                                    CHECK_LOCATION().AsString()));
150     }
151 
152     if (axis < 0)
153     {
154         axis = armnn::numeric_cast<int>(outputDim) + axis;
155     }
156 
157     std::vector<unsigned int> outputShape;
158     outputShape.reserve(tensorShape.GetNumDimensions());
159     for (unsigned int i = 0; i < tensorShape.GetNumDimensions(); ++i)
160     {
161         outputShape.push_back(tensorShape[i]);
162     }
163     outputShape.insert(outputShape.begin() + axis, 1);
164 
165     return { outputDim, outputShape.data() };
166 }
167 
ExpandDimsToRank(const TensorShape & tensorShape,unsigned int rank)168 TensorShape ExpandDimsToRank(const TensorShape& tensorShape, unsigned int rank)
169 {
170     // Can't expand if rank is smaller than current shape
171     if (tensorShape.GetNumDimensions() >= rank)
172     {
173         return tensorShape;
174     }
175 
176     std::vector<unsigned int> newShape;
177 
178     // First add 1s to the beginning of the tensorInfo to fill in the space
179     for (unsigned int i = 0; i < rank - tensorShape.GetNumDimensions(); ++i)
180     {
181         newShape.push_back(1);
182     }
183 
184     // Then iterate through the original shape and append it to the new shape with the added 1s
185     for (unsigned int i = 0; i < tensorShape.GetNumDimensions(); ++i)
186     {
187         newShape.push_back(tensorShape[i]);
188     }
189 
190     return TensorShape(static_cast<unsigned int>(newShape.size()), newShape.data());
191 }
192 
SqueezeDims(const TensorShape & tensorShape)193 std::vector<unsigned int> SqueezeDims(const TensorShape& tensorShape)
194 {
195     std::vector<unsigned int> squeezedDims;
196 
197     for (unsigned int i = 0; i < tensorShape.GetNumDimensions(); ++i)
198     {
199         if (tensorShape[i] != 1)
200         {
201             squeezedDims.push_back(tensorShape[i]);
202         }
203     }
204     return squeezedDims;
205 }
206 
GetNumElementsBetween(const TensorShape & shape,const unsigned int firstAxisInclusive,const unsigned int lastAxisExclusive)207 unsigned int GetNumElementsBetween(const TensorShape& shape,
208                                    const unsigned int firstAxisInclusive,
209                                    const unsigned int lastAxisExclusive)
210 {
211     ARMNN_ASSERT(firstAxisInclusive <= lastAxisExclusive);
212     ARMNN_ASSERT(lastAxisExclusive <= shape.GetNumDimensions());
213     unsigned int count = 1;
214     for (unsigned int i = firstAxisInclusive; i < lastAxisExclusive; i++)
215     {
216         count *= shape[i];
217     }
218     return count;
219 }
220 
GetUnsignedAxis(const unsigned int inputDimension,const int axis)221 unsigned int GetUnsignedAxis(const unsigned int inputDimension, const int axis)
222 {
223     ARMNN_ASSERT_MSG(axis < armnn::numeric_cast<int>(inputDimension),
224                      "Required axis index greater than number of dimensions.");
225     ARMNN_ASSERT_MSG(axis >= -armnn::numeric_cast<int>(inputDimension),
226                      "Required axis index lower than negative of the number of dimensions");
227 
228     unsigned int uAxis = axis < 0  ?
229                          inputDimension - armnn::numeric_cast<unsigned int>(abs(axis))
230                          : armnn::numeric_cast<unsigned int>(axis);
231     return uAxis;
232 }
233 
GetNumElementsAfter(const armnn::TensorShape & shape,unsigned int axis)234 unsigned int GetNumElementsAfter(const armnn::TensorShape& shape, unsigned int axis)
235 {
236     unsigned int numDim = shape.GetNumDimensions();
237     ARMNN_ASSERT(axis <= numDim - 1);
238     unsigned int count = 1;
239     for (unsigned int i = axis+1; i < numDim; i++)
240     {
241         count *= shape[i];
242     }
243     return count;
244 }
245 
GetPerAxisParams(const armnn::TensorInfo & info)246 std::pair<unsigned int, std::vector<float>> GetPerAxisParams(const armnn::TensorInfo& info)
247 {
248     const std::vector<float>& scales = info.GetQuantizationScales();
249     armnn::Optional<unsigned int> quantizationDim = info.GetQuantizationDim();
250     if (!info.HasPerAxisQuantization())
251     {
252         throw armnn::InvalidArgumentException(
253             std::string("Per-axis quantization params not set for tensor of type ") +
254             armnn::GetDataTypeName(info.GetDataType()), CHECK_LOCATION());
255     }
256     unsigned int axisFactor = GetNumElementsAfter(info.GetShape(), quantizationDim.value()) ;
257 
258     return { axisFactor, scales };
259 }
260 
261 template<typename PrimitiveType>
CheckSizes(const std::vector<PrimitiveType> & data,const armnn::TensorInfo & tensorInfo,unsigned int size=1)262 void CheckSizes(const std::vector<PrimitiveType>& data, const armnn::TensorInfo& tensorInfo, unsigned int size = 1)
263 {
264     if (data.size() / size != tensorInfo.GetNumElements())
265     {
266         throw InvalidArgumentException(
267                 fmt::format("The data does not contain the expected number of elements {} != {}. {}",
268                             data.size(), tensorInfo.GetNumElements(), CHECK_LOCATION().AsString()));
269     }
270 }
271 
272 template<typename PrimitiveType>
ToFloatArray(const std::vector<PrimitiveType> & data,const armnn::TensorInfo & tensorInfo)273 std::unique_ptr<float[]> ToFloatArray(const std::vector<PrimitiveType>& data, const armnn::TensorInfo& tensorInfo)
274 {
275     CheckSizes(data, tensorInfo);
276 
277     std::unique_ptr<float[]> returnBuffer(new float[tensorInfo.GetNumElements()]);
278 
279     if (tensorInfo.HasPerAxisQuantization())
280     {
281         unsigned int axis = tensorInfo.GetQuantizationDim().value();
282         auto axisDimensionality = tensorInfo.GetShape()[axis];
283         auto axisFactor = armnnUtils::GetNumElementsAfter(tensorInfo.GetShape(), axis);
284 
285         for (unsigned int i = 0; i < tensorInfo.GetNumElements(); ++i)
286         {
287             unsigned int axisIndex;
288 
289             if (i < axisFactor)
290             {
291                 axisIndex = 0;
292             }
293             else
294             {
295                 axisIndex = (i / axisFactor) % axisDimensionality;
296             }
297             returnBuffer[i] = Dequantize<PrimitiveType>(data[i],
298                                                         tensorInfo.GetQuantizationScales()[axisIndex],
299                                                         tensorInfo.GetQuantizationOffset());
300         }
301     }
302     else
303     {
304         for (unsigned int i = 0; i < tensorInfo.GetNumElements(); ++i)
305         {
306             returnBuffer[i] = Dequantize<PrimitiveType>(data[i],
307                                                         tensorInfo.GetQuantizationScale(),
308                                                         tensorInfo.GetQuantizationOffset());
309         }
310     }
311     return returnBuffer;
312 }
313 
ToFloatArray(const std::vector<uint8_t> & data,const armnn::TensorInfo & tensorInfo)314 std::unique_ptr<float[]> ToFloatArray(const std::vector<uint8_t>& data, const armnn::TensorInfo& tensorInfo)
315 {
316     if (tensorInfo.GetDataType() == DataType::QAsymmS8 || tensorInfo.GetDataType() == DataType::QSymmS8)
317     {
318         CheckSizes(data, tensorInfo);
319         std::vector<int8_t> buffer(tensorInfo.GetNumElements());
320         ::memcpy(buffer.data(), data.data(), data.size());
321         return ToFloatArray<int8_t>(buffer, tensorInfo);
322     }
323     else if (tensorInfo.GetDataType() == DataType::QAsymmU8)
324     {
325         CheckSizes(data, tensorInfo);
326         return ToFloatArray<uint8_t>(data, tensorInfo);
327     }
328     else if (tensorInfo.GetDataType() == DataType::Signed32)
329     {
330         CheckSizes(data, tensorInfo, 4);
331         std::vector<int32_t> buffer(tensorInfo.GetNumElements());
332         ::memcpy(buffer.data(), data.data(), data.size());
333         return ToFloatArray<int32_t>(buffer, tensorInfo);
334     }
335     else if (tensorInfo.GetDataType() == DataType::Signed64)
336     {
337         CheckSizes(data, tensorInfo, 8);
338         std::vector<int64_t> buffer(tensorInfo.GetNumElements());
339         ::memcpy(buffer.data(), data.data(), data.size());
340         return ToFloatArray<int64_t>(buffer, tensorInfo);
341     }
342     throw InvalidArgumentException(
343             fmt::format("Unsupported datatype {}. {}",
344                         GetDataTypeName(tensorInfo.GetDataType()),
345                         CHECK_LOCATION().AsString()));
346 }
347 
348 } // namespace armnnUtils
349