Home | History | Annotate | Download | only in include
      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