1 //
2 // Copyright © 2017 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
FindMinMax(ITensorHandle * tensorHandle)58 std::pair<float, float> FindMinMax(ITensorHandle* tensorHandle)
59 {
60 auto tensor_data = static_cast<const float *>(tensorHandle->Map(true));
61 auto tensor_size = tensorHandle->GetShape().GetNumElements();
62
63 // Set min/max initially to first value in tensor
64 float min = tensor_data[0];
65 float max = tensor_data[0];
66
67 // Loop over rest of tensor and update min/max if necessary
68 for (unsigned int val = 1; val < tensor_size; val++)
69 {
70 if (tensor_data[val] < min)
71 {
72 min = tensor_data[val];
73 }
74 else if (tensor_data[val] > max)
75 {
76 max = tensor_data[val];
77 }
78 }
79
80 tensorHandle->Unmap();
81
82 return std::make_pair(min, max);
83 }
84
ExpandDims(const TensorShape & tensorShape,int axis)85 TensorShape ExpandDims(const TensorShape& tensorShape, int axis)
86 {
87 unsigned int outputDim = tensorShape.GetNumDimensions() + 1;
88
89 if (axis < -armnn::numeric_cast<int>(outputDim) || axis > armnn::numeric_cast<int>(tensorShape.GetNumDimensions()))
90 {
91 throw InvalidArgumentException(fmt::format("Invalid expansion axis {} for {}D input tensor. {}",
92 axis,
93 tensorShape.GetNumDimensions(),
94 CHECK_LOCATION().AsString()));
95 }
96
97 if (axis < 0)
98 {
99 axis = armnn::numeric_cast<int>(outputDim) + axis;
100 }
101
102 std::vector<unsigned int> outputShape;
103 outputShape.reserve(tensorShape.GetNumDimensions());
104 for (unsigned int i = 0; i < tensorShape.GetNumDimensions(); ++i)
105 {
106 outputShape.push_back(tensorShape[i]);
107 }
108 outputShape.insert(outputShape.begin() + axis, 1);
109
110 return TensorShape(outputDim, outputShape.data());
111 }
112
GetNumElementsBetween(const TensorShape & shape,const unsigned int firstAxisInclusive,const unsigned int lastAxisExclusive)113 unsigned int GetNumElementsBetween(const TensorShape& shape,
114 const unsigned int firstAxisInclusive,
115 const unsigned int lastAxisExclusive)
116 {
117 ARMNN_ASSERT(firstAxisInclusive <= lastAxisExclusive);
118 ARMNN_ASSERT(lastAxisExclusive <= shape.GetNumDimensions());
119 unsigned int count = 1;
120 for (unsigned int i = firstAxisInclusive; i < lastAxisExclusive; i++)
121 {
122 count *= shape[i];
123 }
124 return count;
125 }
126
GetUnsignedAxis(const unsigned int inputDimension,const int axis)127 unsigned int GetUnsignedAxis(const unsigned int inputDimension, const int axis)
128 {
129 ARMNN_ASSERT_MSG(axis < armnn::numeric_cast<int>(inputDimension),
130 "Required axis index greater than number of dimensions.");
131 ARMNN_ASSERT_MSG(axis >= -armnn::numeric_cast<int>(inputDimension),
132 "Required axis index lower than negative of the number of dimensions");
133
134 unsigned int uAxis = axis < 0 ?
135 inputDimension - armnn::numeric_cast<unsigned int>(abs(axis))
136 : armnn::numeric_cast<unsigned int>(axis);
137 return uAxis;
138 }
139
GetNumElementsAfter(const armnn::TensorShape & shape,unsigned int axis)140 unsigned int GetNumElementsAfter(const armnn::TensorShape& shape, unsigned int axis)
141 {
142 unsigned int numDim = shape.GetNumDimensions();
143 ARMNN_ASSERT(axis <= numDim - 1);
144 unsigned int count = 1;
145 for (unsigned int i = axis; i < numDim; i++)
146 {
147 count *= shape[i];
148 }
149 return count;
150 }
151
GetPerAxisParams(const armnn::TensorInfo & info)152 std::pair<unsigned int, std::vector<float>> GetPerAxisParams(const armnn::TensorInfo& info)
153 {
154 const std::vector<float>& scales = info.GetQuantizationScales();
155 armnn::Optional<unsigned int> quantizationDim = info.GetQuantizationDim();
156 if (!info.HasPerAxisQuantization())
157 {
158 throw armnn::InvalidArgumentException(
159 std::string("Per-axis quantization params not set for tensor of type ") +
160 armnn::GetDataTypeName(info.GetDataType()), CHECK_LOCATION());
161 }
162 unsigned int axisFactor = GetNumElementsAfter(info.GetShape(), quantizationDim.value());
163
164 return { axisFactor, scales };
165 }
166
167 } // namespace armnnUtils
168