1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef ANDROID_ML_NN_COMMON_OPERATIONS_UTILS_H 18 #define ANDROID_ML_NN_COMMON_OPERATIONS_UTILS_H 19 20 #include "Utils.h" 21 22 #include <cstdint> 23 #include <vector> 24 25 namespace android { 26 namespace nn { 27 28 // DEPRECATED. Use NN_RET_CHECK instead. 29 #define NN_CHECK(x) NN_RET_CHECK(x) 30 #define NN_OPS_CHECK(x) NN_RET_CHECK(x) 31 32 // DEPRECATED. Use NN_RET_CHECK_EQ instead. 33 #define NN_CHECK_EQ(x, y) NN_RET_CHECK_EQ(x, y) 34 35 // An 8-bit boolean type (sizeof(bool) is implementation-defined). 36 typedef uint8_t bool8; 37 38 enum PaddingScheme { 39 kPaddingUnknown = 0, 40 kPaddingSame = 1, 41 kPaddingValid = 2, 42 }; 43 44 // Stores operand type information. "Shape" is a historical name. 45 struct Shape { 46 OperandType type; 47 std::vector<uint32_t> dimensions; 48 float scale; 49 int32_t offset; 50 Operand::ExtraParams extraParams; 51 }; 52 53 // Provides information available during graph creation to validate an operation. 54 class IOperationValidationContext { 55 public: 56 virtual ~IOperationValidationContext() {} 57 58 // The HAL version of the environment in which the operation is to be 59 // executed. 60 // 61 // Operation validation logic needs to handle all HAL versions to support 62 // the following use cases (assume in these examples that the latest HAL 63 // version is V1_2): 64 // 1. Our runtime wants to distribute work to a driver implementing an older 65 // HAL version and calls, for example, 66 // compliantWithV1_0(const V1_2::Model&). 67 // 2. A driver implements an older HAL version and delegates model 68 // validation to, for example, validateModel(const V1_0::Model&). 69 // 70 // If getHalVersion() returns HalVersion::V1_0 and the operation 71 // is only supported since HalVersion::V1_1, validation will fail. 72 virtual HalVersion getHalVersion() const = 0; 73 74 virtual uint32_t getNumInputs() const = 0; 75 virtual OperandType getInputType(uint32_t index) const = 0; 76 virtual Shape getInputShape(uint32_t index) const = 0; 77 virtual const Operand::ExtraParams getInputExtraParams(uint32_t index) const = 0; 78 79 virtual uint32_t getNumOutputs() const = 0; 80 virtual OperandType getOutputType(uint32_t index) const = 0; 81 virtual Shape getOutputShape(uint32_t index) const = 0; 82 }; 83 84 // Provides inputs and outputs during operation execution. 85 class IOperationExecutionContext { 86 public: 87 virtual ~IOperationExecutionContext() {} 88 89 virtual uint32_t getNumInputs() const = 0; 90 virtual OperandType getInputType(uint32_t index) const = 0; 91 virtual Shape getInputShape(uint32_t index) const = 0; 92 virtual const void* getInputBuffer(uint32_t index) const = 0; 93 virtual const Operand::ExtraParams getInputExtraParams(uint32_t index) const = 0; 94 95 virtual uint32_t getNumOutputs() const = 0; 96 virtual OperandType getOutputType(uint32_t index) const = 0; 97 virtual Shape getOutputShape(uint32_t index) const = 0; 98 virtual void* getOutputBuffer(uint32_t index) = 0; 99 100 // Updates the output shape, allocating the buffer if necessary. 101 virtual bool setOutputShape(uint32_t index, const Shape& shape) = 0; 102 103 virtual bool isOmittedInput(uint32_t index) const = 0; 104 virtual bool isOmittedOutput(uint32_t index) const = 0; 105 106 template <typename T> 107 const T* getInputBuffer(uint32_t index) const { 108 return reinterpret_cast<const T*>(getInputBuffer(index)); 109 } 110 111 template <typename T> 112 T* getOutputBuffer(uint32_t index) { 113 return reinterpret_cast<T*>(getOutputBuffer(index)); 114 } 115 116 template <typename T> 117 T getInputValue(uint32_t index) const { 118 return getInputBuffer<T>(index)[0]; 119 } 120 }; 121 122 // Verifies that the number and types of operation inputs are as expected. 123 bool validateInputTypes(const IOperationValidationContext* context, 124 const std::vector<OperandType>& expectedTypes); 125 126 // Verifies that the number and types of operation outputs are as expected. 127 bool validateOutputTypes(const IOperationValidationContext* context, 128 const std::vector<OperandType>& expectedTypes); 129 130 // Verifies that the HAL version specified in the context is greater or equal 131 // than the minimal supported HAL version. 132 bool validateHalVersion(const IOperationValidationContext* context, 133 HalVersion minSupportedHalVersion); 134 135 // Verifies that the two shapes are the same. 136 bool SameShape(const Shape& in1, const Shape& in2); 137 138 // Sets out to the same shape as in. 139 bool SetShape(const Shape& in, Shape* out); 140 141 // Combine two tensor dimensions, both can have unspecified dimensions. 142 bool combineDimensions(const std::vector<uint32_t>& lhs, const std::vector<uint32_t>& rhs, 143 std::vector<uint32_t>* combined); 144 145 // Return the total number of elements, i.e. all the dimensions multiplied 146 // together. For a scalar, returns one. 147 uint32_t getNumberOfElements(const Shape& shape); 148 uint32_t getNumberOfElements(const Shape& shape, 149 size_t firstAxisInclusive, 150 size_t lastAxisExclusive); 151 152 uint32_t getNumberOfDimensions(const Shape& shape); 153 154 uint32_t getSizeOfDimension(const Shape& shape, uint32_t dimensionIdx); 155 156 // Converts an axis index from the range [-dims, dims) into the range [0, dims). 157 bool handleNegativeAxis(int32_t numberOfDimensions, int32_t* axis); 158 159 inline bool handleNegativeAxis(const Shape& shape, int32_t* axis) { 160 return handleNegativeAxis(getNumberOfDimensions(shape), axis); 161 } 162 163 inline int32_t computeOutSize(int32_t imageSize, int32_t filterSize, int32_t stride, 164 int32_t paddingHead, int32_t paddingTail) { 165 return (imageSize - filterSize + stride + paddingHead + paddingTail) / stride; 166 } 167 168 inline int32_t computeOutSize(int32_t imageSize, int32_t filterSize, int32_t stride, 169 int32_t dilationRate, int32_t paddingHead, int32_t paddingTail) { 170 int32_t effectiveFilterSize = ((filterSize - 1) * dilationRate + 1); 171 return (imageSize - effectiveFilterSize + stride + paddingHead + paddingTail) / stride; 172 } 173 174 inline int32_t computeOutSizeTransposeConv(int32_t imageSize, int32_t filterSize, int32_t stride, 175 int32_t paddingHead, int32_t paddingTail) { 176 return imageSize * stride + filterSize - stride - paddingHead - paddingTail; 177 } 178 179 __wur bool QuantizeMultiplier(double double_multiplier, int32_t* quantized_multiplier, int* shift); 180 181 __wur 182 bool QuantizeMultiplierSmallerThanOne(double double_multiplier, 183 int32_t* quantized_multiplier, 184 int32_t* right_shift); 185 186 __wur 187 bool QuantizeMultiplierGreaterThanOne(double double_multiplier, 188 int32_t* quantized_multiplier, 189 int* left_shift); 190 191 __wur bool GetQuantizedConvolutionMultipler(const Shape& inputShape, const Shape& filterShape, 192 const Shape& biasShape, const Shape& outputShape, 193 double* multiplier); 194 195 void CalculateActivationRangeUint8(int32_t activation, 196 const Shape& outputShape, 197 int32_t* act_min, 198 int32_t* act_max); 199 200 void CalculateActivationRangeFloat(int32_t activation, 201 float* activation_min, 202 float* activation_max); 203 204 int32_t CalculateInputRadius(int input_integer_bits, int input_left_shift); 205 206 void calculateExplicitPaddingImpl(int32_t in_size, int32_t stride, int32_t dilation_factor, 207 int32_t filter_size, int32_t padding_implicit, 208 bool isTransposeConv, int32_t* padding_head, 209 int32_t* padding_tail); 210 211 inline void calculateExplicitPadding(int32_t in_size, int32_t stride, int32_t dilation_factor, 212 int32_t filter_size, int32_t padding_implicit, 213 int32_t* padding_head, int32_t* padding_tail) { 214 calculateExplicitPaddingImpl(in_size, stride, dilation_factor, filter_size, padding_implicit, 215 /*isTransposeConv=*/false, padding_head, padding_tail); 216 } 217 218 inline void calculateExplicitPadding(int32_t in_size, int32_t stride, int32_t filter_size, 219 int32_t padding_implicit, int32_t* padding_head, 220 int32_t* padding_tail) { 221 calculateExplicitPadding(in_size, stride, 1, filter_size, padding_implicit, padding_head, 222 padding_tail); 223 } 224 225 inline void calculateExplicitPaddingTransposeConv(int32_t in_size, int32_t stride, 226 int32_t filter_size, int32_t padding_implicit, 227 int32_t* padding_head, int32_t* padding_tail) { 228 calculateExplicitPaddingImpl(in_size, stride, /*dilation_factor=*/1, filter_size, 229 padding_implicit, /*isTransposeConv=*/true, padding_head, 230 padding_tail); 231 } 232 233 inline PaddingScheme getPaddingScheme(int32_t inWidth, int32_t inHeight, 234 int32_t strideWidth, int32_t strideHeight, 235 int32_t filterWidth, int32_t filterHeight, 236 int32_t paddingLeft, int32_t paddingRight, 237 int32_t paddingTop, int32_t paddingBottom) { 238 if (paddingLeft == 0 && paddingRight == 0 && paddingTop == 0 && paddingBottom == 0) { 239 return kPaddingValid; 240 } 241 242 int32_t expectedPaddingLeft, expectedPaddingRight; 243 int32_t expectedPaddingTop, expectedPaddingBottom; 244 245 calculateExplicitPadding(inWidth, strideWidth, filterWidth, kPaddingSame, 246 &expectedPaddingLeft, &expectedPaddingRight); 247 calculateExplicitPadding(inHeight, strideHeight, filterHeight, kPaddingSame, 248 &expectedPaddingTop, &expectedPaddingBottom); 249 if (expectedPaddingLeft == paddingLeft && expectedPaddingRight == paddingRight && 250 expectedPaddingTop == paddingTop && expectedPaddingBottom == paddingBottom) { 251 return kPaddingSame; 252 } else { 253 return kPaddingUnknown; 254 } 255 } 256 257 // TODO: add more documentation from upstream. 258 // Reverse order of bits in the mask to match the expected order in kernel 259 inline int ReverseMaskBits(int mask, int num_dimensions) { 260 int out = 0; 261 for (int dim = 0; dim < num_dimensions; dim++) { 262 out <<= 1; 263 out += (mask & 1); 264 mask >>= 1; 265 } 266 return out; 267 } 268 269 // TODO: add more documentation from upstream. 270 inline int32_t PositiveRemainder(int32_t dividend, int32_t divisor) { 271 return (divisor + (dividend % divisor)) % divisor; 272 } 273 274 // TODO: add more documentation from upstream. 275 inline int32_t ClampedIndex(int32_t index, int dim, bool pos_stride) { 276 return pos_stride 277 ? (index >= dim ? dim 278 : PositiveRemainder( 279 std::min(std::max(index, -dim), dim), dim)) 280 : (index < -dim 281 ? -1 282 : PositiveRemainder( 283 std::min(std::max(index, -dim), dim - 1), dim)); 284 } 285 286 // Broadcasts input shape against one another and puts the result into output 287 // shape. Returns true on success and false on error. 288 bool calculateBroadcastedShape(const Shape& in1, const Shape& in2, Shape* out); 289 290 // Dequantizes a value and quantizes it back using new scale and offset. 291 uint8_t requantize(uint8_t value, const Shape& oldShape, const Shape& newShape); 292 293 // Preparation functions for the corresponding ops 294 bool floorPrepare(const Shape& input, Shape* output); 295 296 bool depthwiseConvPrepare(const Shape& input, const Shape& filter, const Shape& bias, 297 int32_t padding_left, int32_t padding_right, int32_t padding_top, 298 int32_t padding_bottom, int32_t stride_width, int32_t stride_height, 299 int32_t depth_multiplier, int32_t dilation_width_factor, 300 int32_t dilation_height_factor, Shape* output); 301 302 bool genericActivationPrepare(const Shape& input, Shape* output); 303 304 bool genericNormalizationPrepare(const Shape& input, Shape* output); 305 306 bool reshapePrepare(const Shape& input, 307 const int32_t* targetDims, 308 const int32_t targetDimsSize, 309 Shape* output); 310 311 bool depthToSpacePrepare(const Shape& input, 312 int32_t blockSize, 313 Shape* output); 314 315 bool spaceToDepthPrepare(const Shape& input, 316 int32_t blockSize, 317 Shape* output); 318 319 bool embeddingLookupPrepare(const Shape &valueShape, 320 const Shape &lookupShape, 321 Shape *outputShape); 322 323 bool hashtableLookupPrepare(const Shape &lookupShape, 324 const Shape &keyShape, 325 const Shape &valueShape, 326 Shape *outputShape, 327 Shape *hitShape); 328 329 bool padPrepare(const Shape& input, 330 const int32_t* paddingsData, 331 const Shape& paddingsShape, 332 Shape* output); 333 334 bool batchToSpacePrepare(const Shape& input, 335 const int32_t* blockSizeData, 336 const Shape& blockSizeShape, 337 Shape* output); 338 339 bool spaceToBatchPrepare(const Shape& input, 340 const int32_t* blockSizeData, 341 const Shape& blockSizeShape, 342 const int32_t* paddingsData, 343 const Shape& paddingsShape, 344 Shape* output); 345 346 bool squeezePrepare(const Shape& input, 347 const int32_t* squeezeDims, 348 const Shape& squeezeDimsShape, 349 Shape* output); 350 351 bool meanPrepare(const Shape& input, 352 const int32_t* axisData, 353 const Shape& axisShape, 354 bool keepDims, 355 Shape* output); 356 357 bool stridedSlicePrepare(const Shape& input, 358 const int32_t* beginData, const Shape& beginShape, 359 const int32_t* endData, const Shape& endShape, 360 const int32_t* stridesData, const Shape& stridesShape, 361 int32_t beginMask, int32_t endMask, int32_t shrinkAxisMask, 362 Shape* output); 363 364 bool argMinMaxPrepare(const Shape& input, int32_t axis, Shape* output); 365 366 bool splitPrepare(const Shape& input, int32_t axis, int32_t numOutputs, std::vector<Shape>* output); 367 368 bool groupedConvPrepare(const Shape& input, const Shape& filter, const Shape& bias, 369 int32_t padding_left, int32_t padding_right, int32_t padding_top, 370 int32_t padding_bottom, int32_t stride_width, int32_t stride_height, 371 int32_t numGroups, Shape* output); 372 373 // Transposes the first two dimensions. 374 template <typename T> 375 inline bool transposeFirstTwoDimensions(const T* buffer, const Shape& shape, T* transposedBuffer) { 376 const int numDims = getNumberOfDimensions(shape); 377 NN_RET_CHECK(numDims >= 2); 378 const int firstDim = getSizeOfDimension(shape, 0); 379 const int secondDim = getSizeOfDimension(shape, 1); 380 int blockSize = 1; 381 for (int i = 2; i < numDims; ++i) { 382 blockSize *= getSizeOfDimension(shape, i); 383 } 384 385 for (int i = 0; i < firstDim; ++i) { 386 for (int j = 0; j < secondDim; ++j) { 387 for (int k = 0; k < blockSize; ++k) { 388 transposedBuffer[(j * firstDim + i) * blockSize + k] = 389 buffer[(i * secondDim + j) * blockSize + k]; 390 } 391 } 392 } 393 return true; 394 } 395 396 inline bool transposeFirstTwoDimensions(const Shape& shape, Shape* transposedShape) { 397 NN_RET_CHECK(getNumberOfDimensions(shape) >= 2); 398 *transposedShape = shape; 399 transposedShape->dimensions[0] = shape.dimensions[1]; 400 transposedShape->dimensions[1] = shape.dimensions[0]; 401 return true; 402 } 403 404 // Given two 3-dimensional tensors, merge them into one 3-dimensional tensor 405 // at the third dimension. The merged tensor's third dimension size will be 406 // sum of that of the two inputs. 407 template <typename T> 408 inline bool mergeThirdDimension(const T* bufferA, const std::vector<uint32_t>& dimsA, 409 const T* bufferB, const std::vector<uint32_t>& dimsB, T* merged) { 410 NN_RET_CHECK_EQ(dimsA.size(), 3u); 411 NN_RET_CHECK_EQ(dimsB.size(), 3u); 412 413 NN_RET_CHECK_EQ(dimsA[0], dimsB[0]); 414 NN_RET_CHECK_EQ(dimsA[1], dimsB[1]); 415 416 for (unsigned int i = 0; i < dimsA[0]; ++i) { 417 for (unsigned int j = 0; j < dimsA[1]; ++j) { 418 for (unsigned int k = 0; k < dimsA[2]; ++k) { 419 merged[(i * dimsA[1] + j) * (dimsA[2] + dimsB[2]) + k] = 420 bufferA[(i * dimsA[1] + j) * dimsA[2] + k]; 421 } 422 for (unsigned int k = 0; k < dimsB[2]; ++k) { 423 merged[(i * dimsA[1] + j) * (dimsA[2] + dimsB[2]) + dimsA[2] + k] = 424 bufferB[(i * dimsB[1] + j) * dimsB[2] + k]; 425 } 426 } 427 } 428 return true; 429 } 430 431 } // namespace nn 432 } // namespace android 433 434 #endif // ANDROID_ML_NN_COMMON_OPERATIONS_UTILS_H 435