• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #include <aclCommon/ArmComputeTensorUtils.hpp>
6 #include <aclCommon/ArmComputeUtils.hpp>
7 
8 #include "armnn/Exceptions.hpp"
9 #include <armnn/Descriptors.hpp>
10 
11 namespace armnn
12 {
13 namespace armcomputetensorutils
14 {
15 
GetArmComputeDataType(armnn::DataType dataType,bool multiScales)16 arm_compute::DataType GetArmComputeDataType(armnn::DataType dataType, bool multiScales)
17 {
18     switch(dataType)
19     {
20         case armnn::DataType::BFloat16:
21             return arm_compute::DataType::BFLOAT16;
22         case armnn::DataType::Boolean:
23             return arm_compute::DataType::U8;
24         case armnn::DataType::Float16:
25             return arm_compute::DataType::F16;
26         case armnn::DataType::Float32:
27             return arm_compute::DataType::F32;
28         case armnn::DataType::QAsymmS8:
29             return arm_compute::DataType::QASYMM8_SIGNED;
30         case armnn::DataType::QAsymmU8:
31             return arm_compute::DataType::QASYMM8;
32         case armnn::DataType::QSymmS16:
33             return arm_compute::DataType::QSYMM16;
34         case armnn::DataType::Signed64:
35             return arm_compute::DataType::S64;
36         case armnn::DataType::QSymmS8:
37         {
38             return multiScales ? arm_compute::DataType::QSYMM8_PER_CHANNEL : arm_compute::DataType::QSYMM8;
39         }
40         ARMNN_NO_DEPRECATE_WARN_BEGIN
41         case armnn::DataType::QuantizedSymm8PerAxis:
42             return arm_compute::DataType::QSYMM8_PER_CHANNEL;
43         ARMNN_NO_DEPRECATE_WARN_END
44         case armnn::DataType::Signed32:
45             return arm_compute::DataType::S32;
46         default:
47             ARMNN_ASSERT_MSG(false, "Unknown data type");
48             return arm_compute::DataType::UNKNOWN;
49     }
50 }
51 
BuildArmComputeReductionCoordinates(size_t inputDimensions,unsigned int originalInputRank,const std::vector<unsigned int> & armnnAxes)52 arm_compute::Coordinates BuildArmComputeReductionCoordinates(size_t inputDimensions,
53                                                              unsigned int originalInputRank,
54                                                              const std::vector<unsigned int>& armnnAxes)
55 {
56     arm_compute::Coordinates outAclCoords;
57 
58     if (armnnAxes.empty())
59     {
60         // If no reduction axes were provided, then the input must be reduced along all dimensions.
61         // Since Compute Library does not accept an empty vector as the reduction dimensions, we then
62         // manually create a vector including all the input dimensions (in reversed order) as:
63         //
64         // { inputDimensions - 1, inputDimensions - 2, ..., 1, 0 }
65         //
66         outAclCoords.set_num_dimensions(inputDimensions);
67         std::generate(outAclCoords.begin(), outAclCoords.end(), [d = inputDimensions - 1] () mutable { return d--; });
68     }
69     else
70     {
71         // Create a vector of reduction dimensions (in reversed order) with the given reduction axes.
72         //
73         // Adjust the given reduction axes according to the original rank of the input tensor (before ACL applied any
74         // dimension correction).
75         // For example, if the input tensor originally had 4 dimensions, and one of the reduction axes was 2, then the
76         // new value for that reduction axis should be 1.
77         //
78         // Example:
79         // ArmNN input shape = { 1, 1, 3, 2 } -> ACL input shape = { 2, 3 }
80         // ArmNN reduction axis = { 2 }       -> ACL reduction axis = { 1 }
81         // ArmNN reduction axis = { 3 }       -> ACL reduction axis = { 0 }
82         //
83         // The transformation: ACL reduction axis index = original rank - ArmNN reduction axis index - 1
84         //
85         outAclCoords.set_num_dimensions(armnnAxes.size());
86         std::transform(armnnAxes.begin(), armnnAxes.end(),
87                        outAclCoords.begin(),
88                        [originalInputRank](unsigned int i){ return originalInputRank - i - 1; });
89     }
90 
91     return outAclCoords;
92 }
93 
BuildArmComputeTensorShape(const armnn::TensorShape & tensorShape)94 arm_compute::TensorShape BuildArmComputeTensorShape(const armnn::TensorShape& tensorShape)
95 {
96     arm_compute::TensorShape shape;
97 
98     // armnn tensors are (batch, channels, height, width).
99     // arm_compute tensors are (width, height, channels, batch).
100     for (unsigned int i = 0; i < tensorShape.GetNumDimensions(); i++)
101     {
102         // Note that our dimensions are stored in the opposite order to ACL's.
103         shape.set(tensorShape.GetNumDimensions() - i - 1, tensorShape[i], false);
104 
105         // TensorShape::set() flattens leading ones, so that batch size 1 cannot happen.
106         // arm_compute tensors expect this.
107     }
108 
109     // prevent arm_compute issue where tensor is flattened to nothing
110     if (shape.num_dimensions() == 0)
111     {
112         shape.set_num_dimensions(1);
113     }
114 
115     return shape;
116 }
117 
118 // Utility function used to build a TensorInfo object, that can be used to initialise
119 // ARM Compute Tensor and CLTensor allocators.
BuildArmComputeTensorInfo(const armnn::TensorInfo & tensorInfo)120 arm_compute::TensorInfo BuildArmComputeTensorInfo(const armnn::TensorInfo& tensorInfo)
121 {
122     bool multiScales = tensorInfo.HasMultipleQuantizationScales();
123     const arm_compute::TensorShape aclTensorShape = BuildArmComputeTensorShape(tensorInfo.GetShape());
124     const arm_compute::DataType aclDataType       = GetArmComputeDataType(tensorInfo.GetDataType(), multiScales);
125 
126     const arm_compute::QuantizationInfo aclQuantizationInfo = multiScales ?
127         arm_compute::QuantizationInfo(tensorInfo.GetQuantizationScales()) :
128         arm_compute::QuantizationInfo(tensorInfo.GetQuantizationScale(), tensorInfo.GetQuantizationOffset());
129 
130     return arm_compute::TensorInfo(aclTensorShape, 1, aclDataType, aclQuantizationInfo);
131 }
132 
BuildArmComputeTensorInfo(const armnn::TensorInfo & tensorInfo,armnn::DataLayout dataLayout)133 arm_compute::TensorInfo BuildArmComputeTensorInfo(const armnn::TensorInfo& tensorInfo,
134                                                   armnn::DataLayout dataLayout)
135 {
136     arm_compute::TensorInfo aclTensorInfo = BuildArmComputeTensorInfo(tensorInfo);
137     aclTensorInfo.set_data_layout(ConvertDataLayout(dataLayout));
138 
139     return aclTensorInfo;
140 }
141 
ConvertDataLayout(armnn::DataLayout dataLayout)142 arm_compute::DataLayout ConvertDataLayout(armnn::DataLayout dataLayout)
143 {
144     switch(dataLayout)
145     {
146         case armnn::DataLayout::NHWC : return arm_compute::DataLayout::NHWC;
147 
148         case armnn::DataLayout::NCHW : return arm_compute::DataLayout::NCHW;
149 
150         default: throw InvalidArgumentException("Unknown armnn::DataLayout: [" +
151                                                 std::to_string(static_cast<int>(dataLayout)) + "]");
152     }
153 }
154 
BuildArmComputePoolingLayerInfo(const Pooling2dDescriptor & descriptor,bool fpMixedPrecision)155 arm_compute::PoolingLayerInfo BuildArmComputePoolingLayerInfo(const Pooling2dDescriptor& descriptor,
156                                                               bool fpMixedPrecision)
157 {
158     using arm_compute::PoolingType;
159     using arm_compute::DimensionRoundingType;
160     using arm_compute::PadStrideInfo;
161     using arm_compute::PoolingLayerInfo;
162     using arm_compute::Size2D;
163     using arm_compute::DataLayout;
164 
165     // Resolve ARM Compute layer parameters.
166     const PoolingType poolingType = ConvertPoolingAlgorithmToAclPoolingType(descriptor.m_PoolType);
167 
168     const DataLayout dataLayout = ConvertDataLayout(descriptor.m_DataLayout);
169 
170     bool isGlobalPooling = (descriptor.m_StrideX==0 && descriptor.m_StrideY==0);
171     //use specific constructor if global pooling
172     if(isGlobalPooling)
173     {
174         return arm_compute::PoolingLayerInfo(poolingType, dataLayout);
175     }
176 
177     const DimensionRoundingType rounding = ConvertOutputShapeRoundingToAclDimensionRoundingType(
178                                                                                     descriptor.m_OutputShapeRounding);
179     const PadStrideInfo padStrideInfo(descriptor.m_StrideX,
180                                       descriptor.m_StrideY,
181                                       descriptor.m_PadLeft,
182                                       descriptor.m_PadRight,
183                                       descriptor.m_PadTop,
184                                       descriptor.m_PadBottom,
185                                       rounding);
186 
187     const bool excludePadding = (descriptor.m_PaddingMethod == PaddingMethod::Exclude);
188 
189     const Size2D poolSize(descriptor.m_PoolWidth, descriptor.m_PoolHeight);
190 
191     return arm_compute::PoolingLayerInfo(poolingType, poolSize, dataLayout, padStrideInfo, excludePadding,
192                                          fpMixedPrecision);
193 }
194 
BuildArmComputeNormalizationLayerInfo(const NormalizationDescriptor & descriptor)195 arm_compute::NormalizationLayerInfo BuildArmComputeNormalizationLayerInfo(const NormalizationDescriptor& descriptor)
196 {
197     const arm_compute::NormType normType =
198         ConvertNormalizationAlgorithmChannelToAclNormType(descriptor.m_NormChannelType);
199     return arm_compute::NormalizationLayerInfo(normType,
200                                                descriptor.m_NormSize,
201                                                descriptor.m_Alpha,
202                                                descriptor.m_Beta,
203                                                descriptor.m_K,
204                                                false);
205 }
206 
BuildArmComputePermutationVector(const armnn::PermutationVector & perm)207 arm_compute::PermutationVector BuildArmComputePermutationVector(const armnn::PermutationVector& perm)
208 {
209     arm_compute::PermutationVector aclPerm;
210 
211     unsigned int start = 0;
212     while ((start < perm.GetSize()) && (start == perm[start]))
213     {
214         ++start;
215     }
216 
217     for (unsigned int i = start; i < perm.GetSize(); ++i)
218     {
219         aclPerm.set(i - start, perm[i] - start);
220     }
221     return aclPerm;
222 }
223 
BuildArmComputeTransposeVector(const armnn::PermutationVector & perm)224 arm_compute::PermutationVector BuildArmComputeTransposeVector(const armnn::PermutationVector& perm)
225 {
226     arm_compute::PermutationVector aclPerm;
227     std::map<unsigned int, unsigned int> permuteMappings;
228     for (unsigned int i = 0; i < perm.GetSize(); ++i)
229     {
230         permuteMappings[perm[i]] = i;
231     }
232 
233     std::vector<unsigned int> permuteVector;
234     for (unsigned int i = 0; i < perm.GetSize(); ++i)
235     {
236         permuteVector.push_back(permuteMappings.at(i));
237     }
238 
239     unsigned int start = 0;
240     while ((start < perm.GetSize()) && (start == permuteVector[start]))
241     {
242         ++start;
243     }
244 
245     for (unsigned int i = start; i < perm.GetSize(); ++i)
246     {
247         aclPerm.set(i - start, permuteVector[i] - start);
248     }
249     return aclPerm;
250 }
251 
BuildArmComputeSize2D(const unsigned int width,const unsigned int height)252 arm_compute::Size2D BuildArmComputeSize2D(const unsigned int width, const unsigned int height)
253 {
254     return arm_compute::Size2D(width, height);
255 }
256 
GetPixelValue(arm_compute::ITensor & input,float pixelValue)257 arm_compute::PixelValue GetPixelValue(arm_compute::ITensor& input, float pixelValue)
258 {
259     switch (input.info()->data_type())
260     {
261         case arm_compute::DataType::F16:
262             return arm_compute::PixelValue(static_cast<Half>(pixelValue));
263         case arm_compute::DataType::F32:
264             return arm_compute::PixelValue(pixelValue);
265         case arm_compute::DataType::QASYMM8:
266             return arm_compute::PixelValue(static_cast<uint8_t>(pixelValue));
267         case arm_compute::DataType::QSYMM16:
268             return arm_compute::PixelValue(static_cast<int16_t>(pixelValue));
269         case arm_compute::DataType::QASYMM8_SIGNED:
270         case arm_compute::DataType::QSYMM8_PER_CHANNEL:
271             return arm_compute::PixelValue(static_cast<int8_t>(pixelValue));
272         case arm_compute::DataType::S32:
273             return arm_compute::PixelValue(static_cast<int32_t>(pixelValue));
274         default:
275             throw InvalidArgumentException("Unsupported DataType: [" +
276                                            std::to_string(static_cast<int>(input.info()->data_type())) + "]");
277     }
278 }
279 
280 } // namespace armcomputetensorutils
281 } // namespace armnn
282