a84ce1b8d9b64e3d51af870da2b02e90256d65d7
[platform/core/ml/nnfw.git] / runtime / onert / frontend / nnapi / wrapper / OperationFactory.cc
1 /*
2  * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
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 #include "OperationFactory.h"
18 #include "NNAPIConvert.h"
19
20 #include <ir/Operations.Include.h>
21 #include <string.h>
22
23 namespace
24 {
25 using namespace onert::ir;
26
27 void replaceDataType(Operands &operands, const OperandIndex &index, const DataType type)
28 {
29   assert(operands.exist(index));
30   operands.at(index).type(type);
31 }
32
33 ExplicitPadding makeExplicitPadding(Operands &operands, const OperandIndex &left_index,
34                                     const OperandIndex &right_index, const OperandIndex &top_index,
35                                     const OperandIndex &bottom_index)
36 {
37   auto left = operands.at(left_index).asScalar<int32_t>();
38   auto right = operands.at(right_index).asScalar<int32_t>();
39   auto top = operands.at(top_index).asScalar<int32_t>();
40   auto bottom = operands.at(bottom_index).asScalar<int32_t>();
41
42   if (left < 0 || right < 0 || top < 0 || bottom < 0)
43   {
44     throw std::runtime_error{"Cannot handle negative explicit padding value"};
45   }
46
47   ExplicitPadding param;
48   param.left = static_cast<uint32_t>(left);
49   param.right = static_cast<uint32_t>(right);
50   param.top = static_cast<uint32_t>(top);
51   param.bottom = static_cast<uint32_t>(bottom);
52
53   return param;
54 }
55
56 Stride makeStride(Operands &operands, const OperandIndex &horizontal_index,
57                   const OperandIndex &vertical_index)
58 {
59   auto horizontal = operands.at(horizontal_index).asScalar<int32_t>();
60   auto vertical = operands.at(vertical_index).asScalar<int32_t>();
61
62   if (vertical < 0 || horizontal < 0)
63   {
64     throw std::runtime_error{"Cannot handle negative stride value"};
65   }
66
67   Stride stride;
68   stride.horizontal = static_cast<uint32_t>(horizontal);
69   stride.vertical = static_cast<uint32_t>(vertical);
70
71   return stride;
72 }
73
74 uint32_t getUint32Scalar(Operands &operands, const OperandIndex index)
75 {
76   auto int32_value = operands.at(index).asScalar<int32_t>();
77   if (int32_value < 0)
78   {
79     throw std::runtime_error{"Cannot handle negative value"};
80   }
81
82   return static_cast<uint32_t>(int32_value);
83 }
84
85 OperationFactory::Generator
86 getElementwiseActivationGenerator(const onert::ir::operation::ElementwiseActivation::Type op_type,
87                                   float alpha = 0.f, float beta = 0.f)
88 {
89   return [op_type, alpha, beta](const OperationFactory::Param &init_param, Operands &) {
90     assert(init_param.input_count == 1);
91     assert(init_param.output_count == 1);
92
93     // Each input should be interpreted as follows:
94     //
95     //  0 -> Input Tensor Index
96
97     OperandIndexSequence inputs{init_param.inputs[0]};
98     OperandIndexSequence outputs{init_param.outputs[0]};
99
100     operation::ElementwiseActivation::Param param;
101     param.op_type = op_type;
102     param.alpha = alpha;
103     param.beta = beta;
104
105     return new operation::ElementwiseActivation{inputs, outputs, param};
106   };
107 }
108
109 OperationFactory::Generator getElementwiseBinaryGenerator(
110     const onert::ir::operation::ElementwiseBinary::ElementwiseBinaryType op_type)
111 {
112   return [op_type](const OperationFactory::Param &init_param, Operands &) {
113     assert(init_param.input_count == 2);
114     assert(init_param.output_count == 1);
115
116     // Each input should be interpreted as follows:
117     //
118     //  0 -> Lefthand side operand
119     //  1 -> Righthand side operand
120
121     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
122     OperandIndexSequence outputs{init_param.outputs[0]};
123
124     operation::ElementwiseBinary::Param param;
125     param.op_type = op_type;
126
127     return new operation::ElementwiseBinary{inputs, outputs, param};
128   };
129 }
130
131 OperationFactory::Generator
132 getElementwiseUnaryGenerator(const onert::ir::operation::ElementwiseUnary::Type op_type)
133 {
134   return [op_type](const OperationFactory::Param &init_param, Operands &operands) {
135     assert(init_param.input_count == 1);
136     assert(init_param.output_count == 1);
137
138     // Each input should be interpreted as follows:
139     //
140     //  0 ->  Input Tensor Index
141
142     OperandIndexSequence inputs{init_param.inputs[0]};
143     OperandIndexSequence outputs{init_param.outputs[0]};
144
145     operation::ElementwiseUnary::Param param;
146     param.op_type = op_type;
147
148     if (op_type == operation::ElementwiseUnary::Type::CAST)
149     {
150       // NNAPI uses QUANT_UINT8_ASYMM to represent UINT8 type for ANEURALNETWORKS_CAST's
151       // input/output
152       if (operands.at(inputs.at(0)).typeInfo().type() == DataType::QUANT_UINT8_ASYMM)
153       {
154         replaceDataType(operands, inputs.at(0), DataType::UINT8);
155       }
156       if (operands.at(outputs.at(0)).typeInfo().type() == DataType::QUANT_UINT8_ASYMM)
157       {
158         replaceDataType(operands, outputs.at(0), DataType::UINT8);
159       }
160     }
161
162     return new operation::ElementwiseUnary{inputs, outputs, param};
163   };
164 }
165
166 OperationFactory::Generator
167 getBinaryArithmeticGenerator(const onert::ir::operation::BinaryArithmetic::ArithmeticType op_type)
168 {
169   return [op_type](const OperationFactory::Param &init_param, Operands &operands) {
170     assert(init_param.input_count == 3);
171     assert(init_param.output_count == 1);
172
173     // Each input should be interpreted as follows:
174     //
175     //  0 -> Lefthand side operand
176     //  1 -> Righthand side operand
177
178     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
179     OperandIndexSequence outputs{init_param.outputs[0]};
180
181     operation::BinaryArithmetic::Param param;
182     param.arithmetic_type = op_type;
183     const auto activation_index = OperandIndex{init_param.inputs[2]};
184     param.activation =
185         NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
186
187     return new operation::BinaryArithmetic{inputs, outputs, param};
188   };
189 }
190
191 OperationFactory::Generator
192 getPool2DGenerator(const onert::ir::operation::Pool2D::PoolType pool_type)
193 {
194   return [pool_type](const OperationFactory::Param &init_param, Operands &operands) {
195     assert(init_param.input_count == 7 || init_param.input_count == 10);
196     assert(init_param.output_count == 1);
197
198     // In common
199     //  0 -> IFM Tensor Index
200     OperandIndexSequence inputs{init_param.inputs[0]};
201     OperandIndexSequence outputs{init_param.outputs[0]};
202
203     operation::Pool2D::Param param;
204     param.op_type = pool_type;
205     if (init_param.input_count == 7) // support implicit padding
206     {
207       // Each input should be interpreted as follows:
208       //
209       //  1 -> Padding Code (ANEURALNETWORKS_PADDING_SAME or ANEURALNETWORKS_PADDING_VALID) Index
210       //  2 -> Horizontal (over width) Stride Index
211       //  3 -> Vertial (over height) Stride Index
212       //  4 -> Filter Width Index
213       //  5 -> Filter Height Index
214       //  6 -> FuseCode (activation) Index
215
216       const auto padding_index = OperandIndex{init_param.inputs[1]};
217       const auto hstride_index = OperandIndex{init_param.inputs[2]};
218       const auto vstride_index = OperandIndex{init_param.inputs[3]};
219       const auto kw_index = OperandIndex{init_param.inputs[4]};
220       const auto kh_index = OperandIndex{init_param.inputs[5]};
221       const auto activation_index = OperandIndex{init_param.inputs[6]};
222
223       param.padding.type =
224           NNAPIConvert::getPaddingType(operands.at(padding_index).asScalar<PaddingCode>());
225       param.stride = makeStride(operands, hstride_index, vstride_index);
226       param.kw = getUint32Scalar(operands, kw_index);
227       param.kh = operands.at(kh_index).asScalar<uint32_t>();
228       param.activation =
229           NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
230     }
231     else // support explicit padding
232     {
233       // Each input should be interpreted as follows:
234       //
235       //  1 -> Padding_left index
236       //  2 -> Padding_right index
237       //  3 -> Padding_top index
238       //  4 -> Padding_bottom index
239       //  5 -> Horizontal (over width) Stride Index
240       //  6 -> Vertial (over height) Stride Index
241       //  7 -> Filter Width Index
242       //  8 -> Filter Height Index
243       //  9 -> FuseCode (activation) Index
244
245       const auto padding_left_index = OperandIndex{init_param.inputs[1]};
246       const auto padding_right_index = OperandIndex{init_param.inputs[2]};
247       const auto padding_top_index = OperandIndex{init_param.inputs[3]};
248       const auto padding_bottom_index = OperandIndex{init_param.inputs[4]};
249       const auto hstride_index = OperandIndex{init_param.inputs[5]};
250       const auto vstride_index = OperandIndex{init_param.inputs[6]};
251       const auto kw_index = OperandIndex{init_param.inputs[7]};
252       const auto kh_index = OperandIndex{init_param.inputs[8]};
253       const auto activation_index = OperandIndex{init_param.inputs[9]};
254
255       param.padding.type = PaddingType::EXPLICIT;
256       param.padding.param = makeExplicitPadding(operands, padding_left_index, padding_right_index,
257                                                 padding_top_index, padding_bottom_index);
258       param.stride = makeStride(operands, hstride_index, vstride_index);
259       param.kw = getUint32Scalar(operands, kw_index);
260       param.kh = getUint32Scalar(operands, kh_index);
261       param.activation =
262           NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
263     }
264     return new operation::Pool2D{inputs, outputs, param};
265   };
266 }
267
268 OperationFactory::Generator
269 getReduceGenerator(const onert::ir::operation::Reduce::ReduceType reduce_type)
270 {
271   return [reduce_type](const OperationFactory::Param &init_param, Operands &operands) {
272     assert(init_param.input_count == 3);
273     assert(init_param.output_count == 1);
274
275     // Each input should be interpreted as follows:
276     //
277     //  0 -> Input Tensor Index
278     //  1 -> Reduced Axes Tensor Index
279     //  2 -> keep_dims Index
280
281     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
282     OperandIndexSequence outputs{init_param.outputs[0]};
283
284     operation::Reduce::Param param;
285     param.reduce_type = reduce_type;
286     param.keep_dims = operands.at(OperandIndex{init_param.inputs[2]}).asScalar<int8_t>() != 0;
287
288     return new operation::Reduce{inputs, outputs, param};
289   };
290 }
291
292 template <typename T>
293 Operation *CreateSimpleUnaryOp(const OperationFactory::Param &init_param, Operands &)
294 {
295   assert(init_param.input_count == 1 && init_param.output_count == 1);
296
297   OperandIndexSequence outputs{init_param.outputs[0]};
298
299   // Each input should be interpreted as follows:
300   //
301   //  0 -> Input Tensor Index
302   OperandIndexSequence inputs{init_param.inputs[0]};
303
304   return new T{inputs, outputs};
305 }
306
307 // A generator function for binary ops with no params
308 template <typename T>
309 Operation *createSimpleBinaryOp(const OperationFactory::Param &init_param, Operands &)
310 {
311   assert(init_param.input_count == 2 && init_param.output_count == 1);
312
313   OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
314   OperandIndexSequence outputs{init_param.outputs[0]};
315
316   return new T{inputs, outputs};
317 }
318
319 OperationFactory::Generator getComparisonGenerator(operation::Comparison::ComparisonType type)
320 {
321   return [type](const OperationFactory::Param &init_param, Operands &) -> Operation * {
322     assert(init_param.input_count == 2 && init_param.output_count == 1);
323
324     OperandIndexSequence outputs{init_param.outputs[0]};
325
326     // Each input should be interpreted as follows:
327     //
328     //  0 -> input0 Tensor Index
329     //  1 -> input1 Tensor Index
330     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
331
332     operation::Comparison::Param param;
333     param.comparison_type = type;
334
335     return new operation::Comparison{inputs, outputs, param};
336   };
337 }
338
339 } // namespace
340
341 OperationFactory &OperationFactory::get()
342 {
343   static OperationFactory factory;
344   return factory;
345 }
346
347 OperationFactory::OperationFactory()
348 {
349   // Each input should be interpreted as follows:
350   //  0 -> Input Tensor Index
351   //  1 -> Block size Index
352   _map[ANEURALNETWORKS_BATCH_TO_SPACE_ND] = createSimpleBinaryOp<operation::BatchToSpaceND>;
353
354   _map[ANEURALNETWORKS_DEPTHWISE_CONV_2D] = [](const OperationFactory::Param &init_param,
355                                                Operands &operands) {
356     assert((init_param.input_count == 8 || init_param.input_count == 11) &&
357            init_param.output_count == 1);
358
359     // In common
360     // 0 -> IFM Tensor Index
361     // 1 -> Kernel Tensor Index
362     // 2 -> Bias Tensor Index
363     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
364     OperandIndexSequence outputs{init_param.outputs[0]};
365
366     operation::DepthwiseConv2D::Param param;
367     if (init_param.input_count == 8)
368     {
369       // Imlicit Padding case
370       // Each input should be interpreted as follows:
371       //
372       // 3 -> Padding Code (ANEURALNETWORKS_PADDING_SAME or ANEURALNETWORKS_PADDING_VALID) Index
373       // 4 -> Stride (width) Index
374       // 5 -> Stride (height) INdex
375       // 6 -> Depthwise multiplier
376       // 7 -> Activation Index
377
378       const auto padding_index = OperandIndex{init_param.inputs[3]};
379       const auto hstride_index = OperandIndex{init_param.inputs[4]};
380       const auto vstride_index = OperandIndex{init_param.inputs[5]};
381       const auto multiplier_index = OperandIndex{init_param.inputs[6]};
382       const auto activation_index = OperandIndex{init_param.inputs[7]};
383
384       param.padding.type =
385           NNAPIConvert::getPaddingType(operands.at(padding_index).asScalar<PaddingCode>());
386       param.stride = makeStride(operands, hstride_index, vstride_index);
387       param.multiplier = getUint32Scalar(operands, multiplier_index);
388       param.activation =
389           NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
390     }
391     else
392     {
393       // Explicit Padding case
394       // Each input should be interpreted as follows:
395       //
396       // 3 -> Padding On the Left
397       // 4 -> Padding On the Right
398       // 5 -> Padding On the Top
399       // 6 -> Padding On the Bottom
400       // 7 -> Stride (width) Index
401       // 8 -> Stride (height) Index
402       // 9 -> Depthwise multiplier
403       // 10-> Activation Index
404
405       const auto padding_left_index = OperandIndex{init_param.inputs[3]};
406       const auto padding_right_index = OperandIndex{init_param.inputs[4]};
407       const auto padding_top_index = OperandIndex{init_param.inputs[5]};
408       const auto padding_bottom_index = OperandIndex{init_param.inputs[6]};
409       const auto hstride_index = OperandIndex{init_param.inputs[7]};
410       const auto vstride_index = OperandIndex{init_param.inputs[8]};
411       const auto multiplier_index = OperandIndex{init_param.inputs[9]};
412       const auto activation_index = OperandIndex{init_param.inputs[10]};
413
414       param.padding.type = PaddingType::EXPLICIT;
415       param.padding.param = makeExplicitPadding(operands, padding_left_index, padding_right_index,
416                                                 padding_top_index, padding_bottom_index);
417       param.stride = makeStride(operands, hstride_index, vstride_index);
418       param.multiplier = getUint32Scalar(operands, multiplier_index);
419       param.activation =
420           NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
421     }
422
423     // TODO set dilation
424     param.dilation.width_factor = 1;
425     param.dilation.height_factor = 1;
426
427     return new operation::DepthwiseConv2D{inputs, outputs, param};
428   };
429
430   _map[ANEURALNETWORKS_MAX_POOL_2D] = getPool2DGenerator(operation::Pool2D::PoolType::MAX);
431
432   _map[ANEURALNETWORKS_AVERAGE_POOL_2D] = getPool2DGenerator(operation::Pool2D::PoolType::AVG);
433
434   _map[ANEURALNETWORKS_CONCATENATION] = [](const OperationFactory::Param &init_param,
435                                            Operands &operands) {
436     assert(init_param.input_count >= 2); // At least one one input tensor and axis
437     assert(init_param.output_count == 1);
438
439     // When there are N + 1 inputs, each input should be interpreted as follows:
440     //
441     //  [0, N) -> Input tensors
442     //  N -> Axis
443     //
444
445     OperandIndexSequence inputs;
446     for (uint32_t n = 0; n < init_param.input_count - 1; ++n)
447     {
448       inputs.append(OperandIndex{init_param.inputs[n]});
449     }
450     OperandIndexSequence outputs{init_param.outputs[0]};
451
452     operation::Concat::Param param;
453     const OperandIndex axis_index{init_param.inputs[init_param.input_count - 1]};
454     param.axis = operands.at(axis_index).asScalar<int32_t>();
455
456     return new operation::Concat{inputs, outputs, param};
457   };
458
459   _map[ANEURALNETWORKS_RESHAPE] = [](const OperationFactory::Param &init_param, Operands &) {
460     assert(init_param.input_count == 2 && init_param.output_count == 1);
461
462     // Each input should be interpreted as follows:
463     //
464     //  0 -> A tensor, specifying the tensor to be reshaped.
465     //  1 -> A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32, defining the shape of the output
466     //  tensor
467
468     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
469     OperandIndexSequence outputs{init_param.outputs[0]};
470
471     operation::Reshape::Param param{};
472
473     return new operation::Reshape{inputs, outputs, param};
474   };
475
476   _map[ANEURALNETWORKS_FULLY_CONNECTED] = [](const OperationFactory::Param &init_param,
477                                              Operands &operands) {
478     assert(init_param.input_count == 4 && init_param.output_count == 1);
479
480     // Each input should be interpreted as follows:
481     //
482     //  0 -> A tensor, specifying the input.
483     //  1 -> A 2-D tensor, specifying the weights
484     //  2 -> A 1-D tensor, specifying the bias
485     //  3 -> An INT32 value, and has to be one of the FuseCode values
486
487     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
488     OperandIndexSequence outputs{init_param.outputs[0]};
489
490     operation::FullyConnected::Param param;
491     const auto activation_index = OperandIndex{init_param.inputs[3]};
492     param.activation =
493         NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
494     param.weights_format = FullyConnectedWeightsFormat::Default;
495
496     return new operation::FullyConnected{inputs, outputs, param};
497   };
498
499   _map[ANEURALNETWORKS_SOFTMAX] = [](const OperationFactory::Param &init_param,
500                                      Operands &operands) {
501     assert(init_param.input_count == 2 && init_param.output_count == 1);
502
503     // Each input should be interpreted as follows:
504     //
505     //  0 -> A 2-D or 4-D tensor, specifying the tensor to be reshaped.
506     //  1 ->  FLOAT32 value, specifying the positive scaling factor for the exponent, beta.
507
508     OperandIndexSequence inputs{init_param.inputs[0]};
509     OperandIndexSequence outputs{init_param.outputs[0]};
510
511     const auto beta_index = OperandIndex{init_param.inputs[1]};
512
513     operation::Softmax::Param param;
514     param.beta = operands.at(beta_index).asScalar<float>();
515
516     return new operation::Softmax{inputs, outputs, param};
517   };
518
519   _map[ANEURALNETWORKS_CAST] =
520       getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::CAST);
521
522   // ANEURALNETWORKS_CAST_EX is deprecated
523   // TODO Remove ANEURALNETWORKS_CAST_EX
524   _map[ANEURALNETWORKS_CAST_EX] = _map[ANEURALNETWORKS_CAST];
525
526   _map[ANEURALNETWORKS_CONV_2D] = [](const OperationFactory::Param &init_param,
527                                      Operands &operands) {
528     using operation::Conv2D;
529
530     // inputCount is either 7 or 10 acccording to NN API specification.
531     //  - Padding is implicit when inputCount is 7
532     //  - Padding is explicit when inputCount is 10
533     assert(init_param.input_count == 7 || init_param.input_count == 10 ||
534            init_param.input_count == 13);
535     assert(init_param.output_count == 1);
536
537     //  0 -> IFM Tensor Index
538     //  1 -> Kernel Tensor Index
539     //  2 -> Bias Tensor Index
540
541     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
542     OperandIndexSequence outputs{init_param.outputs[0]};
543
544     Conv2D::Param param;
545     if (init_param.input_count == 7) // support implicit padding
546     {
547       // Each input should be interpreted as follows:
548       //
549       //  3 -> Padding Code (ANEURALNETWORKS_PADDING_SAME or ANEURALNETWORKS_PADDING_VALID) Index
550       //  4 -> Stride (width) Index
551       //  5 -> Stride (height) INdex
552       //  6 -> Activation Index
553
554       const auto padding_index = OperandIndex{init_param.inputs[3]};
555       const auto hstride_index = OperandIndex{init_param.inputs[4]};
556       const auto vstride_index = OperandIndex{init_param.inputs[5]};
557       const auto activation_index = OperandIndex{init_param.inputs[6]};
558
559       param.padding.type =
560           NNAPIConvert::getPaddingType(operands.at(padding_index).asScalar<PaddingCode>());
561       param.stride = makeStride(operands, hstride_index, vstride_index);
562
563       param.dilation.width_factor = 1;
564       param.dilation.height_factor = 1;
565
566       param.activation =
567           NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
568     }
569     else if (init_param.input_count == 10) // support explicit padding
570     {
571       // Each input should be interpreted as follows:
572       //
573       //  3 -> Padding_left index
574       //  4 -> Padding_right index
575       //  5 -> Padding_top index
576       //  6 -> Padding_bottom index
577       //  7 -> Stride (width) Index
578       //  8 -> Stride (height) INdex
579       //  9 -> Activation Index
580
581       const auto padding_left_index = OperandIndex{init_param.inputs[3]};
582       const auto padding_right_index = OperandIndex{init_param.inputs[4]};
583       const auto padding_top_index = OperandIndex{init_param.inputs[5]};
584       const auto padding_bottom_index = OperandIndex{init_param.inputs[6]};
585       const auto hstride_index = OperandIndex{init_param.inputs[7]};
586       const auto vstride_index = OperandIndex{init_param.inputs[8]};
587       const auto activation_index = OperandIndex{init_param.inputs[9]};
588
589       param.padding.type = PaddingType::EXPLICIT;
590       param.padding.param = makeExplicitPadding(operands, padding_left_index, padding_right_index,
591                                                 padding_top_index, padding_bottom_index);
592       param.stride = makeStride(operands, hstride_index, vstride_index);
593
594       param.dilation.width_factor = 1;
595       param.dilation.height_factor = 1;
596
597       param.activation =
598           NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
599     }
600     else if (init_param.input_count == 13) // support dilation
601     {
602       // Each input should be interpreted as follows:
603       //
604       //  3 -> Padding_left Index
605       //  4 -> Padding_right Index
606       //  5 -> Padding_top Index
607       //  6 -> Padding_bottom Index
608       //  7 -> Stride (width) Index
609       //  8 -> Stride (height) Index
610       //  9 -> Activation Index
611       //  11 -> Dilation (width_factor) Index
612       //  12 -> Dilation (height_factor) INdex
613
614       const auto padding_left_index = OperandIndex{init_param.inputs[3]};
615       const auto padding_right_index = OperandIndex{init_param.inputs[4]};
616       const auto padding_top_index = OperandIndex{init_param.inputs[5]};
617       const auto padding_bottom_index = OperandIndex{init_param.inputs[6]};
618       const auto hstride_index = OperandIndex{init_param.inputs[7]};
619       const auto vstride_index = OperandIndex{init_param.inputs[8]};
620       const auto activation_index = OperandIndex{init_param.inputs[9]};
621       const auto width_factor_index = OperandIndex{init_param.inputs[11]};
622       const auto height_factor_index = OperandIndex{init_param.inputs[12]};
623
624       param.padding.type = PaddingType::EXPLICIT;
625       param.padding.param = makeExplicitPadding(operands, padding_left_index, padding_right_index,
626                                                 padding_top_index, padding_bottom_index);
627       param.stride = makeStride(operands, hstride_index, vstride_index);
628
629       auto width_factor = operands.at(width_factor_index).asScalar<int32_t>();
630       auto height_factor = operands.at(height_factor_index).asScalar<int32_t>();
631
632       param.dilation.width_factor = width_factor;
633       param.dilation.height_factor = height_factor;
634
635       param.activation =
636           NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
637     }
638     else
639     {
640       throw std::runtime_error{"Conv2D: unsupported input operand count"};
641     }
642
643     return new Conv2D{inputs, outputs, param};
644   };
645
646   _map[ANEURALNETWORKS_ADD] =
647       getBinaryArithmeticGenerator(onert::ir::operation::BinaryArithmetic::ArithmeticType::ADD);
648
649   _map[ANEURALNETWORKS_ADDV2_EX] = _map[ANEURALNETWORKS_ADD];
650
651   _map[ANEURALNETWORKS_REDUCE_SUM] =
652       getReduceGenerator(onert::ir::operation::Reduce::ReduceType::SUM);
653
654   // ANEURALNETWORKS_REDUCE_SUM_EX is deprecated
655   // TODO Remove ANEURALNETWORKS_REDUCE_SUM_EX
656   _map[ANEURALNETWORKS_REDUCE_SUM_EX] = _map[ANEURALNETWORKS_REDUCE_SUM];
657
658   _map[ANEURALNETWORKS_SUB] =
659       getBinaryArithmeticGenerator(onert::ir::operation::BinaryArithmetic::ArithmeticType::SUB);
660
661   _map[ANEURALNETWORKS_SLICE] = [](const OperationFactory::Param &init_param, Operands &) {
662     assert(init_param.input_count == 3 && init_param.output_count == 1);
663
664     OperandIndexSequence outputs{init_param.outputs[0]};
665
666     // Each input should be interpreted as follows:
667     //
668     //  0 -> Input Tensor Index
669     //  1 -> Begins Tensor Index
670     //  2 -> Sizes Tensor Index
671     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
672
673     return new operation::Slice{inputs, outputs};
674   };
675
676   _map[ANEURALNETWORKS_STRIDED_SLICE] = [](const OperationFactory::Param &init_param,
677                                            Operands &operands) {
678     assert(init_param.input_count == 7 && init_param.output_count == 1);
679
680     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2],
681                                 init_param.inputs[3]};
682     OperandIndexSequence outputs{init_param.outputs[0]};
683
684     // Each input should be interpreted as follows:
685     //
686     //  1 -> A 1-D Tensor of {@link ANEURALNETWORKS_TENSOR_INT32}, the starts of
687     //       the dimensions of the input tensor to be sliced. The length must be
688     //       of rank(input0).
689     //  2 -> A 1-D Tensor of {@link ANEURALNETWORKS_TENSOR_INT32}, the ends of
690     //       the dimensions of the input tensor to be sliced. The length must be
691     //       of rank(input0).
692     //  3 -> A 1-D Tensor of {@link ANEURALNETWORKS_TENSOR_INT32}, the strides of
693     //       the dimensions of the input tensor to be sliced. The length must be
694     //       of rank(input0).
695     //  4 -> An {@link ANEURALNETWORKS_INT32} scalar, begin_mask. If the ith bit
696     //       of begin_mask is set, begin[i] is ignored and the fullest possible
697     //       range in that dimension is used instead.
698     //  5 -> An {@link ANEURALNETWORKS_INT32} scalar, end_mask. If the ith bit of
699     //       end_mask is set, end[i] is ignored and the fullest possible range in
700     //       that dimension is used instead.
701     //  6 -> An {@link ANEURALNETWORKS_INT32} scalar, shrink_axis_mask. An int32
702     //       mask. If the ith bit of shrink_axis_mask is set, it implies that the
703     //       ith specification shrinks the dimensionality by 1. A slice of size 1
704     //       starting from begin[i] in the dimension must be preserved.
705
706     operation::StridedSlice::Param param;
707
708     param.begin_mask = operands.at(OperandIndex{init_param.inputs[4]}).asScalar<std::int32_t>();
709     param.end_mask = operands.at(OperandIndex{init_param.inputs[5]}).asScalar<std::int32_t>();
710     param.shrink_axis_mask =
711         operands.at(OperandIndex{init_param.inputs[6]}).asScalar<std::int32_t>();
712
713     return new operation::StridedSlice{inputs, outputs, param};
714   };
715
716   _map[ANEURALNETWORKS_TRANSPOSE] = createSimpleBinaryOp<operation::Transpose>;
717
718   _map[ANEURALNETWORKS_MUL] =
719       getBinaryArithmeticGenerator(onert::ir::operation::BinaryArithmetic::ArithmeticType::MUL);
720
721   _map[ANEURALNETWORKS_SQUEEZE] = [](const OperationFactory::Param &init_param,
722                                      Operands &operands) {
723     assert(init_param.input_count == 1 || init_param.input_count == 2);
724     assert(init_param.output_count == 1);
725
726     OperandIndexSequence outputs{init_param.outputs[0]};
727
728     // Each input should be interpreted as follows:
729     //
730     // 0 -> An n-D tensor, the tensor to be squeezed.
731     // 1 -> An optional 1-D tensor of ANEURALNETWORKS_TENSOR_INT32. The dimensions to squeeze.
732     //      If specified only squeezes the dimensions listed. Otherwise, squeezes all dimensions.
733     //      The dimension index starts at 0. An error must be reported if squeezing a dimension that
734     //      is not 1.
735
736     // Add mandatory input index
737     OperandIndexSequence inputs{init_param.inputs[0]};
738
739     // Add dims index if specified
740     operation::Squeeze::Param param{};
741     if (init_param.input_count == 2)
742     {
743       auto squeeze_dims_idx = OperandIndex{init_param.inputs[1]};
744       assert(operands.at(squeeze_dims_idx).shape().rank() == 1);
745       assert(operands.at(squeeze_dims_idx).shape().dim(0) >= 0);
746       assert(static_cast<uint32_t>(operands.at(squeeze_dims_idx).shape().dim(0)) <=
747              sizeof(param.dims));
748       param.ndim = operands.at(squeeze_dims_idx).shape().dim(0);
749       if (param.ndim > 0)
750       {
751         assert(operands.at(squeeze_dims_idx).data());
752         memcpy(param.dims, operands.at(squeeze_dims_idx).data()->base(),
753                param.ndim * sizeof(param.dims[0]));
754       }
755     }
756
757     return new operation::Squeeze{inputs, outputs, param};
758   };
759
760   _map[ANEURALNETWORKS_TANH] = getElementwiseActivationGenerator(
761       onert::ir::operation::ElementwiseActivation::Type::TANH, 1.f, 1.f);
762
763   _map[ANEURALNETWORKS_LOG] = getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::LOG);
764
765   _map[ANEURALNETWORKS_LOGISTIC] = getElementwiseActivationGenerator(
766       onert::ir::operation::ElementwiseActivation::Type::LOGISTIC);
767
768   _map[ANEURALNETWORKS_DIV] =
769       getBinaryArithmeticGenerator(onert::ir::operation::BinaryArithmetic::ArithmeticType::DIV);
770
771   _map[ANEURALNETWORKS_EXP] = getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::EXP);
772
773   // ANEURALNETWORKS_EXP_EX is deprecated
774   // TODO Remove ANEURALNETWORKS_EXP_EX
775   _map[ANEURALNETWORKS_EXP_EX] = _map[ANEURALNETWORKS_EXP];
776
777   // Each input should be interpreted as follows:
778   //  0 -> Input Tensor Index
779   //  1 -> Axis Tensor Index
780   _map[ANEURALNETWORKS_EXPAND_DIMS] = createSimpleBinaryOp<operation::ExpandDims>;
781
782   _map[ANEURALNETWORKS_GREATER] =
783       getComparisonGenerator(operation::Comparison::ComparisonType::Greater);
784   _map[ANEURALNETWORKS_GREATER_EQUAL] =
785       getComparisonGenerator(operation::Comparison::ComparisonType::GreaterEqual);
786   _map[ANEURALNETWORKS_LESS] = getComparisonGenerator(operation::Comparison::ComparisonType::Less);
787   _map[ANEURALNETWORKS_LESS_EQUAL] =
788       getComparisonGenerator(operation::Comparison::ComparisonType::LessEqual);
789   _map[ANEURALNETWORKS_NOT_EQUAL] =
790       getComparisonGenerator(operation::Comparison::ComparisonType::NotEqual);
791   _map[ANEURALNETWORKS_EQUAL] =
792       getComparisonGenerator(operation::Comparison::ComparisonType::Equal);
793
794   // ANEURALNETWORKS_GREATER_EQUAL_EX is deprecated
795   // TODO Remove ANEURALNETWORKS_GREATER_EQUAL_EX
796   _map[ANEURALNETWORKS_GREATER_EQUAL_EX] = [](const OperationFactory::Param &init_param,
797                                               Operands &operands) {
798     assert(init_param.input_count == 2 && init_param.output_count == 1);
799
800     OperandIndexSequence outputs{init_param.outputs[0]};
801
802     // Each input should be interpreted as follows:
803     //
804     //  0 -> input0 Tensor Index
805     //  1 -> input1 Tensor Index
806     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
807
808     operation::Comparison::Param param;
809     param.comparison_type = operation::Comparison::ComparisonType::GreaterEqual;
810
811     // Output operand type must be boolean
812     replaceDataType(operands, outputs.at(0), DataType::BOOL8);
813
814     return new operation::Comparison{inputs, outputs, param};
815   };
816
817   // ANEURALNETWORKS_LESS_EX is deprecated
818   // TODO Remove ANEURALNETWORKS_LESS_EX
819   _map[ANEURALNETWORKS_LESS_EX] = [](const OperationFactory::Param &init_param,
820                                      Operands &operands) {
821     assert(init_param.input_count == 2 && init_param.output_count == 1);
822
823     OperandIndexSequence outputs{init_param.outputs[0]};
824
825     // Each input should be interpreted as follows:
826     //
827     //  0 -> input0 Tensor Index
828     //  1 -> input1 Tensor Index
829     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
830
831     operation::Comparison::Param param;
832     param.comparison_type = operation::Comparison::ComparisonType::Less;
833
834     // Output operand type must be boolean
835     replaceDataType(operands, outputs.at(0), DataType::BOOL8);
836
837     return new operation::Comparison{inputs, outputs, param};
838   };
839
840   _map[ANEURALNETWORKS_REDUCE_ALL] =
841       getReduceGenerator(onert::ir::operation::Reduce::ReduceType::ALL);
842
843   _map[ANEURALNETWORKS_REDUCE_ANY] =
844       getReduceGenerator(onert::ir::operation::Reduce::ReduceType::ANY);
845
846   _map[ANEURALNETWORKS_REDUCE_MAX] =
847       getReduceGenerator(onert::ir::operation::Reduce::ReduceType::MAX);
848
849   // ANEURALNETWORKS_REDUCE_MAX_EX is deprecated
850   // TODO Remove ANEURALNETWORKS_REDUCE_MAX_EX
851   _map[ANEURALNETWORKS_REDUCE_MAX_EX] = _map[ANEURALNETWORKS_REDUCE_MAX];
852
853   // ANEURALNETWORKS_NOT_EQUAL_EX is deprecated
854   // TODO Remove ANEURALNETWORKS_NOT_EQUAL_EX
855   _map[ANEURALNETWORKS_NOT_EQUAL_EX] = [](const OperationFactory::Param &init_param,
856                                           Operands &operands) {
857     assert(init_param.input_count == 2 && init_param.output_count == 1);
858
859     OperandIndexSequence outputs{init_param.outputs[0]};
860
861     // Each input should be interpreted as follows:
862     //
863     //  0 -> input1 Tensor Index
864     //  1 -> input2 Tensor Index
865     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
866
867     operation::Comparison::Param param;
868     param.comparison_type = operation::Comparison::ComparisonType::NotEqual;
869
870     // Output operand type must be boolean
871     replaceDataType(operands, outputs.at(0), DataType::BOOL8);
872
873     return new operation::Comparison{inputs, outputs, param};
874   };
875
876   _map[ANEURALNETWORKS_LOGICAL_AND] = getElementwiseBinaryGenerator(
877       operation::ElementwiseBinary::ElementwiseBinaryType::LOGICAL_AND);
878
879   // ANEURALNETWORKS_LOGICAL_AND_EX is deprecated
880   // TODO Remove ANEURALNETWORKS_LOGICAL_AND_EX
881   _map[ANEURALNETWORKS_LOGICAL_AND_EX] = [](const OperationFactory::Param &init_param,
882                                             Operands &operands) {
883     assert(init_param.input_count == 2 && init_param.output_count == 1);
884
885     OperandIndexSequence outputs{init_param.outputs[0]};
886
887     // Each input should be interpreted as follows:
888     //
889     //  0 -> input0 Tensor Index
890     //  1 -> input1 Tensor Index
891     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
892
893     // This operation's operands must be boolean type.
894     replaceDataType(operands, inputs.at(0), DataType::BOOL8);
895     replaceDataType(operands, inputs.at(1), DataType::BOOL8);
896     replaceDataType(operands, outputs.at(0), DataType::BOOL8);
897
898     operation::ElementwiseBinary::Param param;
899     param.op_type = operation::ElementwiseBinary::ElementwiseBinaryType::LOGICAL_AND;
900
901     return new operation::ElementwiseBinary{inputs, outputs, param};
902   };
903
904   _map[ANEURALNETWORKS_RSQRT] =
905       getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::RSQRT);
906
907   _map[ANEURALNETWORKS_SELECT] = [](const OperationFactory::Param &init_param, Operands &) {
908     assert(init_param.input_count == 3 && init_param.output_count == 1);
909
910     OperandIndexSequence outputs{init_param.outputs[0]};
911
912     // Each input should be interpreted as follows:
913     //
914     //  0 -> Condition Tensor Index
915     //  1 -> Input X(true) Tensor Index
916     //  2 -> Input Y(false) Tensor Index
917     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
918
919     return new operation::Select{inputs, outputs};
920   };
921
922   _map[ANEURALNETWORKS_SELECT_V2_EX] = [](const OperationFactory::Param &init_param, Operands &) {
923     assert(init_param.input_count == 3 && init_param.output_count == 1);
924
925     OperandIndexSequence outputs{init_param.outputs[0]};
926
927     // Each input should be interpreted as follows:
928     //
929     //  0 -> Condition Tensor Index
930     //  1 -> Input X(true) Tensor Index
931     //  2 -> Input Y(false) Tensor Index
932     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
933
934     return new operation::Select{inputs, outputs};
935   };
936
937   // ANEURALNETWORKS_RSQRT_EX is deprecated
938   // TODO Remove ANEURALNETWORKS_RSQRT_EX
939   _map[ANEURALNETWORKS_RSQRT_EX] = _map[ANEURALNETWORKS_RSQRT];
940
941   _map[ANEURALNETWORKS_RELU] =
942       getElementwiseActivationGenerator(onert::ir::operation::ElementwiseActivation::Type::RELU,
943                                         onert::ir::operation::ElementwiseActivation::infinity, 0);
944
945   _map[ANEURALNETWORKS_RESIZE_BILINEAR] = [](const OperationFactory::Param &init_param,
946                                              Operands &operands) {
947     assert(init_param.input_count == 3 && init_param.output_count == 1);
948
949     OperandIndexSequence outputs{init_param.outputs[0]};
950
951     // Each input should be interpreted as follows:
952     //
953     //  0 -> IFM Index
954     //  1 -> Height Index
955     //  2 -> Width Index
956     OperandIndexSequence inputs{init_param.inputs[0]};
957
958     operation::ResizeBilinear::Param param;
959     param.height_out = operands.at(OperandIndex{init_param.inputs[1]}).asScalar<int32_t>();
960     param.width_out = operands.at(OperandIndex{init_param.inputs[2]}).asScalar<int32_t>();
961     param.align_corners = false;
962     param.half_pixel_centers = false;
963     return new operation::ResizeBilinear{inputs, outputs, param};
964   };
965
966   _map[ANEURALNETWORKS_RESIZE_NEAREST_NEIGHBOR] = [](const OperationFactory::Param &init_param,
967                                                      Operands &operands) {
968     assert((init_param.input_count == 3 || init_param.input_count == 4) &&
969            init_param.output_count == 1);
970
971     OperandIndexSequence outputs{init_param.outputs[0]};
972
973     // Each input should be interpreted as follows:
974     //
975     //  0 -> IFM Index
976     //  1 -> Height Index
977     //  2 -> Width Index
978     OperandIndexSequence inputs{init_param.inputs[0]};
979
980     operation::ResizeNearestNeighbor::Param param;
981     param.height_out = operands.at(OperandIndex{init_param.inputs[1]}).asScalar<int32_t>();
982     param.width_out = operands.at(OperandIndex{init_param.inputs[2]}).asScalar<int32_t>();
983     param.align_corners = false;
984     // The layout input is not supported yet
985     return new operation::ResizeNearestNeighbor{inputs, outputs, param};
986   };
987
988   _map[ANEURALNETWORKS_RELU1] = getElementwiseActivationGenerator(
989       onert::ir::operation::ElementwiseActivation::Type::RELU, 1.f, -1.f);
990
991   _map[ANEURALNETWORKS_RELU6] = getElementwiseActivationGenerator(
992       onert::ir::operation::ElementwiseActivation::Type::RELU, 6.f, 0.f);
993
994   _map[ANEURALNETWORKS_REVERSE_EX] = [](const OperationFactory::Param &init_param, Operands &) {
995     assert(init_param.input_count == 2 && init_param.output_count == 1);
996
997     // Each input should be interpreted as follows:
998     //
999     // 0 -> Input Tensor Index
1000     // 1 -> Axis Tensor Index
1001
1002     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
1003     OperandIndexSequence outputs{init_param.outputs[0]};
1004
1005     return new operation::Reverse{inputs, outputs};
1006   };
1007
1008   _map[ANEURALNETWORKS_RNN] = [](const OperationFactory::Param &init_param, Operands &operands) {
1009     assert(init_param.input_count == 6 && init_param.output_count == 2);
1010
1011     // Each input should be interpreted as follows:
1012     //
1013     // 0 -> Input Tensor Index
1014     // 1 -> Weights Tensor Index
1015     // 2 -> Recurrent Weights Tensor Index
1016     // 3 -> Bias Tensor Index
1017     // 4 -> Hidden state (in) Index
1018     // 5 -> Activation Index
1019
1020     OperandIndexSequence inputs;
1021     for (uint32_t n = 0; n < init_param.input_count - 1; ++n)
1022     {
1023       inputs.append(OperandIndex{init_param.inputs[n]});
1024     }
1025     OperandIndexSequence outputs;
1026     for (uint32_t n = 0; n < init_param.output_count; ++n)
1027     {
1028       outputs.append(OperandIndex{init_param.outputs[n]});
1029     }
1030
1031     operation::RNN::Param param;
1032     const auto activation_index = OperandIndex{init_param.inputs[5]};
1033     param.activation =
1034         NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
1035
1036     return new operation::RNN{inputs, outputs, param};
1037   };
1038
1039   _map[ANEURALNETWORKS_FLOOR] =
1040       getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::FLOOR);
1041
1042   _map[ANEURALNETWORKS_SPACE_TO_BATCH_ND] = [](const OperationFactory::Param &init_param,
1043                                                Operands &) {
1044     assert(init_param.input_count == 3 && init_param.output_count == 1);
1045
1046     OperandIndexSequence outputs{init_param.outputs[0]};
1047
1048     // Each input should be interpreted as follows:
1049     //
1050     //  0 -> Input Tensor Index
1051     //  1 -> Block size Index
1052     //  2 -> Paddings Index
1053     OperandIndexSequence inputs;
1054     for (uint32_t n = 0; n < init_param.input_count; ++n)
1055     {
1056       inputs.append(OperandIndex{init_param.inputs[n]});
1057     }
1058
1059     return new operation::SpaceToBatchND{inputs, outputs};
1060   };
1061
1062   _map[ANEURALNETWORKS_SPACE_TO_DEPTH] = [](const OperationFactory::Param &init_param,
1063                                             Operands &operands) {
1064     assert(init_param.input_count == 2 && init_param.output_count == 1);
1065
1066     OperandIndexSequence outputs{init_param.outputs[0]};
1067
1068     // Each input should be interpreted as follows:
1069     //
1070     //  0 -> Input Tensor Index
1071     //  1 -> Block size Index
1072     OperandIndexSequence inputs{init_param.inputs[0]};
1073
1074     operation::SpaceToDepth::Param param;
1075     param.block_size = operands.at(OperandIndex{init_param.inputs[1]}).asScalar<std::int32_t>();
1076
1077     return new operation::SpaceToDepth{inputs, outputs, param};
1078   };
1079
1080   _map[ANEURALNETWORKS_L2_POOL_2D] = getPool2DGenerator(operation::Pool2D::PoolType::L2);
1081
1082   _map[ANEURALNETWORKS_EMBEDDING_LOOKUP] = [](const OperationFactory::Param &init_param,
1083                                               Operands &) {
1084     assert(init_param.input_count == 2 && init_param.output_count == 1);
1085
1086     OperandIndexSequence outputs{init_param.outputs[0]};
1087
1088     // Each input should be interpreted as follows:
1089     //
1090     //  0 -> Lookups Index
1091     //  1 -> Values Index
1092     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
1093
1094     return new operation::EmbeddingLookup{inputs, outputs};
1095   };
1096
1097   _map[ANEURALNETWORKS_L2_NORMALIZATION] = [](const OperationFactory::Param &init_param,
1098                                               Operands &) {
1099     assert(init_param.input_count == 1 && init_param.output_count == 1);
1100
1101     OperandIndexSequence outputs{init_param.outputs[0]};
1102
1103     // Each input should be interpreted as follows:
1104     //  0 -> input Tensor Index
1105     OperandIndexSequence inputs{init_param.inputs[0]};
1106
1107     return new operation::L2Normalization{inputs, outputs};
1108   };
1109
1110   _map[ANEURALNETWORKS_HASHTABLE_LOOKUP] = [](const OperationFactory::Param &init_param,
1111                                               Operands &) {
1112     assert(init_param.input_count == 3 && init_param.output_count == 2);
1113
1114     // Each output should be interpreted as follows:
1115     //
1116     //  0 -> Output Index
1117     //  1 -> Hits Index
1118     OperandIndexSequence outputs{init_param.outputs[0], init_param.outputs[1]};
1119
1120     // Each input should be interpreted as follows:
1121     //
1122     //  0 -> Lookups Index
1123     //  1 -> Keys Index
1124     //  2 -> Values Index
1125     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
1126
1127     return new operation::HashtableLookup{inputs, outputs};
1128   };
1129
1130   _map[ANEURALNETWORKS_PRELU] = [](const OperationFactory::Param &init_param, Operands &) {
1131     assert(init_param.input_count == 2 && init_param.output_count == 1);
1132
1133     OperandIndexSequence outputs{init_param.outputs[0]};
1134
1135     // Each input should be interpreted as follows:
1136     //
1137     //  0 -> input Tensor Index
1138     //  1 -> alpha Tensor Index
1139     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
1140
1141     return new operation::PReLU{inputs, outputs};
1142   };
1143
1144   // ANEURALNETWORKS_PRELU_EX is deprecated
1145   // TODO Remove ANEURALNETWORKS_PRELU_EX
1146   _map[ANEURALNETWORKS_PRELU_EX] = _map[ANEURALNETWORKS_PRELU];
1147
1148   _map[ANEURALNETWORKS_TRANSPOSE_CONV_EX] = [](const OperationFactory::Param &init_param,
1149                                                Operands &operands) {
1150     assert(init_param.input_count == 6 && init_param.output_count == 1);
1151
1152     OperandIndexSequence outputs{init_param.outputs[0]};
1153
1154     // Each input should be interpreted as follows:
1155     //
1156     //  0 -> Output Shape Index
1157     //  1 -> Weights Index
1158     //  2 -> Input Tensor Index
1159     //  3 -> Padding Type
1160     //  4 -> Stride width
1161     //  5 -> Stride height
1162
1163     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
1164
1165     operation::TransposeConv::Param param;
1166
1167     const auto padding_index = OperandIndex{init_param.inputs[3]};
1168     const auto hstride_index = OperandIndex{init_param.inputs[4]};
1169     const auto vstride_index = OperandIndex{init_param.inputs[5]};
1170
1171     param.padding.type =
1172         NNAPIConvert::getPaddingType(operands.at(padding_index).asScalar<PaddingCode>());
1173     param.stride = makeStride(operands, hstride_index, vstride_index);
1174
1175     return new operation::TransposeConv{inputs, outputs, param};
1176   };
1177
1178   _map[ANEURALNETWORKS_SQRT] =
1179       getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::SQRT);
1180
1181   // ANEURALNETWORKS_SQRT_EX is deprecated
1182   // TODO Remove ANEURALNETWORKS_SQRT_EX
1183   _map[ANEURALNETWORKS_SQRT_EX] = _map[ANEURALNETWORKS_SQRT];
1184
1185   _map[ANEURALNETWORKS_LOGICAL_OR] = getElementwiseBinaryGenerator(
1186       operation::ElementwiseBinary::ElementwiseBinaryType::LOGICAL_OR);
1187
1188   // ANEURALNETWORKS_LOGICAL_OR_EX is deprecated
1189   // TODO Remove ANEURALNETWORKS_LOGICAL_OR_EX
1190   _map[ANEURALNETWORKS_LOGICAL_OR_EX] = [](const OperationFactory::Param &init_param,
1191                                            Operands &operands) {
1192     assert(init_param.input_count == 2 && init_param.output_count == 1);
1193
1194     OperandIndexSequence outputs{init_param.outputs[0]};
1195
1196     // Each input should be interpreted as follows:
1197     //
1198     //  0 -> input0 Tensor Index
1199     //  1 -> input1 Tensor Index
1200     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
1201
1202     // This operation's operands must be boolean type.
1203     replaceDataType(operands, inputs.at(0), DataType::BOOL8);
1204     replaceDataType(operands, inputs.at(1), DataType::BOOL8);
1205     replaceDataType(operands, outputs.at(0), DataType::BOOL8);
1206
1207     operation::ElementwiseBinary::Param param;
1208     param.op_type = operation::ElementwiseBinary::ElementwiseBinaryType::LOGICAL_OR;
1209
1210     return new operation::ElementwiseBinary{inputs, outputs, param};
1211   };
1212
1213   _map[ANEURALNETWORKS_LOGICAL_NOT] =
1214       getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::LOGICAL_NOT);
1215
1216   // ANEURALNETWORKS_LOGICAL_NOT_EX is deprecated
1217   // TODO Remove ANEURALNETWORKS_LOGICAL_NOT_EX
1218   _map[ANEURALNETWORKS_LOGICAL_NOT_EX] = [](const OperationFactory::Param &init_param,
1219                                             Operands &operands) {
1220     assert(init_param.input_count == 1 && init_param.output_count == 1);
1221
1222     OperandIndexSequence outputs{init_param.outputs[0]};
1223
1224     // Each input should be interpreted as follows:
1225     //
1226     //  0 -> input Tensor Index
1227     OperandIndexSequence inputs{init_param.inputs[0]};
1228
1229     // This operation's operands must be boolean type.
1230     replaceDataType(operands, inputs.at(0), DataType::BOOL8);
1231     replaceDataType(operands, outputs.at(0), DataType::BOOL8);
1232
1233     operation::ElementwiseUnary::Param param;
1234     param.op_type = operation::ElementwiseUnary::Type::LOGICAL_NOT;
1235
1236     return new operation::ElementwiseUnary{inputs, outputs, param};
1237   };
1238
1239   _map[ANEURALNETWORKS_LSTM] = [](const OperationFactory::Param &init_param, Operands &operands) {
1240     assert(init_param.input_count == 23 && init_param.output_count == 4);
1241
1242     // Each input should be interpreted as follows:
1243     //
1244     // 0 -> Input Tensor Index
1245     // 1 -> Input to Input Tensor Index
1246     // 2 -> Input to Forget Tensor Index
1247     // 3 -> Input to Cell Tensor Index
1248     // 4 -> Input to Output Tensor Index
1249     // 5 -> Recurrent to Input Weights Tensor Index
1250     // 6 -> Recurrent to Forget Weights Tensor Index
1251     // 7 -> Recurrent to Cell Weights Tensor Index
1252     // 8 -> Recurrent to Output Weights Tensor Index
1253     // 9 -> Cell to Input Weights Tensor Index
1254     // 10 -> Cell to Forget Weights Tensor Index
1255     // 11 -> Cell to Output Weights Tensor Index
1256     // 12 -> Input Gate Bias Tensor Index
1257     // 13 -> Forget Gate Bias Tensor Index
1258     // 14 -> Cell Bias Tensor Index
1259     // 15 -> Output Gate Bias Tensor Index
1260     // 16 -> Projection Weights Tensor Index
1261     // 17 -> Projection Bias Tensor Index
1262     // 18 -> Output State In Tensor Index
1263     // 19 -> Cell State In Tensor Index
1264     OperandIndexSequence inputs;
1265     for (uint32_t n = 0; n < init_param.input_count - 3; ++n)
1266     {
1267       inputs.append(OperandIndex{init_param.inputs[n]});
1268     }
1269
1270     // Each output should be interpreted as follows:
1271     //
1272     // 0 -> Scratch Buffer Tensor Index
1273     // 1 -> Output State Out Tensor Index
1274     // 2 -> Cell State Out Tensor Index
1275     // 3 -> Output Tensor Index
1276     OperandIndexSequence outputs;
1277     for (uint32_t n = 0; n < init_param.output_count; ++n)
1278     {
1279       outputs.append(OperandIndex{init_param.outputs[n]});
1280     }
1281
1282     operation::LSTM::Param param;
1283     const auto activation_index = OperandIndex{init_param.inputs[20]};
1284     switch (operands.at(activation_index).asScalar<int32_t>())
1285     {
1286       case 0:
1287         param.activation = Activation::NONE;
1288         break;
1289       case 1:
1290         param.activation = Activation::RELU;
1291         break;
1292       case 2:
1293         param.activation = Activation::RELU1;
1294         break;
1295       case 3:
1296         param.activation = Activation::RELU6;
1297         break;
1298       case 4:
1299         param.activation = Activation::TANH;
1300         break;
1301       case 6:
1302         param.activation = Activation::SIGMOID;
1303         break;
1304       default:
1305         throw std::runtime_error("Unsupported activation type");
1306         break;
1307     }
1308     param.cell_threshold = operands.at(OperandIndex{init_param.inputs[21]}).asScalar<float>();
1309     param.projection_threshold = operands.at(OperandIndex{init_param.inputs[22]}).asScalar<float>();
1310     // This is initialization to prevent warning or error by static code analyzer. LSTM operation
1311     // does not need time_major
1312     param.time_major = false;
1313
1314     return new operation::LSTM{inputs, outputs, param};
1315   };
1316
1317   _map[ANEURALNETWORKS_UNIDIRECTIONAL_SEQUENCE_LSTM] = [](const OperationFactory::Param &init_param,
1318                                                           Operands &operands) {
1319     assert((init_param.input_count >= 24 || init_param.input_count <= 28) &&
1320            (init_param.output_count >= 1 && init_param.output_count <= 3));
1321
1322     // Each input should be interpreted as follows:
1323     //
1324     // 0 -> Input Tensor Index
1325     // 1 -> Input to Input Tensor Index
1326     // 2 -> Input to Forget Tensor Index
1327     // 3 -> Input to Cell Tensor Index
1328     // 4 -> Input to Output Tensor Index
1329     // 5 -> Recurrent to Input Weights Tensor Index
1330     // 6 -> Recurrent to Forget Weights Tensor Index
1331     // 7 -> Recurrent to Cell Weights Tensor Index
1332     // 8 -> Recurrent to Output Weights Tensor Index
1333     // 9 -> Cell to Input Weights Tensor Index
1334     // 10 -> Cell to Forget Weights Tensor Index
1335     // 11 -> Cell to Output Weights Tensor Index
1336     // 12 -> Input Gate Bias Tensor Index
1337     // 13 -> Forget Gate Bias Tensor Index
1338     // 14 -> Cell Bias Tensor Index
1339     // 15 -> Output Gate Bias Tensor Index
1340     // 16 -> Projection Weights Tensor Index
1341     // 17 -> Projection Bias Tensor Index
1342     // 18 -> Output State In Tensor Index
1343     // 19 -> Cell State In Tensor Index
1344     assert(init_param.input_count - 3 > 20);
1345     OperandIndexSequence inputs;
1346     for (uint32_t n = 0; n < 20; ++n)
1347     {
1348       inputs.append(OperandIndex{init_param.inputs[n]});
1349     }
1350
1351     // 24 -> Input Layer Normalization Weights Tensor Index
1352     // 25 -> Forget Layer Normalization Weights Tensor Index
1353     // 26 -> Cell Layer Normalization Weights Tensor Index
1354     // 27 -> Output Layer Normalization Weights Tensor Index
1355     if (init_param.input_count > 24)
1356     {
1357       for (uint32_t n = 24; n < 28; ++n)
1358       {
1359         if (init_param.input_count > n)
1360         {
1361           inputs.append(OperandIndex{init_param.inputs[n]});
1362         }
1363       }
1364     }
1365
1366     // Each output should be interpreted as follows:
1367     //
1368     // 0 -> Output Tensor Index -> 3
1369     // 1 -> Output State Out Tensor Index
1370     // 2 -> Cell State Out Tensor Index
1371     const OperandIndex scratch_buffer_index;
1372     OperandIndex output_state_index =
1373         init_param.output_count >= 2 ? OperandIndex{init_param.outputs[1]} : OperandIndex();
1374     OperandIndex cell_state_index =
1375         init_param.output_count >= 3 ? OperandIndex{init_param.outputs[2]} : OperandIndex();
1376     const OperandIndex output_index = OperandIndex{init_param.outputs[0]};
1377     OperandIndexSequence outputs{scratch_buffer_index, output_state_index, cell_state_index,
1378                                  output_index};
1379
1380     operation::LSTM::Param param;
1381     const auto activation_index = OperandIndex{init_param.inputs[20]};
1382     switch (operands.at(activation_index).asScalar<int32_t>())
1383     {
1384       case 0:
1385         param.activation = Activation::NONE;
1386         break;
1387       case 1:
1388         param.activation = Activation::RELU;
1389         break;
1390       case 2:
1391         param.activation = Activation::RELU1;
1392         break;
1393       case 3:
1394         param.activation = Activation::RELU6;
1395         break;
1396       case 4:
1397         param.activation = Activation::TANH;
1398         break;
1399       case 6:
1400         param.activation = Activation::SIGMOID;
1401         break;
1402       default:
1403         throw std::runtime_error("Unsupported activation type");
1404         break;
1405     }
1406     param.cell_threshold = operands.at(OperandIndex{init_param.inputs[21]}).asScalar<float>();
1407     param.projection_threshold = operands.at(OperandIndex{init_param.inputs[22]}).asScalar<float>();
1408     param.time_major = operands.at(OperandIndex{init_param.inputs[23]}).asScalar<bool>();
1409
1410     return new operation::LSTM{inputs, outputs, param};
1411   };
1412
1413   // ANEURALNETWORKS_EQUAL_EX is deprecated
1414   // TODO Remove ANEURALNETWORKS_EQUAL_EX
1415   _map[ANEURALNETWORKS_EQUAL_EX] = [](const OperationFactory::Param &init_param,
1416                                       Operands &operands) {
1417     assert(init_param.input_count == 2 && init_param.output_count == 1);
1418
1419     OperandIndexSequence outputs{init_param.outputs[0]};
1420
1421     // Each input should be interpreted as follows:
1422     //
1423     //  0 -> input0 Tensor Index
1424     //  1 -> input1 Tensor Index
1425     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
1426
1427     operation::Comparison::Param param;
1428     param.comparison_type = operation::Comparison::ComparisonType::Equal;
1429
1430     // Output operand type must be boolean
1431     replaceDataType(operands, outputs.at(0), DataType::BOOL8);
1432
1433     return new operation::Comparison{inputs, outputs, param};
1434   };
1435
1436   _map[ANEURALNETWORKS_SQUARED_DIFFERENCE_EX] = [](const OperationFactory::Param &init_param,
1437                                                    Operands &) {
1438     assert(init_param.input_count == 2 && init_param.output_count == 1);
1439
1440     OperandIndexSequence outputs{init_param.outputs[0]};
1441
1442     // Each input should be interpreted as follows:
1443     //
1444     //  0 -> LHS Tensor Index
1445     //  1 -> RHS Tensor Index
1446     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
1447
1448     return new operation::SquaredDifference{inputs, outputs};
1449   };
1450
1451   _map[ANEURALNETWORKS_TOPK_V2] = [](const OperationFactory::Param &init_param,
1452                                      Operands &operands) {
1453     assert(init_param.input_count == 2 && init_param.output_count == 2);
1454
1455     // Each output should be interpreted as follows:
1456     //
1457     //  0 -> Index for Output Values
1458     //  1 -> Index for Output Indices
1459     OperandIndexSequence outputs{init_param.outputs[0], init_param.outputs[1]};
1460
1461     // Each input should be interpreted as follows:
1462     //
1463     //  0 -> Index for Input Data
1464     //  1 -> Index for K
1465     OperandIndexSequence inputs{init_param.inputs[0]};
1466
1467     operation::TopKV2::Param param;
1468     param.k = operands.at(OperandIndex{init_param.inputs[1]}).asScalar<std::int32_t>();
1469
1470     return new operation::TopKV2{inputs, outputs, param};
1471   };
1472
1473   // ANEURALNETWORKS_CAST_EX is deprecated
1474   // TODO Remove ANEURALNETWORKS_CAST_EX
1475   _map[ANEURALNETWORKS_TOPK_V2_EX] = _map[ANEURALNETWORKS_TOPK_V2];
1476
1477   _map[ANEURALNETWORKS_GATHER] = [](const OperationFactory::Param &init_param, Operands &operands) {
1478     assert(init_param.input_count == 3 && init_param.output_count == 1);
1479
1480     OperandIndexSequence outputs{init_param.outputs[0]};
1481
1482     // Each input should be interpreted as follows:
1483     //
1484     //  0 -> input Tensor Index
1485     //  1 -> axis Index
1486     //  2 -> indices Tensor Index
1487     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[2]};
1488
1489     operation::Gather::Param param;
1490     param.axis = operands.at(OperandIndex{init_param.inputs[1]}).asScalar<int32_t>();
1491
1492     return new operation::Gather{inputs, outputs, param};
1493   };
1494
1495   // ANEURALNETWORKS_GATHER_EX is deprecated
1496   // TODO Remove ANEURALNETWORKS_GATHER_EX
1497   _map[ANEURALNETWORKS_GATHER_EX] = _map[ANEURALNETWORKS_GATHER];
1498
1499   _map[ANEURALNETWORKS_NEG] = getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::NEG);
1500
1501   // ANEURALNETWORKS_NEG_EX is deprecated
1502   // TODO Remove ANEURALNETWORKS_NEG_EX
1503   _map[ANEURALNETWORKS_NEG_EX] = _map[ANEURALNETWORKS_NEG];
1504
1505   _map[ANEURALNETWORKS_ABS] = getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::ABS);
1506
1507   // ANEURALNETWORKS_ABS_EX is deprecated
1508   // TODO Remove ANEURALNETWORKS_ABS_EX
1509   _map[ANEURALNETWORKS_ABS_EX] = _map[ANEURALNETWORKS_ABS];
1510
1511   _map[ANEURALNETWORKS_ARGMAX] = [](const OperationFactory::Param &init_param, Operands &) {
1512     assert(init_param.input_count == 2 && init_param.output_count == 1);
1513
1514     OperandIndexSequence outputs{init_param.outputs[0]};
1515
1516     // Each input should be interpreted as follows:
1517     //
1518     //  0 -> Input Tensor Index
1519     //  1 -> Axis Tensor Index
1520     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
1521
1522     operation::ArgMax::Param param;
1523     // NNAPI ARGMAX output type is always int32
1524     param.output_type = DataType::INT32;
1525
1526     return new operation::ArgMax{inputs, outputs, param};
1527   };
1528
1529   // ANEURALNETWORKS_ARGMAX_EX is deprecated
1530   // TODO Remove ANEURALNETWORKS_ARGMAX_EX
1531   _map[ANEURALNETWORKS_ARGMAX_EX] = _map[ANEURALNETWORKS_ARGMAX];
1532
1533   _map[ANEURALNETWORKS_DEQUANTIZE] =
1534       getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::DEQUANTIZE);
1535
1536   _map[ANEURALNETWORKS_MEAN] = [](const OperationFactory::Param &init_param, Operands &operands) {
1537     assert(init_param.input_count == 3 && init_param.output_count == 1);
1538
1539     OperandIndexSequence outputs{init_param.outputs[0]};
1540
1541     // Each input should be interpreted as follows:
1542     //
1543     //  0 -> ifm Tensor Index
1544     //  1 -> axis Tensor Index
1545     //  2 -> keep_dims Index
1546     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
1547
1548     operation::Reduce::Param param;
1549     param.reduce_type = operation::Reduce::ReduceType::MEAN;
1550     param.keep_dims = operands.at(OperandIndex{init_param.inputs[2]}).asScalar<int32_t>() != 0;
1551
1552     return new operation::Reduce{inputs, outputs, param};
1553   };
1554
1555   _map[ANEURALNETWORKS_LOCAL_RESPONSE_NORMALIZATION] = [](const OperationFactory::Param &init_param,
1556                                                           Operands &operands) {
1557     assert(init_param.input_count == 5 && init_param.output_count == 1);
1558
1559     OperandIndexSequence outputs{init_param.outputs[0]};
1560
1561     OperandIndexSequence inputs{init_param.inputs[0]};
1562
1563     operation::LocalResponseNormalization::Param param;
1564     param.radius = operands.at(OperandIndex{init_param.inputs[1]}).asScalar<std::int32_t>();
1565     param.bias = operands.at(OperandIndex{init_param.inputs[2]}).asScalar<float>();
1566     param.alpha = operands.at(OperandIndex{init_param.inputs[3]}).asScalar<float>();
1567     param.beta = operands.at(OperandIndex{init_param.inputs[4]}).asScalar<float>();
1568
1569     return new operation::LocalResponseNormalization{inputs, outputs, param};
1570   };
1571
1572   _map[ANEURALNETWORKS_DEPTH_TO_SPACE] = [](const OperationFactory::Param &init_param,
1573                                             Operands &operands) {
1574     assert(init_param.input_count == 2 && init_param.output_count == 1);
1575
1576     OperandIndexSequence outputs{init_param.outputs[0]};
1577
1578     // Each input should be interpreted as follows:
1579     //
1580     //  0 -> Input Tensor Index
1581     //  1 -> Block size Index
1582     OperandIndexSequence inputs{init_param.inputs[0]};
1583
1584     operation::DepthToSpace::Param param;
1585     param.block_size = operands.at(OperandIndex{init_param.inputs[1]}).asScalar<std::int32_t>();
1586
1587     return new operation::DepthToSpace{inputs, outputs, param};
1588   };
1589
1590   _map[ANEURALNETWORKS_PACK_EX] = [](const OperationFactory::Param &init_param,
1591                                      Operands &operands) {
1592     assert(init_param.input_count >= 3 && init_param.output_count == 1);
1593
1594     OperandIndexSequence outputs{init_param.outputs[0]};
1595     OperandIndexSequence inputs;
1596     for (uint32_t n = 0; n < init_param.input_count - 2; ++n)
1597     {
1598       inputs.append(OperandIndex{init_param.inputs[n]});
1599     }
1600
1601     operation::Pack::Param param;
1602     const auto num_index = OperandIndex{init_param.inputs[init_param.input_count - 2]};
1603     const auto axis_index = OperandIndex{init_param.inputs[init_param.input_count - 1]};
1604     param.num = operands.at(num_index).asScalar<int32_t>();
1605     param.axis = operands.at(axis_index).asScalar<int32_t>();
1606
1607     return new operation::Pack{inputs, outputs, param};
1608   };
1609
1610   _map[ANEURALNETWORKS_REDUCE_MIN] =
1611       getReduceGenerator(onert::ir::operation::Reduce::ReduceType::MIN);
1612
1613   // ANEURALNETWORKS_REDUCE_MIN_EX is deprecated
1614   // TODO Remove ANEURALNETWORKS_REDUCE_MIN_EX
1615   _map[ANEURALNETWORKS_REDUCE_MIN_EX] = _map[ANEURALNETWORKS_REDUCE_MIN];
1616
1617   _map[ANEURALNETWORKS_SPLIT] = [](const OperationFactory::Param &init_param, Operands &operands) {
1618     assert(init_param.input_count == 3);
1619     assert(init_param.output_count >= 1); // At least one output tensor and axis
1620
1621     OperandIndexSequence inputs{init_param.inputs[1], init_param.inputs[0]};
1622     OperandIndexSequence outputs;
1623     for (uint32_t n = 0; n < init_param.output_count; ++n)
1624     {
1625       outputs.append(OperandIndex{init_param.outputs[n]});
1626     }
1627
1628     operation::Split::Param param;
1629     param.num_splits = operands.at(OperandIndex{init_param.inputs[2]}).asScalar<std::int32_t>();
1630
1631     return new operation::Split{inputs, outputs, param};
1632   };
1633
1634   _map[ANEURALNETWORKS_SPLIT_V_EX] = [](const OperationFactory::Param &init_param,
1635                                         Operands &operands) {
1636     assert(init_param.input_count == 4);
1637     assert(init_param.output_count >= 1); // At least one output tensor and axis
1638
1639     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
1640     OperandIndexSequence outputs;
1641     for (uint32_t n = 0; n < init_param.output_count; ++n)
1642     {
1643       outputs.append(OperandIndex{init_param.outputs[n]});
1644     }
1645
1646     operation::SplitV::Param param;
1647     param.num_splits = operands.at(OperandIndex{init_param.inputs[3]}).asScalar<std::int32_t>();
1648     return new operation::SplitV{inputs, outputs, param};
1649   };
1650
1651   // ANEURALNETWORKS_SPLIT_EX is deprecated
1652   // TODO Remove ANEURALNETWORKS_SPLIT_EX
1653   _map[ANEURALNETWORKS_SPLIT_EX] = _map[ANEURALNETWORKS_SPLIT];
1654
1655   _map[ANEURALNETWORKS_UNPACK_EX] = [](const OperationFactory::Param &init_param,
1656                                        Operands &operands) {
1657     assert(init_param.input_count == 3 && init_param.output_count >= 1);
1658
1659     OperandIndexSequence inputs{init_param.inputs[0]};
1660     OperandIndexSequence outputs;
1661     for (uint32_t n = 0; n < init_param.output_count; ++n)
1662     {
1663       outputs.append(OperandIndex{init_param.outputs[n]});
1664     }
1665
1666     operation::Unpack::Param param;
1667     const auto num_index = OperandIndex{init_param.inputs[1]};
1668     const auto axis_index = OperandIndex{init_param.inputs[2]};
1669     param.num = operands.at(num_index).asScalar<int32_t>();
1670     param.axis = operands.at(axis_index).asScalar<int32_t>();
1671
1672     return new operation::Unpack{inputs, outputs, param};
1673   };
1674
1675   _map[ANEURALNETWORKS_PAD] = [](const OperationFactory::Param &init_param, Operands &) {
1676     assert(init_param.input_count >= 2 && init_param.input_count <= 3 &&
1677            init_param.output_count >= 1);
1678
1679     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
1680     if (init_param.input_count == 3)
1681     {
1682       inputs.append(OperandIndex{init_param.inputs[2]});
1683     }
1684     OperandIndexSequence outputs{init_param.outputs[0]};
1685
1686     return new operation::Pad{inputs, outputs};
1687   };
1688
1689   _map[ANEURALNETWORKS_PAD_V2] = _map[ANEURALNETWORKS_PAD];
1690
1691   _map[ANEURALNETWORKS_MINIMUM] =
1692       getElementwiseBinaryGenerator(operation::ElementwiseBinary::ElementwiseBinaryType::MIN);
1693
1694   _map[ANEURALNETWORKS_MAXIMUM] =
1695       getElementwiseBinaryGenerator(operation::ElementwiseBinary::ElementwiseBinaryType::MAX);
1696
1697   _map[ANEURALNETWORKS_ONE_HOT_EX] = [](const OperationFactory::Param &init_param,
1698                                         Operands &operands) {
1699     assert(init_param.input_count == 5);
1700     assert(init_param.output_count == 1);
1701     // Each input should be interpreted as follows:
1702     //
1703     // 0 -> indices tensor
1704     // 1 -> depth tensor
1705     // 2 -> on_value tensor
1706     // 3 -> off_value tensor
1707     // 4 -> axis scalar
1708     OperandIndexSequence inputs;
1709     for (uint32_t n = 0; n < init_param.input_count - 1; ++n)
1710     {
1711       inputs.append(OperandIndex{init_param.inputs[n]});
1712     }
1713     OperandIndexSequence outputs{init_param.outputs[0]};
1714
1715     operation::OneHot::Param param;
1716     param.axis = operands.at(OperandIndex{init_param.inputs[4]}).asScalar<std::int32_t>();
1717
1718     return new operation::OneHot{inputs, outputs, param};
1719   };
1720
1721   _map[ANEURALNETWORKS_COS_EX] =
1722       getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::COS);
1723
1724   _map[ANEURALNETWORKS_SIN] = getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::SIN);
1725
1726   _map[ANEURALNETWORKS_SHAPE_EX] = [](const OperationFactory::Param &init_param, Operands &) {
1727     assert(init_param.input_count == 1 && init_param.output_count == 1);
1728
1729     OperandIndexSequence inputs{init_param.inputs[0]};
1730     OperandIndexSequence outputs{init_param.outputs[0]};
1731
1732     return new operation::Shape{inputs, outputs};
1733   };
1734
1735   _map[ANEURALNETWORKS_REDUCE_PROD] =
1736       getReduceGenerator(onert::ir::operation::Reduce::ReduceType::PROD);
1737
1738   _map[ANEURALNETWORKS_ROUND_EX] =
1739       getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::ROUND);
1740
1741   _map[ANEURALNETWORKS_RANGE_EX] = [](const OperationFactory::Param &init_param, Operands &) {
1742     assert(init_param.input_count == 3 && init_param.output_count == 1);
1743
1744     OperandIndexSequence outputs{init_param.outputs[0]};
1745
1746     // Each input should be interpreted as follows:
1747     //  0 -> start Tensor Index
1748     //  1 -> limit Tensor Index
1749     //  2 -> delta Tensor Index
1750
1751     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
1752
1753     return new operation::Range{inputs, outputs};
1754   };
1755
1756   // Each input should be interpreted as follows:
1757   //  0 -> LHS Tensor Index
1758   //  1 -> RHS Tensor Index
1759   _map[ANEURALNETWORKS_POW] = createSimpleBinaryOp<operation::Pow>;
1760
1761   // Each input should be interpreted as follows:
1762   //  0 -> A tensor, specifying the input.
1763   //  1 -> A 1-D tensor, specifying the value
1764   _map[ANEURALNETWORKS_FILL_EX] = createSimpleBinaryOp<operation::Fill>;
1765
1766   _map[ANEURALNETWORKS_ZEROS_LIKE_EX] =
1767       getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::ZEROS_LIKE);
1768   // Each input should be interpreted as follows:
1769   //  0 -> Input Tensor Index
1770   //  1 -> Multiple Tensor Index
1771   _map[ANEURALNETWORKS_TILE] = createSimpleBinaryOp<operation::Tile>;
1772
1773   _map[ANEURALNETWORKS_MATRIX_BAND_PART_EX] = [](const OperationFactory::Param &init_param,
1774                                                  Operands &) {
1775     assert(init_param.input_count == 3);
1776     assert(init_param.output_count == 1);
1777     // Each input should be interpreted as follows:
1778     //
1779     // 0 -> A tensor, input
1780     // 1 -> A 0-D tensor, number of lower diagnonals to keep
1781     // 2 -> A 0-D tensor, number of upper diagnonals to keep
1782     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
1783     OperandIndexSequence outputs{init_param.outputs[0]};
1784
1785     return new operation::MatrixBandPart{inputs, outputs};
1786   };
1787
1788   _map[ANEURALNETWORKS_BATCH_MATMUL_EX] = [](const OperationFactory::Param &init_param,
1789                                              Operands &operands) {
1790     assert(init_param.input_count == 4 && init_param.output_count == 1);
1791
1792     OperandIndexSequence outputs{init_param.outputs[0]};
1793
1794     // Each input should be interpreted as follows:
1795     //
1796     //  0 -> Lhs Tensor Index
1797     //  1 -> Rhs Tensor Index
1798     //  2 -> adj_x boolean scalar Index
1799     //  3 -> adj_y boolean scalar Index
1800
1801     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
1802
1803     operation::BatchMatMul::Param param;
1804     param.adj_x = operands.at(OperandIndex{init_param.inputs[2]}).asScalar<bool>();
1805     param.adj_y = operands.at(OperandIndex{init_param.inputs[3]}).asScalar<bool>();
1806
1807     return new operation::BatchMatMul{inputs, outputs, param};
1808   };
1809
1810   _map[ANEURALNETWORKS_EINSUM_EX] = [](const OperationFactory::Param &init_param,
1811                                        Operands &operands) {
1812     // Each input should be interpreted as follows:
1813     //
1814     //  0....n - 1 -> n Input Tensors Index
1815     //  n -> equation
1816     assert(init_param.input_count >= 1 && init_param.output_count == 1);
1817
1818     OperandIndexSequence inputs;
1819     for (uint32_t n = 0; n < init_param.input_count - 1; ++n)
1820     {
1821       inputs.append(OperandIndex{init_param.inputs[n]});
1822     }
1823     OperandIndexSequence outputs{init_param.outputs[0]};
1824
1825     operation::Einsum::Param param;
1826     const OperandIndex equation_index{init_param.inputs[init_param.input_count - 1]};
1827     std::vector<char> equation_vector = operands.at(equation_index).asVector<char>();
1828     param.equation = std::string(equation_vector.begin(), equation_vector.end());
1829
1830     return new operation::Einsum{inputs, outputs, param};
1831   };
1832
1833   //  0 -> Input Tensor Index
1834   //  1 -> int32, int64, An 1-D int tensor Index
1835   _map[ANEURALNETWORKS_BROADCAST_TO_EX] = createSimpleBinaryOp<operation::BroadcastTo>;
1836
1837   _map[ANEURALNETWORKS_STATELESS_RANDOM_UNIFORM_EX] = [](const OperationFactory::Param &init_param,
1838                                                          Operands &) {
1839     assert(init_param.input_count == 2 && init_param.output_count == 1);
1840     OperandIndexSequence outputs{init_param.outputs[0]};
1841
1842     // Each input should be interpreted as follows:
1843     //
1844     //  0 -> Shape Tensor Index
1845     //  1 -> int32, int64, An 1-D int tensor Index
1846
1847     OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
1848
1849     return new operation::StatelessRandomUniform{inputs, outputs};
1850   };
1851
1852   _map[ANEURALNETWORKS_FUSED_BATCH_NORM_V3_EX] = [](const OperationFactory::Param &init_param,
1853                                                     Operands &operands) {
1854     // Each input should be interpreted as follows:
1855     //
1856     //  0....4  -> 5 Input Tensors Index
1857     //  n-2     -> is_training
1858     //  n-1     -> data_format
1859     //  n       -> epsilon
1860
1861     assert(init_param.input_count == 8 && init_param.output_count == 1);
1862
1863     OperandIndexSequence inputs;
1864     for (uint32_t n = 0; n < init_param.input_count - 3; ++n)
1865     {
1866       inputs.append(OperandIndex{init_param.inputs[n]});
1867     }
1868     OperandIndexSequence outputs{init_param.outputs[0]};
1869
1870     operation::FusedBatchNorm::Param param;
1871     const OperandIndex is_training_index{init_param.inputs[init_param.input_count - 3]};
1872     param.is_training = operands.at(is_training_index).asScalar<bool>();
1873
1874     const OperandIndex data_format_index{init_param.inputs[init_param.input_count - 2]};
1875     std::vector<char> data_format_vector = operands.at(data_format_index).asVector<char>();
1876     param.data_format = std::string(data_format_vector.begin(), data_format_vector.end());
1877
1878     const OperandIndex epsilon_index{init_param.inputs[init_param.input_count - 1]};
1879     param.epsilon = operands.at(epsilon_index).asScalar<float>();
1880     return new operation::FusedBatchNorm{inputs, outputs, param};
1881   };
1882
1883   _map[ANEURALNETWORKS_LOG_SOFTMAX] = [](const OperationFactory::Param &init_param,
1884                                          Operands &operands) {
1885     assert(init_param.input_count == 3 && init_param.output_count == 1);
1886
1887     // Each input should be interpreted as follows:
1888     //
1889     //  0 -> A tensor specifying the input logits.
1890     //  1 -> A scalar, specifying the positive scaling factor for the exponent, beta.
1891     //  2 -> An scalar specifying the axis to reduce across.
1892
1893     OperandIndexSequence inputs{init_param.inputs[0]};
1894     OperandIndexSequence outputs{init_param.outputs[0]};
1895
1896     const auto beta_index = OperandIndex{init_param.inputs[1]};
1897     const auto axis_index = OperandIndex{init_param.inputs[2]};
1898
1899     operation::LogSoftmax::Param param;
1900     param.beta = operands.at(beta_index).asScalar<float>();
1901     param.axis = operands.at(axis_index).asScalar<int>();
1902
1903     return new operation::LogSoftmax{inputs, outputs, param};
1904   };
1905
1906   _map[ANEURALNETWORKS_QUANTIZE] =
1907       getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::QUANTIZE);
1908 }
1909
1910 Operation *OperationFactory::create(ANeuralNetworksOperationType type,
1911                                     const OperationFactory::Param &param, Operands &operands)
1912 {
1913   auto it = _map.find(type);
1914   if (it == _map.end())
1915   {
1916     throw std::runtime_error("Unsupported operation type: " + std::to_string(type));
1917   }
1918   return it->second(param, operands);
1919 }