1 /*
2  * Copyright (C) 2018 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 #define LOG_TAG "Operations"
18 
19 #include "CpuOperationUtils.h"
20 #include "HalInterfaces.h"
21 #include "IndexedShapeWrapper.h"
22 #include "OperationResolver.h"
23 
24 #include <vector>
25 
26 namespace android {
27 namespace nn {
28 namespace slice {
29 
30 constexpr char kOperationName[] = "SLICE";
31 
32 constexpr uint32_t kNumInputs = 3;
33 constexpr uint32_t kInputTensor = 0;
34 constexpr uint32_t kBeginTensor = 1;
35 constexpr uint32_t kSizeTensor = 2;
36 
37 constexpr uint32_t kNumOutputs = 1;
38 constexpr uint32_t kOutputTensor = 0;
39 
40 using namespace hal;
41 
42 namespace {
43 
44 template <typename T>
addVectors(const std::vector<T> & a,const std::vector<T> & b,std::vector<T> * res)45 void addVectors(const std::vector<T>& a, const std::vector<T>& b, std::vector<T>* res) {
46     for (int i = 0; i < res->size(); ++i) {
47         res->at(i) = a[i] + b[i];
48     }
49 }
50 
51 template <typename T>
evalGeneric(const T * inputData,const Shape & inputShape,const int32_t * beginData,const Shape & beginShape,const int32_t * sizeData,const Shape & sizeShape,T * outputData,const Shape & outputShape)52 bool evalGeneric(const T* inputData, const Shape& inputShape, const int32_t* beginData,
53                  const Shape& beginShape, const int32_t* sizeData, const Shape& sizeShape,
54                  T* outputData, const Shape& outputShape) {
55     const int outputSize = getNumberOfElements(outputShape);
56     const IndexedShapeWrapper indexedOutput = IndexedShapeWrapper(outputShape);
57     const IndexedShapeWrapper indexedInput = IndexedShapeWrapper(inputShape);
58     std::vector<uint32_t> outputIndex(getNumberOfDimensions(outputShape), 0);
59     std::vector<uint32_t> beginIndex(getSizeOfDimension(beginShape, 0));
60     std::vector<uint32_t> inputIndex(getNumberOfDimensions(inputShape));
61 
62     for (int i = 0; i < beginIndex.size(); ++i) {
63         beginIndex[i] = static_cast<uint32_t>(beginData[i]);
64     }
65 
66     bool lastIndex = false;
67     uint32_t outputOffset;
68     uint32_t inputOffset;
69 
70     do {
71         addVectors(outputIndex, beginIndex, &inputIndex);
72 
73         NN_RET_CHECK(indexedOutput.indexToFlatIndex(outputIndex, &outputOffset));
74         NN_RET_CHECK(indexedInput.indexToFlatIndex(inputIndex, &inputOffset));
75 
76         outputData[outputOffset] = inputData[inputOffset];
77         NN_RET_CHECK(indexedOutput.nextIndexInplace(&outputIndex, &lastIndex));
78     } while (!lastIndex);
79     return true;
80 }
81 
82 }  // namespace
83 
validate(const IOperationValidationContext * context)84 bool validate(const IOperationValidationContext* context) {
85     NN_RET_CHECK_EQ(context->getNumInputs(), kNumInputs);
86     NN_RET_CHECK_EQ(context->getNumOutputs(), kNumOutputs);
87 
88     const OperandType inputType = context->getInputType(kInputTensor);
89     NN_RET_CHECK(inputType == OperandType::TENSOR_FLOAT16 ||
90                  inputType == OperandType::TENSOR_FLOAT32 ||
91                  inputType == OperandType::TENSOR_INT32 ||
92                  inputType == OperandType::TENSOR_QUANT8_ASYMM ||
93                  inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED)
94             << "Unsupported tensor type for operation " << kOperationName;
95     if (inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) {
96         NN_RET_CHECK(validateHalVersion(context, HalVersion::V1_3));
97     } else {
98         NN_RET_CHECK(validateHalVersion(context, HalVersion::V1_2));
99     }
100     return validateInputTypes(context,
101                               {inputType, OperandType::TENSOR_INT32, OperandType::TENSOR_INT32}) &&
102            validateOutputTypes(context, {inputType});
103 }
104 
prepare(IOperationExecutionContext * context)105 bool prepare(IOperationExecutionContext* context) {
106     const Shape& inputShape = context->getInputShape(kInputTensor);
107     const int32_t n_dims = getNumberOfDimensions(inputShape);
108     NN_RET_CHECK(n_dims > 0);
109 
110     const Shape& beginShape = context->getInputShape(kBeginTensor);
111     NN_RET_CHECK_EQ(getNumberOfDimensions(beginShape), 1);
112     NN_RET_CHECK_EQ(getSizeOfDimension(beginShape, 0), n_dims);
113 
114     const Shape& sizeShape = context->getInputShape(kSizeTensor);
115     NN_RET_CHECK_EQ(getNumberOfDimensions(sizeShape), 1);
116     NN_RET_CHECK_EQ(getSizeOfDimension(sizeShape, 0), n_dims);
117 
118     const int32_t* beginData = context->getInputBuffer<int32_t>(kBeginTensor);
119     const int32_t* sizeData = context->getInputBuffer<int32_t>(kSizeTensor);
120 
121     Shape outputShape = context->getOutputShape(kOutputTensor);
122     outputShape.dimensions.resize(n_dims);
123     for (int i = 0; i < n_dims; ++i) {
124         const int32_t sliceBegin = beginData[i];
125         int32_t sliceSize = sizeData[i];
126         if (sliceSize == -1) {
127             sliceSize = getSizeOfDimension(inputShape, i) - sliceBegin;
128         }
129         NN_RET_CHECK_LE(beginData[i], getSizeOfDimension(inputShape, i));
130         NN_RET_CHECK_GE(sliceSize, 0);
131         NN_RET_CHECK_LE(sliceBegin + sliceSize, getSizeOfDimension(inputShape, i));
132         outputShape.dimensions[i] = sliceSize;
133     }
134     return context->setOutputShape(kOutputTensor, outputShape);
135 }
136 
execute(IOperationExecutionContext * context)137 bool execute(IOperationExecutionContext* context) {
138     // Bypass execution in the case of zero-sized input.
139     if (getNumberOfElements(context->getOutputShape(kOutputTensor)) == 0) return true;
140     switch (context->getInputType(kInputTensor)) {
141         case OperandType::TENSOR_FLOAT16:
142             return evalGeneric(context->getInputBuffer<_Float16>(kInputTensor),
143                                context->getInputShape(kInputTensor),
144                                context->getInputBuffer<int32_t>(kBeginTensor),
145                                context->getInputShape(kBeginTensor),
146                                context->getInputBuffer<int32_t>(kSizeTensor),
147                                context->getInputShape(kSizeTensor),
148                                context->getOutputBuffer<_Float16>(kOutputTensor),
149                                context->getOutputShape(kOutputTensor));
150         case OperandType::TENSOR_FLOAT32:
151             return evalGeneric(context->getInputBuffer<float>(kInputTensor),
152                                context->getInputShape(kInputTensor),
153                                context->getInputBuffer<int32_t>(kBeginTensor),
154                                context->getInputShape(kBeginTensor),
155                                context->getInputBuffer<int32_t>(kSizeTensor),
156                                context->getInputShape(kSizeTensor),
157                                context->getOutputBuffer<float>(kOutputTensor),
158                                context->getOutputShape(kOutputTensor));
159         case OperandType::TENSOR_INT32:
160             return evalGeneric(context->getInputBuffer<int32_t>(kInputTensor),
161                                context->getInputShape(kInputTensor),
162                                context->getInputBuffer<int32_t>(kBeginTensor),
163                                context->getInputShape(kBeginTensor),
164                                context->getInputBuffer<int32_t>(kSizeTensor),
165                                context->getInputShape(kSizeTensor),
166                                context->getOutputBuffer<int32_t>(kOutputTensor),
167                                context->getOutputShape(kOutputTensor));
168         case OperandType::TENSOR_QUANT8_ASYMM:
169             return evalGeneric(context->getInputBuffer<uint8_t>(kInputTensor),
170                                context->getInputShape(kInputTensor),
171                                context->getInputBuffer<int32_t>(kBeginTensor),
172                                context->getInputShape(kBeginTensor),
173                                context->getInputBuffer<int32_t>(kSizeTensor),
174                                context->getInputShape(kSizeTensor),
175                                context->getOutputBuffer<uint8_t>(kOutputTensor),
176                                context->getOutputShape(kOutputTensor));
177         case OperandType::TENSOR_QUANT8_ASYMM_SIGNED:
178             return evalGeneric(context->getInputBuffer<int8_t>(kInputTensor),
179                                context->getInputShape(kInputTensor),
180                                context->getInputBuffer<int32_t>(kBeginTensor),
181                                context->getInputShape(kBeginTensor),
182                                context->getInputBuffer<int32_t>(kSizeTensor),
183                                context->getInputShape(kSizeTensor),
184                                context->getOutputBuffer<int8_t>(kOutputTensor),
185                                context->getOutputShape(kOutputTensor));
186         default:
187             NN_RET_CHECK_FAIL() << "Unsupported tensor type for operation " << kOperationName;
188     }
189 }
190 
191 }  // namespace slice
192 
193 NN_REGISTER_OPERATION(SLICE, slice::kOperationName, slice::validate, slice::prepare, slice::execute,
194                       .allowZeroSizedInput = true);
195 
196 }  // namespace nn
197 }  // namespace android
198