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 #define LOG_TAG "android.hardware.neuralnetworks@1.0-impl-hvx"
18 
19 #include "HexagonModel.h"
20 #include "HexagonOperations.h"
21 #include "OperationsUtils.h"
22 
23 namespace android {
24 namespace hardware {
25 namespace neuralnetworks {
26 namespace V1_0 {
27 namespace implementation {
28 namespace hexagon {
29 
30 using android::nn::Shape;
31 
32 namespace {
33 namespace float32 {
34 
add(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)35 bool add(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs, HexagonModel* model) {
36     HEXAGON_SOFT_ASSERT_EQ(3, ins.size(), "Need 3 inputs for float32::add");
37     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::add");
38 
39     // get parameters
40     const hexagon_nn_input& in1 = model->getTensor(ins[0]);
41     const hexagon_nn_input& in2 = model->getTensor(ins[1]);
42 
43     const op_type act = model->getFloatActivation(ins[2]);
44 
45     // add node to graph
46     return model->addFusedFloatOperation(OP_Add_f, NN_PAD_NA, {}, act, {in1, in2}, outs);
47 }
48 
average_pool_2d(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)49 bool average_pool_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
50                      HexagonModel* model) {
51     HEXAGON_SOFT_ASSERT(ins.size() == 10 || ins.size() == 7,
52                         "Need 7 or 10 inputs for float32::average_pool_2d");
53     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::average_pool_2d");
54 
55     // get parameters
56     const hexagon_nn_input& input = model->getTensor(ins[0]);
57 
58     // setup parameters
59     hexagon_nn_padding_type pad;
60     int32_t stride_width;
61     int32_t stride_height;
62     int32_t filter_width;
63     int32_t filter_height;
64     op_type act;
65 
66     // get parameters
67     if (ins.size() == 10) {
68         const int32_t padding_left = model->getScalar<int32_t>(ins[1]);
69         const int32_t padding_right = model->getScalar<int32_t>(ins[2]);
70         const int32_t padding_top = model->getScalar<int32_t>(ins[3]);
71         const int32_t padding_bottom = model->getScalar<int32_t>(ins[4]);
72         stride_width = model->getScalar<int32_t>(ins[5]);
73         stride_height = model->getScalar<int32_t>(ins[6]);
74         filter_width = model->getScalar<int32_t>(ins[7]);
75         filter_height = model->getScalar<int32_t>(ins[8]);
76         act = model->getFloatActivation(ins[9]);
77 
78         const Shape inputShape = model->getShape(ins[0]);
79         pad = getPadding(inputShape.dimensions[2], inputShape.dimensions[1], stride_width,
80                          stride_height, filter_width, filter_height, padding_left, padding_right,
81                          padding_top, padding_bottom);
82         HEXAGON_SOFT_ASSERT_NE(pad, NN_PAD_NA, "Unknown padding");
83     } else {
84         pad = model->getPadding(ins[1]);
85         stride_width = model->getScalar<int32_t>(ins[2]);
86         stride_height = model->getScalar<int32_t>(ins[3]);
87         filter_width = model->getScalar<int32_t>(ins[4]);
88         filter_height = model->getScalar<int32_t>(ins[5]);
89         act = model->getFloatActivation(ins[6]);
90     }
91 
92     const hexagon_nn_input window = model->createShape(1, filter_height, filter_width, 1);
93     const hexagon_nn_input stride = model->createShape(1, stride_height, stride_width, 1);
94 
95     // add node to graph
96     return model->addFloatOperationWithActivation(OP_AvgPool_f, pad, act, {input, window, stride},
97                                                   outs);
98 }
99 
concatenation(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)100 bool concatenation(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
101                    HexagonModel* model) {
102     HEXAGON_SOFT_ASSERT_LE(3, ins.size(), "Need at least 3 inputs for float32::concatenation");
103     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::concatenation");
104 
105     const size_t numInputTensors = ins.size() - 1;
106 
107     // get parameters
108     std::vector<hexagon_nn_input> inputs(numInputTensors + 1);
109     for (size_t i = 0; i < numInputTensors; ++i) {
110         inputs[i + 1] = model->getTensor(ins[i]);
111     }
112 
113     // axis being concatenated
114     const int32_t axis = model->getScalar<int32_t>(ins[numInputTensors]);
115     const int32_t dims = model->getShape(ins[0]).dimensions.size();
116     inputs[0] = model->createScalar<int32_t>(axis + (4 - dims));
117 
118     // add node to graph
119     return model->addBasicOperation(OP_Concat_f, NN_PAD_NA, inputs, outs);
120 }
121 
conv_2d(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)122 bool conv_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
123              HexagonModel* model) {
124     HEXAGON_SOFT_ASSERT(ins.size() == 10 || ins.size() == 7,
125                         "Need 7 or 10 inputs for float32::conv_2d");
126     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::conv_2d");
127 
128     // get parameters
129     const hexagon_nn_input& input = model->getTensor(ins[0]);
130     const hexagon_nn_input filter = model->createConvFilterTensor(ins[1]);
131     const hexagon_nn_input& bias = model->getTensor(ins[2]);
132 
133     // setup parameters
134     hexagon_nn_padding_type pad;
135     int32_t stride_width;
136     int32_t stride_height;
137     op_type act;
138 
139     // get parameters
140     if (ins.size() == 10) {
141         const int32_t padding_left = model->getScalar<int32_t>(ins[3]);
142         const int32_t padding_right = model->getScalar<int32_t>(ins[4]);
143         const int32_t padding_top = model->getScalar<int32_t>(ins[5]);
144         const int32_t padding_bottom = model->getScalar<int32_t>(ins[6]);
145         stride_width = model->getScalar<int32_t>(ins[7]);
146         stride_height = model->getScalar<int32_t>(ins[8]);
147         act = model->getFloatActivation(ins[9]);
148 
149         const Shape inputShape = model->getShape(ins[0]);
150         const Shape filterShape = model->getShape(ins[1]);
151         pad = getPadding(inputShape.dimensions[2], inputShape.dimensions[1], stride_width,
152                          stride_height, filterShape.dimensions[2], filterShape.dimensions[1],
153                          padding_left, padding_right, padding_top, padding_bottom);
154         HEXAGON_SOFT_ASSERT_NE(pad, NN_PAD_NA, "Unknown padding");
155     } else {
156         pad = model->getPadding(ins[3]);
157         stride_width = model->getScalar<int32_t>(ins[4]);
158         stride_height = model->getScalar<int32_t>(ins[5]);
159         act = model->getFloatActivation(ins[6]);
160     }
161 
162     const hexagon_nn_input stride = model->createShape(1, stride_height, stride_width, 1);
163 
164     // add node to graph
165     return model->addFusedFloatOperation(OP_Conv2d_f, pad, bias, act, {input, filter, stride},
166                                          outs);
167 }
168 
depthwise_conv_2d(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)169 bool depthwise_conv_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
170                        HexagonModel* model) {
171     HEXAGON_SOFT_ASSERT(ins.size() == 11 || ins.size() == 8,
172                         "Need 8 or 11 inputs for float32::depthwise_conv_2d");
173     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::depthwise_conv_2d");
174 
175     // get parameters
176     const hexagon_nn_input& input = model->getTensor(ins[0]);
177     const hexagon_nn_input& bias = model->getTensor(ins[2]);
178 
179     const Shape filterShape = model->getShape(ins[1]);
180 
181     // setup parameters
182     hexagon_nn_padding_type pad;
183     int32_t stride_width;
184     int32_t stride_height;
185     int32_t depth_multiplier;
186     op_type act;
187 
188     // get parameters
189     if (ins.size() == 11) {
190         const int32_t padding_left = model->getScalar<int32_t>(ins[3]);
191         const int32_t padding_right = model->getScalar<int32_t>(ins[4]);
192         const int32_t padding_top = model->getScalar<int32_t>(ins[5]);
193         const int32_t padding_bottom = model->getScalar<int32_t>(ins[6]);
194         stride_width = model->getScalar<int32_t>(ins[7]);
195         stride_height = model->getScalar<int32_t>(ins[8]);
196         depth_multiplier = model->getScalar<int32_t>(ins[9]);
197         act = model->getFloatActivation(ins[10]);
198 
199         const Shape inputShape = model->getShape(ins[0]);
200         const Shape filterShape = model->getShape(ins[1]);
201         pad = getPadding(inputShape.dimensions[2], inputShape.dimensions[1], stride_width,
202                          stride_height, filterShape.dimensions[2], filterShape.dimensions[1],
203                          padding_left, padding_right, padding_top, padding_bottom);
204         HEXAGON_SOFT_ASSERT_NE(pad, NN_PAD_NA, "Unknown padding");
205     } else {
206         pad = model->getPadding(ins[3]);
207         stride_width = model->getScalar<int32_t>(ins[4]);
208         stride_height = model->getScalar<int32_t>(ins[5]);
209         depth_multiplier = model->getScalar<int32_t>(ins[6]);
210         act = model->getFloatActivation(ins[7]);
211     }
212 
213     const hexagon_nn_input filter = model->createDepthwiseFilterTensor(ins[1], depth_multiplier);
214     const hexagon_nn_input stride = model->createShape(1, stride_height, stride_width, 1);
215 
216     // add node to graph
217     return model->addFusedFloatOperation(OP_DepthwiseConv2d_f, pad, bias, act,
218                                          {input, filter, stride}, outs);
219 }
220 
fully_connected(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)221 bool fully_connected(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
222                      HexagonModel* model) {
223     HEXAGON_SOFT_ASSERT_EQ(4, ins.size(), "Need 4 inputs for float32::fully_connected");
224     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::fully_connected");
225 
226     // get parameters
227     const hexagon_nn_input& input = model->getTensor(ins[0]);
228     const hexagon_nn_input& weights = model->createFullyConnectedWeightTensor(ins[1]);
229     const hexagon_nn_input& bias = model->getTensor(ins[2]);
230 
231     const op_type act = model->getFloatActivation(ins[3]);
232 
233     // add node to graph
234     return model->addFusedFloatOperation(OP_MatMul_f, NN_PAD_NA, bias, act, {input, weights}, outs);
235 }
236 
l2_pool_2d(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)237 bool l2_pool_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
238                 HexagonModel* model) {
239     HEXAGON_SOFT_ASSERT(ins.size() == 10 || ins.size() == 7,
240                         "Need 7 or 10 inputs for float32::l2_pool_2d");
241     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::l2_pool_2d");
242 
243     // get parameters
244     const hexagon_nn_input& input = model->getTensor(ins[0]);
245 
246     // setup parameters
247     hexagon_nn_padding_type pad;
248     int32_t stride_width;
249     int32_t stride_height;
250     int32_t filter_width;
251     int32_t filter_height;
252     op_type act;
253 
254     // get parameters
255     if (ins.size() == 10) {
256         const int32_t padding_left = model->getScalar<int32_t>(ins[1]);
257         const int32_t padding_right = model->getScalar<int32_t>(ins[2]);
258         const int32_t padding_top = model->getScalar<int32_t>(ins[3]);
259         const int32_t padding_bottom = model->getScalar<int32_t>(ins[4]);
260         stride_width = model->getScalar<int32_t>(ins[5]);
261         stride_height = model->getScalar<int32_t>(ins[6]);
262         filter_width = model->getScalar<int32_t>(ins[7]);
263         filter_height = model->getScalar<int32_t>(ins[8]);
264         act = model->getFloatActivation(ins[9]);
265 
266         const Shape inputShape = model->getShape(ins[0]);
267         pad = getPadding(inputShape.dimensions[2], inputShape.dimensions[1], stride_width,
268                          stride_height, filter_width, filter_height, padding_left, padding_right,
269                          padding_top, padding_bottom);
270         HEXAGON_SOFT_ASSERT_NE(pad, NN_PAD_NA, "Unknown padding");
271     } else {
272         pad = model->getPadding(ins[1]);
273         stride_width = model->getScalar<int32_t>(ins[2]);
274         stride_height = model->getScalar<int32_t>(ins[3]);
275         filter_width = model->getScalar<int32_t>(ins[4]);
276         filter_height = model->getScalar<int32_t>(ins[5]);
277         act = model->getFloatActivation(ins[6]);
278     }
279 
280     const hexagon_nn_input window = model->createShape(1, filter_height, filter_width, 1);
281     const hexagon_nn_input stride = model->createShape(1, stride_height, stride_width, 1);
282 
283     // add node to graph
284     return model->addFloatOperationWithActivation(OP_L2Pool_f, pad, act, {input, window, stride},
285                                                   outs);
286 }
287 
local_response_normalization(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)288 bool local_response_normalization(const std::vector<uint32_t>& ins,
289                                   const std::vector<uint32_t>& outs, HexagonModel* model) {
290     HEXAGON_SOFT_ASSERT_EQ(5, ins.size(),
291                            "Need 5 inputs for float32::local_response_normalization");
292     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(),
293                            "Need 1 output for float32::local_response_normalization");
294 
295     // get parameters
296     const hexagon_nn_input& input = model->getTensor(ins[0]);
297     const hexagon_nn_input& bias = model->getTensor(ins[2]);
298     const hexagon_nn_input& alpha = model->getTensor(ins[3]);
299     const hexagon_nn_input& beta = model->getTensor(ins[4]);
300 
301     // create value that's [1, 1, 1, radius] with value of 1.0f
302     const int32_t radius = model->getScalar<int32_t>(ins[1]);
303     const hexagon_nn_input window = model->createTensor<float>(1, 1, 1, radius * 2 + 1, {1.0f});
304 
305     // add node to graph
306     return model->addBasicOperation(OP_LRN_f, NN_PAD_NA, {input, window, bias, alpha, beta}, outs);
307 }
308 
logistic(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)309 bool logistic(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
310               HexagonModel* model) {
311     HEXAGON_SOFT_ASSERT_EQ(1, ins.size(), "Need 1 input for float32::logistic");
312     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::logistic");
313 
314     // get parameters
315     const hexagon_nn_input& input = model->getTensor(ins[0]);
316 
317     // add node to graph
318     return model->addBasicOperation(OP_Sigmoid_f, NN_PAD_NA, {input}, outs);
319 }
320 
max_pool_2d(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)321 bool max_pool_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
322                  HexagonModel* model) {
323     HEXAGON_SOFT_ASSERT(ins.size() == 10 || ins.size() == 7,
324                         "Need 7 or 10 inputs for float32::max_pool_2d");
325     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::max_pool_2d");
326 
327     // get parameters
328     const hexagon_nn_input& input = model->getTensor(ins[0]);
329 
330     // setup parameters
331     hexagon_nn_padding_type pad;
332     int32_t stride_width;
333     int32_t stride_height;
334     int32_t filter_width;
335     int32_t filter_height;
336     op_type act;
337 
338     // get parameters
339     if (ins.size() == 10) {
340         const int32_t padding_left = model->getScalar<int32_t>(ins[1]);
341         const int32_t padding_right = model->getScalar<int32_t>(ins[2]);
342         const int32_t padding_top = model->getScalar<int32_t>(ins[3]);
343         const int32_t padding_bottom = model->getScalar<int32_t>(ins[4]);
344         stride_width = model->getScalar<int32_t>(ins[5]);
345         stride_height = model->getScalar<int32_t>(ins[6]);
346         filter_width = model->getScalar<int32_t>(ins[7]);
347         filter_height = model->getScalar<int32_t>(ins[8]);
348         act = model->getFloatActivation(ins[9]);
349 
350         const Shape inputShape = model->getShape(ins[0]);
351         pad = getPadding(inputShape.dimensions[2], inputShape.dimensions[1], stride_width,
352                          stride_height, filter_width, filter_height, padding_left, padding_right,
353                          padding_top, padding_bottom);
354         HEXAGON_SOFT_ASSERT_NE(pad, NN_PAD_NA, "Unknown padding");
355     } else {
356         pad = model->getPadding(ins[1]);
357         stride_width = model->getScalar<int32_t>(ins[2]);
358         stride_height = model->getScalar<int32_t>(ins[3]);
359         filter_width = model->getScalar<int32_t>(ins[4]);
360         filter_height = model->getScalar<int32_t>(ins[5]);
361         act = model->getFloatActivation(ins[6]);
362     }
363 
364     const hexagon_nn_input window = model->createShape(1, filter_height, filter_width, 1);
365     const hexagon_nn_input stride = model->createShape(1, stride_height, stride_width, 1);
366 
367     // add node to graph
368     return model->addFloatOperationWithActivation(OP_MaxPool_f, pad, act, {input, window, stride},
369                                                   outs);
370 }
371 
mul(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)372 bool mul(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs, HexagonModel* model) {
373     HEXAGON_SOFT_ASSERT_EQ(3, ins.size(), "Need 3 inputs for float32::mul");
374     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::mul");
375 
376     // get parameters
377     const hexagon_nn_input& in1 = model->getTensor(ins[0]);
378     const hexagon_nn_input& in2 = model->getTensor(ins[1]);
379 
380     const op_type act = model->getFloatActivation(ins[2]);
381 
382     // add node to graph
383     return model->addFusedFloatOperation(OP_Mul_f, NN_PAD_NA, {}, act, {in1, in2}, outs);
384 }
385 
relu(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)386 bool relu(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
387           HexagonModel* model) {
388     HEXAGON_SOFT_ASSERT_EQ(1, ins.size(), "Need 1 input for float32::relu");
389     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::relu");
390 
391     // get parameters
392     const hexagon_nn_input& input = model->getTensor(ins[0]);
393 
394     // add node to graph
395     return model->addBasicOperation(OP_Relu_f, NN_PAD_NA, {input}, outs);
396 }
397 
relu1(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)398 bool relu1(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
399            HexagonModel* model) {
400     HEXAGON_SOFT_ASSERT_EQ(1, ins.size(), "Need 1 input for float32::relu1");
401     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::relu1");
402 
403     // get parameters
404     const hexagon_nn_input& input = model->getTensor(ins[0]);
405     const hexagon_nn_input min = model->createScalar(-1.0f);
406     const hexagon_nn_input max = model->createScalar(1.0f);
407 
408     // add node to graph
409     return model->addBasicOperation(OP_Clamp_f, NN_PAD_NA, {input, min, max}, outs);
410 }
411 
relu6(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)412 bool relu6(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
413            HexagonModel* model) {
414     HEXAGON_SOFT_ASSERT_EQ(1, ins.size(), "Need 1 input for float32::relu6");
415     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::relu6");
416 
417     // get parameters
418     const hexagon_nn_input& input = model->getTensor(ins[0]);
419     const hexagon_nn_input max = model->createScalar(6.0f);
420 
421     // add node to graph
422     return model->addBasicOperation(OP_ReluX_f, NN_PAD_NA, {input, max}, outs);
423 }
424 
reshape(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)425 bool reshape(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
426              HexagonModel* model) {
427     HEXAGON_SOFT_ASSERT_EQ(2, ins.size(), "Need 2 inputs for float32::reshape");
428     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::reshape");
429 
430     // get parameters
431     const hexagon_nn_input& input = model->getTensor(ins[0]);
432     const hexagon_nn_input& newdims = model->getTensor(ins[1]);
433 
434     // add node to graph
435     return model->addBasicOperation(OP_Reshape, NN_PAD_NA, {input, newdims}, outs);
436 }
437 
resize_bilinear(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)438 bool resize_bilinear(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
439                      HexagonModel* model) {
440     HEXAGON_SOFT_ASSERT_EQ(3, ins.size(), "Need 3 inputs for float32::resize_bilinear");
441     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::resize_bilinear");
442 
443     // get parameters
444     const hexagon_nn_input& input = model->getTensor(ins[0]);
445 
446     const int32_t width = model->getScalar<int32_t>(ins[1]);
447     const int32_t height = model->getScalar<int32_t>(ins[2]);
448 
449     const hexagon_nn_input newdim = model->createValues<int32_t>({height, width});
450 
451     // add node to graph
452     return model->addBasicOperation(OP_ResizeBilinear_f, NN_PAD_NA, {input, newdim}, outs);
453 }
454 
softmax(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)455 bool softmax(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
456              HexagonModel* model) {
457     HEXAGON_SOFT_ASSERT_EQ(2, ins.size(), "Need 2 inputs for float32::softmax");
458     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::softmax");
459 
460     // get parameters
461     const hexagon_nn_input& input = model->getTensor(ins[0]);
462     const hexagon_nn_input& beta = model->getTensor(ins[1]);
463 
464     // add node to graph
465     return model->addBasicOperation(OP_Softmax_f, NN_PAD_NA, {input, beta}, outs);
466 }
467 
tanh(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)468 bool tanh(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
469           HexagonModel* model) {
470     HEXAGON_SOFT_ASSERT_EQ(1, ins.size(), "Need 1 input for float32::tanh");
471     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for float32::tanh");
472 
473     // get parameters
474     const hexagon_nn_input& input = model->getTensor(ins[0]);
475 
476     // add node to graph
477     return model->addBasicOperation(OP_Tanh_f, NN_PAD_NA, {input}, outs);
478 }
479 
480 }  // namespace float32
481 
482 namespace quant8_asym {
483 
add(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)484 bool add(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs, HexagonModel* model) {
485     HEXAGON_SOFT_ASSERT_EQ(3, ins.size(), "Need 3 inputs for quant8_asym::add");
486     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::add");
487 
488     // get parameters
489     const hexagon_nn_input& in1 = model->getTensor(ins[0]);
490     const hexagon_nn_input& in2 = model->getTensor(ins[1]);
491 
492     const op_type act = model->getQuantizedActivation(ins[2]);
493 
494     const hexagon_nn_input& in1_min = model->getQuantizationMin(ins[0]);
495     const hexagon_nn_input& in1_max = model->getQuantizationMax(ins[0]);
496     const hexagon_nn_input& in2_min = model->getQuantizationMin(ins[1]);
497     const hexagon_nn_input& in2_max = model->getQuantizationMax(ins[1]);
498 
499     // add node to graph
500     return model->addFusedQuant8Operation(OP_QuantizedAdd_8p8to32, NN_PAD_NA, {}, act,
501                                           {in1, in2, in1_min, in1_max, in2_min, in2_max}, outs);
502 }
503 
average_pool_2d(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)504 bool average_pool_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
505                      HexagonModel* model) {
506     HEXAGON_SOFT_ASSERT(ins.size() == 10 || ins.size() == 7,
507                         "Need 7 or 10 inputs for quant8_asym::average_pool_2d");
508     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::average_pool_2d");
509 
510     // get parameters
511     const hexagon_nn_input& input = model->getTensor(ins[0]);
512 
513     // setup parameters
514     hexagon_nn_padding_type pad;
515     int32_t stride_width;
516     int32_t stride_height;
517     int32_t filter_width;
518     int32_t filter_height;
519     op_type act;
520 
521     // get parameters
522     if (ins.size() == 10) {
523         const int32_t padding_left = model->getScalar<int32_t>(ins[1]);
524         const int32_t padding_right = model->getScalar<int32_t>(ins[2]);
525         const int32_t padding_top = model->getScalar<int32_t>(ins[3]);
526         const int32_t padding_bottom = model->getScalar<int32_t>(ins[4]);
527         stride_width = model->getScalar<int32_t>(ins[5]);
528         stride_height = model->getScalar<int32_t>(ins[6]);
529         filter_width = model->getScalar<int32_t>(ins[7]);
530         filter_height = model->getScalar<int32_t>(ins[8]);
531         act = model->getQuantizedActivation(ins[9]);
532 
533         const Shape inputShape = model->getShape(ins[0]);
534         pad = getPadding(inputShape.dimensions[2], inputShape.dimensions[1], stride_width,
535                          stride_height, filter_width, filter_height, padding_left, padding_right,
536                          padding_top, padding_bottom);
537         HEXAGON_SOFT_ASSERT_NE(pad, NN_PAD_NA, "Unknown padding");
538     } else {
539         pad = model->getPadding(ins[1]);
540         stride_width = model->getScalar<int32_t>(ins[2]);
541         stride_height = model->getScalar<int32_t>(ins[3]);
542         filter_width = model->getScalar<int32_t>(ins[4]);
543         filter_height = model->getScalar<int32_t>(ins[5]);
544         act = model->getQuantizedActivation(ins[6]);
545     }
546 
547     const hexagon_nn_input& in_min = model->getQuantizationMin(ins[0]);
548     const hexagon_nn_input& in_max = model->getQuantizationMax(ins[0]);
549     const hexagon_nn_input window = model->createShape(1, filter_height, filter_width, 1);
550     const hexagon_nn_input stride = model->createShape(1, stride_height, stride_width, 1);
551 
552     // add node to graph
553     return model->addQuant8OperationWithActivation(OP_QuantizedAvgPool_8, pad, act,
554                                                    {input, in_min, in_max, window, stride}, outs);
555 }
556 
concatenation(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)557 bool concatenation(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
558                    HexagonModel* model) {
559     HEXAGON_SOFT_ASSERT_LE(3, ins.size(), "Need at least 3 inputs for quant8_asym::concatenation");
560     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::concatenation");
561 
562     const size_t numInputTensors = ins.size() - 1;
563 
564     // get parameters
565     std::vector<hexagon_nn_input> inputs(numInputTensors * 3 + 1);
566     for (size_t i = 0; i < numInputTensors; ++i) {
567         inputs[i + 1 + numInputTensors * 0] = model->getTensor(ins[i]);
568         inputs[i + 1 + numInputTensors * 1] = model->getQuantizationMin(ins[i]);
569         inputs[i + 1 + numInputTensors * 2] = model->getQuantizationMax(ins[i]);
570     }
571 
572     // axis being concatenated
573     const int32_t axis = model->getScalar<int32_t>(ins[numInputTensors]);
574     const int32_t dims = model->getShape(ins[0]).dimensions.size();
575     inputs[0] = model->createScalar<int32_t>(axis + (4 - dims));
576 
577     // add node to graph
578     return model->addBasicOperation(OP_QuantizedConcat_8, NN_PAD_NA, inputs, outs);
579 }
580 
conv_2d(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)581 bool conv_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
582              HexagonModel* model) {
583     HEXAGON_SOFT_ASSERT(ins.size() == 10 || ins.size() == 7,
584                         "Need 7 or 10 inputs for quant8_asym::conv_2d");
585     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::conv_2d");
586 
587     // get parameters
588     const hexagon_nn_input& input = model->getTensor(ins[0]);
589     const hexagon_nn_input filter = model->createConvFilterTensor(ins[1]);
590     const hexagon_nn_input& bias = model->getTensor(ins[2]);
591 
592     // setup parameters
593     hexagon_nn_padding_type pad;
594     int32_t stride_width;
595     int32_t stride_height;
596     op_type act;
597 
598     // get parameters
599     if (ins.size() == 10) {
600         const int32_t padding_left = model->getScalar<int32_t>(ins[3]);
601         const int32_t padding_right = model->getScalar<int32_t>(ins[4]);
602         const int32_t padding_top = model->getScalar<int32_t>(ins[5]);
603         const int32_t padding_bottom = model->getScalar<int32_t>(ins[6]);
604         stride_width = model->getScalar<int32_t>(ins[7]);
605         stride_height = model->getScalar<int32_t>(ins[8]);
606         act = model->getQuantizedActivation(ins[9]);
607 
608         const Shape inputShape = model->getShape(ins[0]);
609         const Shape filterShape = model->getShape(ins[1]);
610         pad = getPadding(inputShape.dimensions[2], inputShape.dimensions[1], stride_width,
611                          stride_height, filterShape.dimensions[2], filterShape.dimensions[1],
612                          padding_left, padding_right, padding_top, padding_bottom);
613         HEXAGON_SOFT_ASSERT_NE(pad, NN_PAD_NA, "Unknown padding");
614     } else {
615         pad = model->getPadding(ins[3]);
616         stride_width = model->getScalar<int32_t>(ins[4]);
617         stride_height = model->getScalar<int32_t>(ins[5]);
618         act = model->getQuantizedActivation(ins[6]);
619     }
620 
621     const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
622     const hexagon_nn_input& input_max = model->getQuantizationMax(ins[0]);
623     const hexagon_nn_input& filter_min = model->getQuantizationMin(ins[1]);
624     const hexagon_nn_input& filter_max = model->getQuantizationMax(ins[1]);
625     const hexagon_nn_input& bias_min = model->getQuantizationMin(ins[2]);
626     const hexagon_nn_input& bias_max = model->getQuantizationMax(ins[2]);
627 
628     const hexagon_nn_input stride = model->createShape(1, stride_height, stride_width, 1);
629 
630     // add node to graph
631     return model->addFusedQuant8Operation(
632         OP_QuantizedConv2d_8x8to32, pad, {bias, bias_min, bias_max}, act,
633         {input, filter, input_min, input_max, filter_min, filter_max, stride}, outs);
634 }
635 
depthwise_conv_2d(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)636 bool depthwise_conv_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
637                        HexagonModel* model) {
638     HEXAGON_SOFT_ASSERT(ins.size() == 11 || ins.size() == 8,
639                         "Need 8 to 11 inputs for quant8_asym::depthwise_conv_2d");
640     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::depthwise_conv_2d");
641 
642     // get parameters
643     const hexagon_nn_input& input = model->getTensor(ins[0]);
644     const hexagon_nn_input& bias = model->getTensor(ins[2]);
645 
646     // setup parameters
647     hexagon_nn_padding_type pad;
648     int32_t stride_width;
649     int32_t stride_height;
650     int32_t depth_multiplier;
651     op_type act;
652 
653     // get parameters
654     if (ins.size() == 11) {
655         const int32_t padding_left = model->getScalar<int32_t>(ins[3]);
656         const int32_t padding_right = model->getScalar<int32_t>(ins[4]);
657         const int32_t padding_top = model->getScalar<int32_t>(ins[5]);
658         const int32_t padding_bottom = model->getScalar<int32_t>(ins[6]);
659         stride_width = model->getScalar<int32_t>(ins[7]);
660         stride_height = model->getScalar<int32_t>(ins[8]);
661         depth_multiplier = model->getScalar<int32_t>(ins[9]);
662         act = model->getQuantizedActivation(ins[10]);
663 
664         const Shape inputShape = model->getShape(ins[0]);
665         const Shape filterShape = model->getShape(ins[1]);
666         pad = getPadding(inputShape.dimensions[2], inputShape.dimensions[1], stride_width,
667                          stride_height, filterShape.dimensions[2], filterShape.dimensions[1],
668                          padding_left, padding_right, padding_top, padding_bottom);
669         HEXAGON_SOFT_ASSERT_NE(pad, NN_PAD_NA, "Unknown padding");
670     } else {
671         pad = model->getPadding(ins[3]);
672         stride_width = model->getScalar<int32_t>(ins[4]);
673         stride_height = model->getScalar<int32_t>(ins[5]);
674         depth_multiplier = model->getScalar<int32_t>(ins[6]);
675         act = model->getQuantizedActivation(ins[7]);
676     }
677 
678     const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
679     const hexagon_nn_input& input_max = model->getQuantizationMax(ins[0]);
680     const hexagon_nn_input& filter_min = model->getQuantizationMin(ins[1]);
681     const hexagon_nn_input& filter_max = model->getQuantizationMax(ins[1]);
682     const hexagon_nn_input& bias_min = model->getQuantizationMin(ins[2]);
683     const hexagon_nn_input& bias_max = model->getQuantizationMax(ins[2]);
684 
685     const hexagon_nn_input filter = model->createDepthwiseFilterTensor(ins[1], depth_multiplier);
686     const hexagon_nn_input stride = model->createShape(1, stride_height, stride_width, 1);
687 
688     // add node to graph
689     return model->addFusedQuant8Operation(
690         OP_QuantizedDepthwiseConv2d_8x8to32, pad, {bias, bias_min, bias_max}, act,
691         {input, filter, input_min, input_max, filter_min, filter_max, stride}, outs);
692 }
693 
dequantize(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)694 bool dequantize(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
695                 HexagonModel* model) {
696     HEXAGON_SOFT_ASSERT_EQ(1, ins.size(), "Need 1 input for quant8_asym::dequantize");
697     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::dequantize");
698 
699     // get parameters
700     const hexagon_nn_input& input = model->getTensor(ins[0]);
701 
702     const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
703     const hexagon_nn_input& input_max = model->getQuantizationMax(ins[0]);
704 
705     // add node to graph
706     return model->addBasicOperation(OP_Dequantize, NN_PAD_NA, {input, input_min, input_max}, outs);
707 }
708 
fully_connected(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)709 bool fully_connected(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
710                      HexagonModel* model) {
711     HEXAGON_SOFT_ASSERT_EQ(4, ins.size(), "Need 4 inputs for quant8::fully_connected");
712     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8::fully_connected");
713 
714     // get parameters
715     const hexagon_nn_input& input = model->getTensor(ins[0]);
716     const hexagon_nn_input& weights = model->createFullyConnectedWeightTensor(ins[1]);
717     const hexagon_nn_input& bias = model->getTensor(ins[2]);
718 
719     const op_type act = model->getQuantizedActivation(ins[3]);
720 
721     const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
722     const hexagon_nn_input& input_max = model->getQuantizationMax(ins[0]);
723     const hexagon_nn_input& weights_min = model->getQuantizationMin(ins[1]);
724     const hexagon_nn_input& weights_max = model->getQuantizationMax(ins[1]);
725     const hexagon_nn_input& bias_min = model->getQuantizationMin(ins[2]);
726     const hexagon_nn_input& bias_max = model->getQuantizationMax(ins[2]);
727 
728     // add node to graph
729     return model->addFusedQuant8Operation(
730         OP_QuantizedMatMul_8x8to32, NN_PAD_NA, {bias, bias_min, bias_max}, act,
731         {input, weights, input_min, input_max, weights_min, weights_max}, outs);
732 }
733 
logistic(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)734 bool logistic(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
735               HexagonModel* model) {
736     HEXAGON_SOFT_ASSERT_EQ(1, ins.size(), "Need 1 input for quant8_asym::logistic");
737     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::logistic");
738 
739     // get parameters
740     const hexagon_nn_input& input = model->getTensor(ins[0]);
741 
742     const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
743 
744     // TFLite uses different max value
745     const hexagon_nn_input input_max = model->createQuantizationValue(ins[0], 256);
746 
747     // add node to graph
748     return model->addBasicOperation(OP_QuantizedSigmoid_8, NN_PAD_NA, {input, input_min, input_max},
749                                     outs);
750 }
751 
max_pool_2d(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)752 bool max_pool_2d(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
753                  HexagonModel* model) {
754     HEXAGON_SOFT_ASSERT(ins.size() == 10 || ins.size() == 7,
755                         "Need 7 or 10 inputs for quant8_asym::max_pool_2d");
756     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::max_pool_2d");
757 
758     // get parameters
759     const hexagon_nn_input& input = model->getTensor(ins[0]);
760 
761     // setup parameters
762     hexagon_nn_padding_type pad;
763     int32_t stride_width;
764     int32_t stride_height;
765     int32_t filter_width;
766     int32_t filter_height;
767     op_type act;
768 
769     // get parameters
770     if (ins.size() == 10) {
771         const int32_t padding_left = model->getScalar<int32_t>(ins[1]);
772         const int32_t padding_right = model->getScalar<int32_t>(ins[2]);
773         const int32_t padding_top = model->getScalar<int32_t>(ins[3]);
774         const int32_t padding_bottom = model->getScalar<int32_t>(ins[4]);
775         stride_width = model->getScalar<int32_t>(ins[5]);
776         stride_height = model->getScalar<int32_t>(ins[6]);
777         filter_width = model->getScalar<int32_t>(ins[7]);
778         filter_height = model->getScalar<int32_t>(ins[8]);
779         act = model->getQuantizedActivation(ins[9]);
780 
781         const Shape inputShape = model->getShape(ins[0]);
782         pad = getPadding(inputShape.dimensions[2], inputShape.dimensions[1], stride_width,
783                          stride_height, filter_width, filter_height, padding_left, padding_right,
784                          padding_top, padding_bottom);
785         HEXAGON_SOFT_ASSERT_NE(pad, NN_PAD_NA, "Unknown padding");
786     } else {
787         pad = model->getPadding(ins[1]);
788         stride_width = model->getScalar<int32_t>(ins[2]);
789         stride_height = model->getScalar<int32_t>(ins[3]);
790         filter_width = model->getScalar<int32_t>(ins[4]);
791         filter_height = model->getScalar<int32_t>(ins[5]);
792         act = model->getQuantizedActivation(ins[6]);
793     }
794 
795     const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
796     const hexagon_nn_input& input_max = model->getQuantizationMax(ins[0]);
797     const hexagon_nn_input window = model->createShape(1, filter_height, filter_width, 1);
798     const hexagon_nn_input stride = model->createShape(1, stride_height, stride_width, 1);
799 
800     // add node to graph
801     return model->addQuant8OperationWithActivation(
802         OP_QuantizedMaxPool_8, pad, act, {input, input_min, input_max, window, stride}, outs);
803 }
804 
mul(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)805 bool mul(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs, HexagonModel* model) {
806     HEXAGON_SOFT_ASSERT_EQ(3, ins.size(), "Need 3 inputs for quant8_asym::mul");
807     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::mul");
808 
809     // get parameters
810     const hexagon_nn_input& in1 = model->getTensor(ins[0]);
811     const hexagon_nn_input& in2 = model->getTensor(ins[1]);
812 
813     const op_type act = model->getQuantizedActivation(ins[2]);
814 
815     const hexagon_nn_input& in1_min = model->getQuantizationMin(ins[0]);
816     const hexagon_nn_input& in1_max = model->getQuantizationMax(ins[0]);
817     const hexagon_nn_input& in2_min = model->getQuantizationMin(ins[1]);
818     const hexagon_nn_input& in2_max = model->getQuantizationMax(ins[1]);
819 
820     // add node to graph
821     return model->addFusedQuant8Operation(OP_QuantizedMul_8x8to32, NN_PAD_NA, {}, act,
822                                           {in1, in2, in1_min, in1_max, in2_min, in2_max}, outs);
823 }
824 
relu(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)825 bool relu(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
826           HexagonModel* model) {
827     HEXAGON_SOFT_ASSERT_EQ(1, ins.size(), "Need 1 input for quant8_asym::relu");
828     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::relu");
829 
830     // get parameters
831     const hexagon_nn_input& input = model->getTensor(ins[0]);
832 
833     const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
834     const hexagon_nn_input& input_max = model->getQuantizationMax(ins[0]);
835 
836     // add node to graph
837     return model->addBasicOperation(OP_QuantizedRelu_8, NN_PAD_NA, {input, input_min, input_max},
838                                     outs);
839 }
840 
relu1(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)841 bool relu1(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
842            HexagonModel* model) {
843     HEXAGON_SOFT_ASSERT_EQ(1, ins.size(), "Need 1 input for quant8_asym::relu1");
844     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::relu1");
845 
846     // get parameters
847     const hexagon_nn_input& input = model->getTensor(ins[0]);
848     const hexagon_nn_input min = model->createScalar(-1.0f);
849     const hexagon_nn_input max = model->createScalar(1.0f);
850 
851     const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
852     const hexagon_nn_input& input_max = model->getQuantizationMax(ins[0]);
853 
854     // add node to graph
855     return model->addBasicOperation(OP_QuantizedClamp_8, NN_PAD_NA,
856                                     {input, input_min, input_max, min, max}, outs);
857 }
858 
relu6(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)859 bool relu6(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
860            HexagonModel* model) {
861     HEXAGON_SOFT_ASSERT_EQ(1, ins.size(), "Need 1 input for quant8_asym::relu6");
862     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::relu6");
863 
864     // get parameters
865     const hexagon_nn_input& input = model->getTensor(ins[0]);
866     const hexagon_nn_input max = model->createScalar(6.0f);
867 
868     const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
869     const hexagon_nn_input& input_max = model->getQuantizationMax(ins[0]);
870 
871     // add node to graph
872     return model->addBasicOperation(OP_QuantizedReluX_8, NN_PAD_NA,
873                                     {input, input_min, input_max, max}, outs);
874 }
875 
reshape(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)876 bool reshape(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
877              HexagonModel* model) {
878     HEXAGON_SOFT_ASSERT_EQ(2, ins.size(), "Need 2 inputs for quant8_asym::reshape");
879     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::reshape");
880 
881     // get parameters
882     const hexagon_nn_input& input = model->getTensor(ins[0]);
883     const hexagon_nn_input& newdims = model->getTensor(ins[1]);
884 
885     const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
886     const hexagon_nn_input& input_max = model->getQuantizationMax(ins[0]);
887 
888     // add node to graph
889     return model->addBasicOperation(OP_QuantizedReshape, NN_PAD_NA,
890                                     {input, newdims, input_min, input_max}, outs);
891 }
892 
softmax(const std::vector<uint32_t> & ins,const std::vector<uint32_t> & outs,HexagonModel * model)893 bool softmax(const std::vector<uint32_t>& ins, const std::vector<uint32_t>& outs,
894              HexagonModel* model) {
895     HEXAGON_SOFT_ASSERT_EQ(2, ins.size(), "Need 2 inputs for quant8_asym::softmax");
896     HEXAGON_SOFT_ASSERT_EQ(1, outs.size(), "Need 1 output for quant8_asym::softmax");
897 
898     // get parameters
899     const hexagon_nn_input& input = model->getTensor(ins[0]);
900     const hexagon_nn_input& beta = model->getTensor(ins[1]);
901 
902     const hexagon_nn_input& input_min = model->getQuantizationMin(ins[0]);
903     const hexagon_nn_input& input_max = model->getQuantizationMax(ins[0]);
904 
905     // add node to graph
906     return model->addBasicOperation(OP_QuantizedSoftmax_8, NN_PAD_NA,
907                                     {input, input_min, input_max, beta}, outs);
908 }
909 
910 }  // namespace quant8_asym
911 
912 }  // namespace
913 
getOperationPrepareTable()914 OperationTable& getOperationPrepareTable() {
915     static OperationTable table = {
916         // NOTE: the operations that are commented out via inline represent
917         // operations that are valid for the Android O NNAPI release, but are
918         // currently not implemented in HVX.
919 
920         // -------------------------- 32-BIT FLOAT ----------------------------
921         // HVX is only performant when running on quantized values. Further, as
922         // an optimization, the current HVX driver will convert some floating
923         // point tensors into quantized values, perform the operation, and then
924         // convert them back to floating point. This results in a loss in
925         // precision causing some tests to fail. For these reasons, the FLOAT32
926         // operations are being temporarily disabled.
927         /*
928         {{OperationType::ADD, OperandType::TENSOR_FLOAT32}, float32::add},
929         {{OperationType::AVERAGE_POOL_2D, OperandType::TENSOR_FLOAT32}, float32::average_pool_2d},
930         {{OperationType::CONCATENATION, OperandType::TENSOR_FLOAT32}, float32::concatenation},
931         {{OperationType::CONV_2D, OperandType::TENSOR_FLOAT32}, float32::conv_2d},
932         {{OperationType::DEPTHWISE_CONV_2D, OperandType::TENSOR_FLOAT32},
933           float32::depthwise_conv_2d},
934         //{{OperationType::DEPTH_TO_SPACE, OperandType::TENSOR_FLOAT32}, float32::depth_to_space},
935         //{{OperationType::EMBEDDING_LOOKUP, OperandType::TENSOR_FLOAT32},
936         //  float32::embedding_lookup},
937         //{{OperationType::FLOOR, OperandType::TENSOR_FLOAT32}, float32::floor},
938         {{OperationType::FULLY_CONNECTED, OperandType::TENSOR_FLOAT32}, float32::fully_connected},
939         //{{OperationType::HASHTABLE_LOOKUP, OperandType::TENSOR_FLOAT32},
940         //  float32::hashtable_lookup},
941         //{{OperationType::L2_NORMALIZATION, OperandType::TENSOR_FLOAT32},
942         //  float32::l2_normalization},
943         {{OperationType::L2_POOL_2D, OperandType::TENSOR_FLOAT32}, float32::l2_pool_2d},
944         {{OperationType::LOCAL_RESPONSE_NORMALIZATION, OperandType::TENSOR_FLOAT32},
945           float32::local_response_normalization},
946         {{OperationType::LOGISTIC, OperandType::TENSOR_FLOAT32}, float32::logistic},
947         //{{OperationType::LSH_PROJECTION, OperandType::TENSOR_FLOAT32}, float32::lsh_projection},
948         //{{OperationType::LSTM, OperandType::TENSOR_FLOAT32}, float32::lstm },
949         {{OperationType::MAX_POOL_2D, OperandType::TENSOR_FLOAT32}, float32::max_pool_2d},
950         {{OperationType::MUL, OperandType::TENSOR_FLOAT32}, float32::mul},
951         {{OperationType::RELU, OperandType::TENSOR_FLOAT32}, float32::relu},
952         {{OperationType::RELU1, OperandType::TENSOR_FLOAT32}, float32::relu1},
953         {{OperationType::RELU6, OperandType::TENSOR_FLOAT32}, float32::relu6},
954         {{OperationType::RESHAPE, OperandType::TENSOR_FLOAT32}, float32::reshape},
955         {{OperationType::RESIZE_BILINEAR, OperandType::TENSOR_FLOAT32}, float32::resize_bilinear},
956         //{{OperationType::RNN, OperandType::TENSOR_FLOAT32}, float32::rnn},
957         {{OperationType::SOFTMAX, OperandType::TENSOR_FLOAT32}, float32::softmax},
958         //{{OperationType::SPACE_TO_DEPTH, OperandType::TENSOR_FLOAT32}, float32::space_to_depth},
959         //{{OperationType::SVDF, OperandType::TENSOR_FLOAT32}, float32::svdf },
960         {{OperationType::TANH, OperandType::TENSOR_FLOAT32}, float32::tanh},
961         */
962 
963         // -------------------- QUANTIZED 8-BIT ASYMMETRICAL ------------------
964         {{OperationType::ADD, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::add},
965         {{OperationType::AVERAGE_POOL_2D, OperandType::TENSOR_QUANT8_ASYMM},
966          quant8_asym::average_pool_2d},
967         {{OperationType::CONCATENATION, OperandType::TENSOR_QUANT8_ASYMM},
968          quant8_asym::concatenation},
969         {{OperationType::CONV_2D, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::conv_2d},
970         {{OperationType::DEPTHWISE_CONV_2D, OperandType::TENSOR_QUANT8_ASYMM},
971          quant8_asym::depthwise_conv_2d},
972         //{{OperationType::DEPTH_TO_SPACE, OperandType::TENSOR_QUANT8_ASYMM},
973         //  quant8_asym::depth_to_space},
974         {{OperationType::DEQUANTIZE, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::dequantize},
975         //{{OperationType::EMBEDDING_LOOKUP, OperandType::TENSOR_QUANT8_ASYMM},
976         //  quant8_asym::embedding_lookup},
977         {{OperationType::FULLY_CONNECTED, OperandType::TENSOR_QUANT8_ASYMM},
978          quant8_asym::fully_connected},
979         //{{OperationType::HASHTABLE_LOOKUP, OperandType::TENSOR_QUANT8_ASYMM},
980         //  quant8_asym::hashtable_lookup},
981         {{OperationType::LOGISTIC, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::logistic},
982         //{{OperationType::LSH_PROJECTION, OperandType::TENSOR_QUANT8_ASYMM},
983         //  quant8_asym::lsh_projection},
984         {{OperationType::MAX_POOL_2D, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::max_pool_2d},
985         {{OperationType::MUL, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::mul},
986         {{OperationType::RELU, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::relu},
987         {{OperationType::RELU1, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::relu1},
988         {{OperationType::RELU6, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::relu6},
989         {{OperationType::RESHAPE, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::reshape},
990         {{OperationType::SOFTMAX, OperandType::TENSOR_QUANT8_ASYMM}, quant8_asym::softmax},
991         //{{OperationType::SPACE_TO_DEPTH, OperandType::TENSOR_QUANT8_ASYMM},
992         //  quant8_asym::space_to_depth},
993     };
994 
995     // The following functions are normally used by float32, but those
996     // operations have been temporarily disabled. Void explicitly marks them as
997     // unused, and prevents the compiler from throwing an error.
998     (void)float32::add;
999     (void)float32::average_pool_2d;
1000     (void)float32::concatenation;
1001     (void)float32::conv_2d;
1002     (void)float32::depthwise_conv_2d;
1003     (void)float32::fully_connected;
1004     (void)float32::l2_pool_2d;
1005     (void)float32::local_response_normalization;
1006     (void)float32::logistic;
1007     (void)float32::max_pool_2d;
1008     (void)float32::mul;
1009     (void)float32::relu;
1010     (void)float32::relu1;
1011     (void)float32::relu6;
1012     (void)float32::reshape;
1013     (void)float32::resize_bilinear;
1014     (void)float32::softmax;
1015     (void)float32::tanh;
1016 
1017     return table;
1018 }
1019 
1020 }  // namespace hexagon
1021 }  // namespace implementation
1022 }  // namespace V1_0
1023 }  // namespace neuralnetworks
1024 }  // namespace hardware
1025 }  // namespace android
1026