Laurent Carlier | 749294b | 2020-06-01 09:03:17 +0100 | [diff] [blame] | 1 | // |
Teresa Charlin | a52bca2 | 2024-02-01 17:36:48 +0000 | [diff] [blame] | 2 | // Copyright © 2017-2024 Arm Ltd and Contributors. All rights reserved. |
David Beck | ecb56cd | 2018-09-05 12:52:57 +0100 | [diff] [blame] | 3 | // SPDX-License-Identifier: MIT |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 4 | // |
Aron Virginas-Tar | c9cc804 | 2018-11-01 16:15:57 +0000 | [diff] [blame] | 5 | #include <aclCommon/ArmComputeTensorUtils.hpp> |
| 6 | #include <aclCommon/ArmComputeUtils.hpp> |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 7 | |
Francis Murtagh | 351d13d | 2018-09-24 15:01:18 +0100 | [diff] [blame] | 8 | #include "armnn/Exceptions.hpp" |
Teresa Charlin | 6bc8525 | 2022-12-06 20:43:06 +0000 | [diff] [blame] | 9 | #include "ArmComputeUtils.hpp" |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 10 | #include <armnn/Descriptors.hpp> |
| 11 | |
Cathal Corbett | 4b19d22 | 2022-05-11 20:12:17 +0100 | [diff] [blame] | 12 | #include <fmt/format.h> |
| 13 | |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 14 | namespace armnn |
| 15 | { |
| 16 | namespace armcomputetensorutils |
| 17 | { |
| 18 | |
Derek Lamberti | d466a54 | 2020-01-22 15:37:29 +0000 | [diff] [blame] | 19 | arm_compute::DataType GetArmComputeDataType(armnn::DataType dataType, bool multiScales) |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 20 | { |
| 21 | switch(dataType) |
| 22 | { |
Narumol Prangnawarat | 250d392 | 2020-03-30 16:11:04 +0100 | [diff] [blame] | 23 | case armnn::DataType::BFloat16: |
| 24 | return arm_compute::DataType::BFLOAT16; |
Mike Kelly | 130ec60 | 2019-11-08 12:08:35 +0000 | [diff] [blame] | 25 | case armnn::DataType::Boolean: |
| 26 | return arm_compute::DataType::U8; |
telsoa01 | c577f2c | 2018-08-31 09:22:23 +0100 | [diff] [blame] | 27 | case armnn::DataType::Float16: |
| 28 | return arm_compute::DataType::F16; |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 29 | case armnn::DataType::Float32: |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 30 | return arm_compute::DataType::F32; |
Ryan OShea | 9add120 | 2020-02-07 10:06:33 +0000 | [diff] [blame] | 31 | case armnn::DataType::QAsymmS8: |
| 32 | return arm_compute::DataType::QASYMM8_SIGNED; |
Derek Lamberti | f90c56d | 2020-01-10 17:14:08 +0000 | [diff] [blame] | 33 | case armnn::DataType::QAsymmU8: |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 34 | return arm_compute::DataType::QASYMM8; |
Derek Lamberti | f90c56d | 2020-01-10 17:14:08 +0000 | [diff] [blame] | 35 | case armnn::DataType::QSymmS16: |
Aron Virginas-Tar | 7a3e2fe | 2019-06-27 18:54:47 +0100 | [diff] [blame] | 36 | return arm_compute::DataType::QSYMM16; |
Inki Dae | d4619e2 | 2020-09-10 15:33:54 +0900 | [diff] [blame] | 37 | case armnn::DataType::Signed64: |
| 38 | return arm_compute::DataType::S64; |
Finn Williams | fd27106 | 2019-12-04 14:27:27 +0000 | [diff] [blame] | 39 | case armnn::DataType::QSymmS8: |
Derek Lamberti | d466a54 | 2020-01-22 15:37:29 +0000 | [diff] [blame] | 40 | { |
| 41 | return multiScales ? arm_compute::DataType::QSYMM8_PER_CHANNEL : arm_compute::DataType::QSYMM8; |
| 42 | } |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 43 | case armnn::DataType::Signed32: |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 44 | return arm_compute::DataType::S32; |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 45 | default: |
Narumol Prangnawarat | ac2770a | 2020-04-01 16:51:23 +0100 | [diff] [blame] | 46 | ARMNN_ASSERT_MSG(false, "Unknown data type"); |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 47 | return arm_compute::DataType::UNKNOWN; |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 48 | } |
| 49 | } |
| 50 | |
Cathal Corbett | fd5bec4 | 2022-03-03 15:13:23 +0000 | [diff] [blame] | 51 | armnn::DataType GetArmNNDataType(arm_compute::DataType dataType) |
| 52 | { |
| 53 | switch(dataType) |
| 54 | { |
| 55 | case arm_compute::DataType::BFLOAT16: |
| 56 | return armnn::DataType::BFloat16; |
| 57 | case arm_compute::DataType::U8: |
| 58 | return armnn::DataType::Boolean; |
| 59 | case arm_compute::DataType::F16: |
| 60 | return armnn::DataType::Float16; |
| 61 | case arm_compute::DataType::F32: |
| 62 | return armnn::DataType::Float32; |
| 63 | case arm_compute::DataType::QASYMM8_SIGNED: |
| 64 | return armnn::DataType::QAsymmS8; |
| 65 | case arm_compute::DataType::QASYMM8: |
| 66 | return armnn::DataType::QAsymmU8; |
| 67 | case arm_compute::DataType::QSYMM16: |
| 68 | return armnn::DataType::QSymmS16; |
| 69 | case arm_compute::DataType::S64: |
| 70 | return armnn::DataType::Signed64; |
| 71 | case arm_compute::DataType::QSYMM8_PER_CHANNEL: |
| 72 | return armnn::DataType::QSymmS8; |
| 73 | case arm_compute::DataType::QSYMM8: |
| 74 | return armnn::DataType::QSymmS8; |
| 75 | case arm_compute::DataType::S32: |
| 76 | return armnn::DataType::Signed32; |
| 77 | default: |
| 78 | ARMNN_ASSERT_MSG(false, "Unknown data type"); |
| 79 | return armnn::DataType::Float32; |
| 80 | } |
| 81 | } |
| 82 | |
Matthew Bentham | fd89996 | 2018-12-31 15:49:42 +0000 | [diff] [blame] | 83 | arm_compute::Coordinates BuildArmComputeReductionCoordinates(size_t inputDimensions, |
| 84 | unsigned int originalInputRank, |
| 85 | const std::vector<unsigned int>& armnnAxes) |
| 86 | { |
| 87 | arm_compute::Coordinates outAclCoords; |
| 88 | |
| 89 | if (armnnAxes.empty()) |
| 90 | { |
| 91 | // If no reduction axes were provided, then the input must be reduced along all dimensions. |
| 92 | // Since Compute Library does not accept an empty vector as the reduction dimensions, we then |
| 93 | // manually create a vector including all the input dimensions (in reversed order) as: |
| 94 | // |
| 95 | // { inputDimensions - 1, inputDimensions - 2, ..., 1, 0 } |
| 96 | // |
| 97 | outAclCoords.set_num_dimensions(inputDimensions); |
| 98 | std::generate(outAclCoords.begin(), outAclCoords.end(), [d = inputDimensions - 1] () mutable { return d--; }); |
| 99 | } |
| 100 | else |
| 101 | { |
| 102 | // Create a vector of reduction dimensions (in reversed order) with the given reduction axes. |
| 103 | // |
| 104 | // Adjust the given reduction axes according to the original rank of the input tensor (before ACL applied any |
| 105 | // dimension correction). |
| 106 | // For example, if the input tensor originally had 4 dimensions, and one of the reduction axes was 2, then the |
| 107 | // new value for that reduction axis should be 1. |
| 108 | // |
| 109 | // Example: |
| 110 | // ArmNN input shape = { 1, 1, 3, 2 } -> ACL input shape = { 2, 3 } |
| 111 | // ArmNN reduction axis = { 2 } -> ACL reduction axis = { 1 } |
| 112 | // ArmNN reduction axis = { 3 } -> ACL reduction axis = { 0 } |
| 113 | // |
| 114 | // The transformation: ACL reduction axis index = original rank - ArmNN reduction axis index - 1 |
| 115 | // |
| 116 | outAclCoords.set_num_dimensions(armnnAxes.size()); |
| 117 | std::transform(armnnAxes.begin(), armnnAxes.end(), |
| 118 | outAclCoords.begin(), |
| 119 | [originalInputRank](unsigned int i){ return originalInputRank - i - 1; }); |
| 120 | } |
| 121 | |
| 122 | return outAclCoords; |
| 123 | } |
| 124 | |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 125 | arm_compute::TensorShape BuildArmComputeTensorShape(const armnn::TensorShape& tensorShape) |
| 126 | { |
| 127 | arm_compute::TensorShape shape; |
| 128 | |
telsoa01 | c577f2c | 2018-08-31 09:22:23 +0100 | [diff] [blame] | 129 | // armnn tensors are (batch, channels, height, width). |
| 130 | // arm_compute tensors are (width, height, channels, batch). |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 131 | for (unsigned int i = 0; i < tensorShape.GetNumDimensions(); i++) |
| 132 | { |
telsoa01 | c577f2c | 2018-08-31 09:22:23 +0100 | [diff] [blame] | 133 | // Note that our dimensions are stored in the opposite order to ACL's. |
Matthew Bentham | 8910528 | 2018-11-20 14:33:33 +0000 | [diff] [blame] | 134 | shape.set(tensorShape.GetNumDimensions() - i - 1, tensorShape[i], false); |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 135 | |
| 136 | // TensorShape::set() flattens leading ones, so that batch size 1 cannot happen. |
telsoa01 | c577f2c | 2018-08-31 09:22:23 +0100 | [diff] [blame] | 137 | // arm_compute tensors expect this. |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 138 | } |
| 139 | |
| 140 | // prevent arm_compute issue where tensor is flattened to nothing |
| 141 | if (shape.num_dimensions() == 0) |
| 142 | { |
| 143 | shape.set_num_dimensions(1); |
| 144 | } |
| 145 | |
| 146 | return shape; |
| 147 | } |
| 148 | |
Mike Kelly | 0e3fe10 | 2023-01-23 19:32:06 +0000 | [diff] [blame] | 149 | std::vector<unsigned int> ReduceDimsForACL(const armnn::TensorShape tensorShape, unsigned int dimensions) |
| 150 | { |
| 151 | std::vector<unsigned int> newShape; |
| 152 | |
| 153 | unsigned int dimsToSkip = 0; |
| 154 | |
| 155 | if (tensorShape.GetNumDimensions() > dimensions) |
| 156 | { |
| 157 | dimsToSkip = tensorShape.GetNumDimensions() - dimensions; |
| 158 | } |
| 159 | unsigned int dimsSkipped = 0; |
| 160 | bool insertRemainder = false; |
| 161 | |
| 162 | for (unsigned int i = 0; i < tensorShape.GetNumDimensions(); ++i) |
| 163 | { |
| 164 | if (tensorShape[i] == 1 && dimsSkipped < dimsToSkip && !insertRemainder) |
| 165 | { |
| 166 | ++dimsSkipped; |
| 167 | continue; |
| 168 | } |
| 169 | newShape.insert(newShape.begin(), tensorShape[i]); |
| 170 | // Once we insert the first dimension we can't skip any more |
| 171 | insertRemainder = true; |
| 172 | } |
| 173 | return newShape; |
| 174 | } |
| 175 | |
| 176 | arm_compute::TensorShape BuildArmComputeTensorShape(const armnn::TensorShape& tensorShape, unsigned int dimensions) |
| 177 | { |
| 178 | arm_compute::TensorShape shape; |
| 179 | std::vector<unsigned int> strippedShape = ReduceDimsForACL(tensorShape, dimensions); |
| 180 | |
| 181 | for (unsigned int i = 0; i < strippedShape.size(); i++) |
| 182 | { |
| 183 | shape.set(i, strippedShape[i], false); |
| 184 | } |
| 185 | |
| 186 | // prevent arm_compute issue where tensor is flattened to nothing |
| 187 | if (shape.num_dimensions() == 0) |
| 188 | { |
| 189 | shape.set_num_dimensions(1); |
| 190 | } |
| 191 | return shape; |
| 192 | } |
| 193 | |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 194 | // Utility function used to build a TensorInfo object, that can be used to initialise |
| 195 | // ARM Compute Tensor and CLTensor allocators. |
Cathal Corbett | 4452baf | 2022-05-13 09:55:59 +0100 | [diff] [blame] | 196 | // Note: this utility ignores the value of armnn::TensorInfo.IsConstant(). ACL tensors |
| 197 | // default to constant but Arm NN ones default to non constant. In the cases where |
| 198 | // we expect ACL to treat a tensor as constant that value must be set after this |
| 199 | // utility has been called. |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 200 | arm_compute::TensorInfo BuildArmComputeTensorInfo(const armnn::TensorInfo& tensorInfo) |
| 201 | { |
Derek Lamberti | d466a54 | 2020-01-22 15:37:29 +0000 | [diff] [blame] | 202 | bool multiScales = tensorInfo.HasMultipleQuantizationScales(); |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 203 | const arm_compute::TensorShape aclTensorShape = BuildArmComputeTensorShape(tensorInfo.GetShape()); |
Derek Lamberti | d466a54 | 2020-01-22 15:37:29 +0000 | [diff] [blame] | 204 | const arm_compute::DataType aclDataType = GetArmComputeDataType(tensorInfo.GetDataType(), multiScales); |
Aron Virginas-Tar | 13b653f | 2019-11-01 11:40:39 +0000 | [diff] [blame] | 205 | |
Derek Lamberti | d466a54 | 2020-01-22 15:37:29 +0000 | [diff] [blame] | 206 | const arm_compute::QuantizationInfo aclQuantizationInfo = multiScales ? |
Aron Virginas-Tar | 13b653f | 2019-11-01 11:40:39 +0000 | [diff] [blame] | 207 | arm_compute::QuantizationInfo(tensorInfo.GetQuantizationScales()) : |
| 208 | arm_compute::QuantizationInfo(tensorInfo.GetQuantizationScale(), tensorInfo.GetQuantizationOffset()); |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 209 | |
| 210 | return arm_compute::TensorInfo(aclTensorShape, 1, aclDataType, aclQuantizationInfo); |
| 211 | } |
| 212 | |
Francis Murtagh | 351d13d | 2018-09-24 15:01:18 +0100 | [diff] [blame] | 213 | arm_compute::TensorInfo BuildArmComputeTensorInfo(const armnn::TensorInfo& tensorInfo, |
| 214 | armnn::DataLayout dataLayout) |
| 215 | { |
Aron Virginas-Tar | 13b653f | 2019-11-01 11:40:39 +0000 | [diff] [blame] | 216 | arm_compute::TensorInfo aclTensorInfo = BuildArmComputeTensorInfo(tensorInfo); |
| 217 | aclTensorInfo.set_data_layout(ConvertDataLayout(dataLayout)); |
Francis Murtagh | 351d13d | 2018-09-24 15:01:18 +0100 | [diff] [blame] | 218 | |
Aron Virginas-Tar | 13b653f | 2019-11-01 11:40:39 +0000 | [diff] [blame] | 219 | return aclTensorInfo; |
Francis Murtagh | 351d13d | 2018-09-24 15:01:18 +0100 | [diff] [blame] | 220 | } |
| 221 | |
Mike Kelly | 0e3fe10 | 2023-01-23 19:32:06 +0000 | [diff] [blame] | 222 | arm_compute::TensorInfo BuildArmComputeTensorInfo(const armnn::TensorInfo& tensorInfo, unsigned int dimensions) |
| 223 | { |
| 224 | bool multiScales = tensorInfo.HasMultipleQuantizationScales(); |
| 225 | const arm_compute::TensorShape aclTensorShape = BuildArmComputeTensorShape(tensorInfo.GetShape(), dimensions); |
| 226 | const arm_compute::DataType aclDataType = GetArmComputeDataType(tensorInfo.GetDataType(), multiScales); |
| 227 | |
| 228 | const arm_compute::QuantizationInfo aclQuantizationInfo = multiScales ? |
| 229 | arm_compute::QuantizationInfo(tensorInfo.GetQuantizationScales()) : |
| 230 | arm_compute::QuantizationInfo(tensorInfo.GetQuantizationScale(), tensorInfo.GetQuantizationOffset()); |
| 231 | |
| 232 | return arm_compute::TensorInfo(aclTensorShape, 1, aclDataType, aclQuantizationInfo); |
| 233 | } |
| 234 | arm_compute::TensorInfo BuildArmComputeTensorInfo(const armnn::TensorInfo& tensorInfo, |
| 235 | armnn::DataLayout dataLayout, unsigned int dimensions) |
| 236 | { |
| 237 | arm_compute::TensorInfo aclTensorInfo = BuildArmComputeTensorInfo(tensorInfo, dimensions); |
| 238 | aclTensorInfo.set_data_layout(ConvertDataLayout(dataLayout)); |
| 239 | |
| 240 | return aclTensorInfo; |
| 241 | } |
| 242 | |
| 243 | |
Matteo Martincigh | 747ef82 | 2018-12-18 09:26:39 +0000 | [diff] [blame] | 244 | arm_compute::DataLayout ConvertDataLayout(armnn::DataLayout dataLayout) |
| 245 | { |
| 246 | switch(dataLayout) |
| 247 | { |
| 248 | case armnn::DataLayout::NHWC : return arm_compute::DataLayout::NHWC; |
| 249 | |
| 250 | case armnn::DataLayout::NCHW : return arm_compute::DataLayout::NCHW; |
| 251 | |
Teresa Charlin | ec5f7d1 | 2021-10-22 17:15:00 +0100 | [diff] [blame] | 252 | case armnn::DataLayout::NDHWC : return arm_compute::DataLayout::NDHWC; |
| 253 | |
| 254 | case armnn::DataLayout::NCDHW : return arm_compute::DataLayout::NCDHW; |
| 255 | |
Matteo Martincigh | 747ef82 | 2018-12-18 09:26:39 +0000 | [diff] [blame] | 256 | default: throw InvalidArgumentException("Unknown armnn::DataLayout: [" + |
| 257 | std::to_string(static_cast<int>(dataLayout)) + "]"); |
| 258 | } |
| 259 | } |
| 260 | |
Sadik Armagan | a3600ba | 2019-10-10 10:43:20 +0100 | [diff] [blame] | 261 | arm_compute::PoolingLayerInfo BuildArmComputePoolingLayerInfo(const Pooling2dDescriptor& descriptor, |
| 262 | bool fpMixedPrecision) |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 263 | { |
telsoa01 | c577f2c | 2018-08-31 09:22:23 +0100 | [diff] [blame] | 264 | // Resolve ARM Compute layer parameters. |
Ryan OShea | bab8fa9 | 2022-03-09 10:29:02 +0000 | [diff] [blame] | 265 | const arm_compute::PoolingType poolingType = ConvertPoolingAlgorithmToAclPoolingType(descriptor.m_PoolType); |
telsoa01 | c577f2c | 2018-08-31 09:22:23 +0100 | [diff] [blame] | 266 | |
Ryan OShea | bab8fa9 | 2022-03-09 10:29:02 +0000 | [diff] [blame] | 267 | const arm_compute::DataLayout dataLayout = ConvertDataLayout(descriptor.m_DataLayout); |
Teresa Charlin | c809a29 | 2020-01-31 10:21:44 +0000 | [diff] [blame] | 268 | |
telsoa01 | c577f2c | 2018-08-31 09:22:23 +0100 | [diff] [blame] | 269 | bool isGlobalPooling = (descriptor.m_StrideX==0 && descriptor.m_StrideY==0); |
| 270 | //use specific constructor if global pooling |
| 271 | if(isGlobalPooling) |
| 272 | { |
Teresa Charlin | c809a29 | 2020-01-31 10:21:44 +0000 | [diff] [blame] | 273 | return arm_compute::PoolingLayerInfo(poolingType, dataLayout); |
telsoa01 | c577f2c | 2018-08-31 09:22:23 +0100 | [diff] [blame] | 274 | } |
| 275 | |
Ryan OShea | bab8fa9 | 2022-03-09 10:29:02 +0000 | [diff] [blame] | 276 | const arm_compute::DimensionRoundingType rounding = ConvertOutputShapeRoundingToAclDimensionRoundingType( |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 277 | descriptor.m_OutputShapeRounding); |
Ryan OShea | bab8fa9 | 2022-03-09 10:29:02 +0000 | [diff] [blame] | 278 | const arm_compute::PadStrideInfo padStrideInfo(descriptor.m_StrideX, |
Teresa Charlin | a52bca2 | 2024-02-01 17:36:48 +0000 | [diff] [blame] | 279 | descriptor.m_StrideY, |
| 280 | descriptor.m_PadLeft, |
| 281 | descriptor.m_PadRight, |
| 282 | descriptor.m_PadTop, |
| 283 | descriptor.m_PadBottom, |
| 284 | rounding); |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 285 | |
| 286 | const bool excludePadding = (descriptor.m_PaddingMethod == PaddingMethod::Exclude); |
| 287 | |
Ryan OShea | bab8fa9 | 2022-03-09 10:29:02 +0000 | [diff] [blame] | 288 | const arm_compute::Size2D poolSize(descriptor.m_PoolWidth, descriptor.m_PoolHeight); |
surmeh01 | bceff2f | 2018-03-29 16:29:27 +0100 | [diff] [blame] | 289 | |
Teresa Charlin | c809a29 | 2020-01-31 10:21:44 +0000 | [diff] [blame] | 290 | return arm_compute::PoolingLayerInfo(poolingType, poolSize, dataLayout, padStrideInfo, excludePadding, |
| 291 | fpMixedPrecision); |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 292 | } |
| 293 | |
Ryan OShea | bab8fa9 | 2022-03-09 10:29:02 +0000 | [diff] [blame] | 294 | arm_compute::Pooling3dLayerInfo BuildArmComputePooling3dLayerInfo(const Pooling3dDescriptor& descriptor, |
| 295 | bool fpMixedPrecision) |
| 296 | { |
| 297 | const arm_compute::PoolingType poolingType = ConvertPoolingAlgorithmToAclPoolingType(descriptor.m_PoolType); |
| 298 | |
| 299 | bool isGlobalPooling = (descriptor.m_StrideX==0 && descriptor.m_StrideY==0 && descriptor.m_StrideZ==0); |
| 300 | //use specific constructor if global pooling |
| 301 | if(isGlobalPooling) |
| 302 | { |
| 303 | return arm_compute::Pooling3dLayerInfo(poolingType); |
| 304 | } |
| 305 | |
| 306 | const arm_compute::Size3D poolSize(descriptor.m_PoolWidth, descriptor.m_PoolHeight, descriptor.m_PoolDepth); |
| 307 | |
| 308 | const arm_compute::Size3D stride(descriptor.m_StrideX, |
| 309 | descriptor.m_StrideY, |
| 310 | descriptor.m_StrideZ); |
| 311 | |
| 312 | const arm_compute::Padding3D padding(descriptor.m_PadLeft, |
| 313 | descriptor.m_PadRight, |
| 314 | descriptor.m_PadTop, |
| 315 | descriptor.m_PadBottom, |
| 316 | descriptor.m_PadFront, |
| 317 | descriptor.m_PadBack); |
| 318 | |
| 319 | const bool excludePadding = (descriptor.m_PaddingMethod == PaddingMethod::Exclude); |
| 320 | |
| 321 | const arm_compute::DimensionRoundingType rounding = ConvertOutputShapeRoundingToAclDimensionRoundingType( |
| 322 | descriptor.m_OutputShapeRounding); |
| 323 | |
| 324 | return arm_compute::Pooling3dLayerInfo(poolingType, |
| 325 | poolSize, |
| 326 | stride, |
| 327 | padding, |
| 328 | excludePadding, |
| 329 | fpMixedPrecision, |
| 330 | rounding); |
| 331 | } |
| 332 | |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 333 | arm_compute::NormalizationLayerInfo BuildArmComputeNormalizationLayerInfo(const NormalizationDescriptor& descriptor) |
| 334 | { |
| 335 | const arm_compute::NormType normType = |
| 336 | ConvertNormalizationAlgorithmChannelToAclNormType(descriptor.m_NormChannelType); |
| 337 | return arm_compute::NormalizationLayerInfo(normType, |
| 338 | descriptor.m_NormSize, |
| 339 | descriptor.m_Alpha, |
| 340 | descriptor.m_Beta, |
| 341 | descriptor.m_K, |
| 342 | false); |
| 343 | } |
| 344 | |
| 345 | arm_compute::PermutationVector BuildArmComputePermutationVector(const armnn::PermutationVector& perm) |
| 346 | { |
| 347 | arm_compute::PermutationVector aclPerm; |
| 348 | |
| 349 | unsigned int start = 0; |
surmeh01 | bceff2f | 2018-03-29 16:29:27 +0100 | [diff] [blame] | 350 | while ((start < perm.GetSize()) && (start == perm[start])) |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 351 | { |
| 352 | ++start; |
| 353 | } |
| 354 | |
| 355 | for (unsigned int i = start; i < perm.GetSize(); ++i) |
| 356 | { |
| 357 | aclPerm.set(i - start, perm[i] - start); |
| 358 | } |
Mike Kelly | c9ea45a | 2020-02-28 18:11:58 +0000 | [diff] [blame] | 359 | return aclPerm; |
| 360 | } |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 361 | |
Mike Kelly | c9ea45a | 2020-02-28 18:11:58 +0000 | [diff] [blame] | 362 | arm_compute::PermutationVector BuildArmComputeTransposeVector(const armnn::PermutationVector& perm) |
| 363 | { |
Teresa Charlin | 6bc8525 | 2022-12-06 20:43:06 +0000 | [diff] [blame] | 364 | // As ArmNN indexes are left to right and ACL indexes are right to left, |
| 365 | // the permutation vector has to be reversed and then translated into ACL axis. |
| 366 | // i.e. {1, 0, 2, 3} --> {3, 2, 0, 1} --> {0, 1, 3, 2} |
| 367 | |
| 368 | // Below an example of how the ArmNN and ACL index format work: |
| 369 | // ArmNN Format: |
| 370 | // Input Shape {1, 10, 20, 30} |
| 371 | // Permutation Vector {1, 0, 2, 3} |
| 372 | // Output Shape {10, 1, 20, 30} |
| 373 | // dim "1" of input goes into index 0 of the output ([ 10, X, X, X]) |
| 374 | // dim "0" of input goes into index 1 of the output ([ 10, 1, X, X ]) |
| 375 | // dim "2" of input goes into index 2 of the output ([ 10, 1, 20, X ]) |
| 376 | // dim "3" of input goes into index 3 of the output ([ 10, 1, 20, 30 ]) |
| 377 | // ACL Format: |
| 378 | // Input Shape {30, 20, 10, 1} |
| 379 | // Permutation Vector {0, 1, 3, 2} |
| 380 | // Output Shape {30, 20, 1, 10} |
| 381 | // dim "0" of input goes into index 0 of the output ([ 30, X, X, X]) |
| 382 | // dim "1" of input goes into index 1 of the output ([ 30, 20, X, X ]) |
| 383 | // dim "3" of input goes into index 2 of the output ([ 30, 20, 1, X ]) |
| 384 | // dim "2" of input goes into index 3 of the output ([ 30, 20, 1, 10 ]) |
| 385 | |
Mike Kelly | c9ea45a | 2020-02-28 18:11:58 +0000 | [diff] [blame] | 386 | arm_compute::PermutationVector aclPerm; |
Teresa Charlin | 6bc8525 | 2022-12-06 20:43:06 +0000 | [diff] [blame] | 387 | auto rank = perm.GetSize(); |
| 388 | |
| 389 | // Reverse the order. i.e. {1, 0, 2, 3} --> {3, 2, 0, 1} |
| 390 | std::vector<unsigned int> reversedPerm; |
| 391 | reversedPerm.reserve(rank); |
| 392 | for (unsigned int i = rank; i > 0; --i) |
Mike Kelly | c9ea45a | 2020-02-28 18:11:58 +0000 | [diff] [blame] | 393 | { |
Teresa Charlin | 6bc8525 | 2022-12-06 20:43:06 +0000 | [diff] [blame] | 394 | reversedPerm.push_back(perm[i-1]); |
Mike Kelly | c9ea45a | 2020-02-28 18:11:58 +0000 | [diff] [blame] | 395 | } |
| 396 | |
Teresa Charlin | 6bc8525 | 2022-12-06 20:43:06 +0000 | [diff] [blame] | 397 | // Translate from Arm NN axis to ACL axis. i.e. {3, 2, 0, 1} --> {0, 1, 3, 2} |
| 398 | for (unsigned int i = 0; i < rank; ++i) |
Mike Kelly | c9ea45a | 2020-02-28 18:11:58 +0000 | [diff] [blame] | 399 | { |
Teresa Charlin | 6bc8525 | 2022-12-06 20:43:06 +0000 | [diff] [blame] | 400 | auto aclAxis = rank - 1 - reversedPerm[i]; |
| 401 | aclPerm.set(i, aclAxis); |
Mike Kelly | c9ea45a | 2020-02-28 18:11:58 +0000 | [diff] [blame] | 402 | } |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 403 | return aclPerm; |
| 404 | } |
| 405 | |
Sadik Armagan | f446432 | 2018-12-20 16:19:12 +0000 | [diff] [blame] | 406 | arm_compute::Size2D BuildArmComputeSize2D(const unsigned int width, const unsigned int height) |
| 407 | { |
| 408 | return arm_compute::Size2D(width, height); |
| 409 | } |
| 410 | |
Kevin May | 263d709 | 2022-11-29 14:34:48 +0000 | [diff] [blame] | 411 | arm_compute::PixelValue GetPixelValue(const arm_compute::ITensorInfo* tensorInfo, float value) |
Mike Kelly | 0a08ec6 | 2019-07-25 08:39:31 +0100 | [diff] [blame] | 412 | { |
Matthew Sloyan | 2e5d0b2 | 2021-10-21 14:05:31 +0100 | [diff] [blame] | 413 | switch (tensorInfo->data_type()) |
Mike Kelly | 0a08ec6 | 2019-07-25 08:39:31 +0100 | [diff] [blame] | 414 | { |
Mike Kelly | 0a08ec6 | 2019-07-25 08:39:31 +0100 | [diff] [blame] | 415 | case arm_compute::DataType::F16: |
Kevin May | 263d709 | 2022-11-29 14:34:48 +0000 | [diff] [blame] | 416 | { |
| 417 | arm_compute::PixelValue pixelValue = arm_compute::PixelValue(static_cast<Half>(value)); |
| 418 | if (isinf(pixelValue.get<Half>())) { |
| 419 | throw InvalidArgumentException("Under/Overflow converting float value [" + std::to_string(value) + |
| 420 | "] to fp16: [" + std::to_string(pixelValue.get<Half>()) + "]"); |
| 421 | } |
| 422 | return pixelValue; |
| 423 | } |
Mike Kelly | 0a08ec6 | 2019-07-25 08:39:31 +0100 | [diff] [blame] | 424 | case arm_compute::DataType::F32: |
Kevin May | 263d709 | 2022-11-29 14:34:48 +0000 | [diff] [blame] | 425 | return arm_compute::PixelValue(value); |
Mike Kelly | 130ec60 | 2019-11-08 12:08:35 +0000 | [diff] [blame] | 426 | case arm_compute::DataType::QASYMM8: |
Kevin May | 263d709 | 2022-11-29 14:34:48 +0000 | [diff] [blame] | 427 | return arm_compute::PixelValue(static_cast<uint8_t>(value)); |
Mike Kelly | 130ec60 | 2019-11-08 12:08:35 +0000 | [diff] [blame] | 428 | case arm_compute::DataType::QSYMM16: |
Kevin May | 263d709 | 2022-11-29 14:34:48 +0000 | [diff] [blame] | 429 | return arm_compute::PixelValue(static_cast<int16_t>(value)); |
Tamas Nyiri | d3065d7 | 2021-11-12 11:22:50 +0000 | [diff] [blame] | 430 | case arm_compute::DataType::QSYMM8: |
Sadik Armagan | e5d0b93 | 2020-04-09 15:48:44 +0100 | [diff] [blame] | 431 | case arm_compute::DataType::QASYMM8_SIGNED: |
Mike Kelly | 130ec60 | 2019-11-08 12:08:35 +0000 | [diff] [blame] | 432 | case arm_compute::DataType::QSYMM8_PER_CHANNEL: |
Kevin May | 263d709 | 2022-11-29 14:34:48 +0000 | [diff] [blame] | 433 | return arm_compute::PixelValue(static_cast<int8_t>(value)); |
Sadik Armagan | a792a05 | 2020-06-23 16:22:23 +0100 | [diff] [blame] | 434 | case arm_compute::DataType::S32: |
Kevin May | 263d709 | 2022-11-29 14:34:48 +0000 | [diff] [blame] | 435 | return arm_compute::PixelValue(static_cast<int32_t>(value)); |
Mike Kelly | 0a08ec6 | 2019-07-25 08:39:31 +0100 | [diff] [blame] | 436 | default: |
| 437 | throw InvalidArgumentException("Unsupported DataType: [" + |
Matthew Sloyan | 2e5d0b2 | 2021-10-21 14:05:31 +0100 | [diff] [blame] | 438 | std::to_string(static_cast<int>(tensorInfo->data_type())) + "]"); |
Mike Kelly | 0a08ec6 | 2019-07-25 08:39:31 +0100 | [diff] [blame] | 439 | } |
| 440 | } |
| 441 | |
Cathal Corbett | 4b19d22 | 2022-05-11 20:12:17 +0100 | [diff] [blame] | 442 | unsigned int ComputeDepthwiseConv2dDepthMultiplier(armnn::DataLayout layout, |
| 443 | const arm_compute::TensorShape& weightsShape, |
| 444 | const arm_compute::TensorShape& inputShape) |
| 445 | { |
| 446 | unsigned int depthMultiplier; |
| 447 | if (layout == armnn::DataLayout::NHWC) |
| 448 | { |
| 449 | depthMultiplier = static_cast<uint32_t>(weightsShape[0]) / static_cast<uint32_t>(inputShape[0]); |
| 450 | } |
| 451 | else if (layout == armnn::DataLayout::NCHW) |
| 452 | { |
| 453 | depthMultiplier = static_cast<uint32_t>(weightsShape[2]) / static_cast<uint32_t>(inputShape[2]); |
| 454 | } |
| 455 | else |
| 456 | { |
| 457 | throw InvalidArgumentException(fmt::format("Unknown data layout for tensor conversion: {}", |
| 458 | GetDataLayoutName(layout))); |
| 459 | } |
| 460 | return depthMultiplier; |
| 461 | } |
| 462 | |
telsoa01 | 4fcda01 | 2018-03-09 14:13:49 +0000 | [diff] [blame] | 463 | } // namespace armcomputetensorutils |
| 464 | } // namespace armnn |