2 * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "OperationFactory.h"
18 #include "NNAPIConvert.h"
20 #include <ir/Operations.Include.h>
25 using namespace onert::ir;
27 void replaceDataType(Operands &operands, const OperandIndex &index, const DataType type)
29 assert(operands.exist(index));
30 operands.at(index).type(type);
33 ExplicitPadding makeExplicitPadding(Operands &operands, const OperandIndex &left_index,
34 const OperandIndex &right_index, const OperandIndex &top_index,
35 const OperandIndex &bottom_index)
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>();
42 if (left < 0 || right < 0 || top < 0 || bottom < 0)
44 throw std::runtime_error{"Cannot handle negative explicit padding value"};
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);
56 Stride makeStride(Operands &operands, const OperandIndex &horizontal_index,
57 const OperandIndex &vertical_index)
59 auto horizontal = operands.at(horizontal_index).asScalar<int32_t>();
60 auto vertical = operands.at(vertical_index).asScalar<int32_t>();
62 if (vertical < 0 || horizontal < 0)
64 throw std::runtime_error{"Cannot handle negative stride value"};
68 stride.horizontal = static_cast<uint32_t>(horizontal);
69 stride.vertical = static_cast<uint32_t>(vertical);
74 uint32_t getUint32Scalar(Operands &operands, const OperandIndex index)
76 auto int32_value = operands.at(index).asScalar<int32_t>();
79 throw std::runtime_error{"Cannot handle negative value"};
82 return static_cast<uint32_t>(int32_value);
85 OperationFactory::Generator
86 getElementwiseActivationGenerator(const onert::ir::operation::ElementwiseActivation::Type op_type,
87 float alpha = 0.f, float beta = 0.f)
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);
93 // Each input should be interpreted as follows:
95 // 0 -> Input Tensor Index
97 OperandIndexSequence inputs{init_param.inputs[0]};
98 OperandIndexSequence outputs{init_param.outputs[0]};
100 operation::ElementwiseActivation::Param param;
101 param.op_type = op_type;
105 return new operation::ElementwiseActivation{inputs, outputs, param};
109 OperationFactory::Generator getElementwiseBinaryGenerator(
110 const onert::ir::operation::ElementwiseBinary::ElementwiseBinaryType op_type)
112 return [op_type](const OperationFactory::Param &init_param, Operands &) {
113 assert(init_param.input_count == 2);
114 assert(init_param.output_count == 1);
116 // Each input should be interpreted as follows:
118 // 0 -> Lefthand side operand
119 // 1 -> Righthand side operand
121 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
122 OperandIndexSequence outputs{init_param.outputs[0]};
124 operation::ElementwiseBinary::Param param;
125 param.op_type = op_type;
127 return new operation::ElementwiseBinary{inputs, outputs, param};
131 OperationFactory::Generator
132 getElementwiseUnaryGenerator(const onert::ir::operation::ElementwiseUnary::Type op_type)
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);
138 // Each input should be interpreted as follows:
140 // 0 -> Input Tensor Index
142 OperandIndexSequence inputs{init_param.inputs[0]};
143 OperandIndexSequence outputs{init_param.outputs[0]};
145 operation::ElementwiseUnary::Param param;
146 param.op_type = op_type;
148 if (op_type == operation::ElementwiseUnary::Type::CAST)
150 // NNAPI uses QUANT_UINT8_ASYMM to represent UINT8 type for ANEURALNETWORKS_CAST's
152 if (operands.at(inputs.at(0)).typeInfo().type() == DataType::QUANT_UINT8_ASYMM)
154 replaceDataType(operands, inputs.at(0), DataType::UINT8);
156 if (operands.at(outputs.at(0)).typeInfo().type() == DataType::QUANT_UINT8_ASYMM)
158 replaceDataType(operands, outputs.at(0), DataType::UINT8);
162 return new operation::ElementwiseUnary{inputs, outputs, param};
166 OperationFactory::Generator
167 getBinaryArithmeticGenerator(const onert::ir::operation::BinaryArithmetic::ArithmeticType op_type)
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);
173 // Each input should be interpreted as follows:
175 // 0 -> Lefthand side operand
176 // 1 -> Righthand side operand
178 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
179 OperandIndexSequence outputs{init_param.outputs[0]};
181 operation::BinaryArithmetic::Param param;
182 param.arithmetic_type = op_type;
183 const auto activation_index = OperandIndex{init_param.inputs[2]};
185 NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
187 return new operation::BinaryArithmetic{inputs, outputs, param};
191 OperationFactory::Generator
192 getPool2DGenerator(const onert::ir::operation::Pool2D::PoolType pool_type)
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);
199 // 0 -> IFM Tensor Index
200 OperandIndexSequence inputs{init_param.inputs[0]};
201 OperandIndexSequence outputs{init_param.outputs[0]};
203 operation::Pool2D::Param param;
204 param.op_type = pool_type;
205 if (init_param.input_count == 7) // support implicit padding
207 // Each input should be interpreted as follows:
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
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]};
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>();
229 NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
231 else // support explicit padding
233 // Each input should be interpreted as follows:
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
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]};
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);
262 NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
264 return new operation::Pool2D{inputs, outputs, param};
268 OperationFactory::Generator
269 getReduceGenerator(const onert::ir::operation::Reduce::ReduceType reduce_type)
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);
275 // Each input should be interpreted as follows:
277 // 0 -> Input Tensor Index
278 // 1 -> Reduced Axes Tensor Index
279 // 2 -> keep_dims Index
281 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
282 OperandIndexSequence outputs{init_param.outputs[0]};
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;
288 return new operation::Reduce{inputs, outputs, param};
292 template <typename T>
293 Operation *CreateSimpleUnaryOp(const OperationFactory::Param &init_param, Operands &)
295 assert(init_param.input_count == 1 && init_param.output_count == 1);
297 OperandIndexSequence outputs{init_param.outputs[0]};
299 // Each input should be interpreted as follows:
301 // 0 -> Input Tensor Index
302 OperandIndexSequence inputs{init_param.inputs[0]};
304 return new T{inputs, outputs};
307 // A generator function for binary ops with no params
308 template <typename T>
309 Operation *createSimpleBinaryOp(const OperationFactory::Param &init_param, Operands &)
311 assert(init_param.input_count == 2 && init_param.output_count == 1);
313 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
314 OperandIndexSequence outputs{init_param.outputs[0]};
316 return new T{inputs, outputs};
319 OperationFactory::Generator getComparisonGenerator(operation::Comparison::ComparisonType type)
321 return [type](const OperationFactory::Param &init_param, Operands &) -> Operation * {
322 assert(init_param.input_count == 2 && init_param.output_count == 1);
324 OperandIndexSequence outputs{init_param.outputs[0]};
326 // Each input should be interpreted as follows:
328 // 0 -> input0 Tensor Index
329 // 1 -> input1 Tensor Index
330 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
332 operation::Comparison::Param param;
333 param.comparison_type = type;
335 return new operation::Comparison{inputs, outputs, param};
341 OperationFactory &OperationFactory::get()
343 static OperationFactory factory;
347 OperationFactory::OperationFactory()
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>;
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);
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]};
366 operation::DepthwiseConv2D::Param param;
367 if (init_param.input_count == 8)
369 // Imlicit Padding case
370 // Each input should be interpreted as follows:
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
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]};
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);
389 NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
393 // Explicit Padding case
394 // Each input should be interpreted as follows:
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
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]};
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);
420 NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
424 param.dilation.width_factor = 1;
425 param.dilation.height_factor = 1;
427 return new operation::DepthwiseConv2D{inputs, outputs, param};
430 _map[ANEURALNETWORKS_MAX_POOL_2D] = getPool2DGenerator(operation::Pool2D::PoolType::MAX);
432 _map[ANEURALNETWORKS_AVERAGE_POOL_2D] = getPool2DGenerator(operation::Pool2D::PoolType::AVG);
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);
439 // When there are N + 1 inputs, each input should be interpreted as follows:
441 // [0, N) -> Input tensors
445 OperandIndexSequence inputs;
446 for (uint32_t n = 0; n < init_param.input_count - 1; ++n)
448 inputs.append(OperandIndex{init_param.inputs[n]});
450 OperandIndexSequence outputs{init_param.outputs[0]};
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>();
456 return new operation::Concat{inputs, outputs, param};
459 _map[ANEURALNETWORKS_RESHAPE] = [](const OperationFactory::Param &init_param, Operands &) {
460 assert(init_param.input_count == 2 && init_param.output_count == 1);
462 // Each input should be interpreted as follows:
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
468 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
469 OperandIndexSequence outputs{init_param.outputs[0]};
471 operation::Reshape::Param param{};
473 return new operation::Reshape{inputs, outputs, param};
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);
480 // Each input should be interpreted as follows:
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
487 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
488 OperandIndexSequence outputs{init_param.outputs[0]};
490 operation::FullyConnected::Param param;
491 const auto activation_index = OperandIndex{init_param.inputs[3]};
493 NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
494 param.weights_format = FullyConnectedWeightsFormat::Default;
496 return new operation::FullyConnected{inputs, outputs, param};
499 _map[ANEURALNETWORKS_SOFTMAX] = [](const OperationFactory::Param &init_param,
500 Operands &operands) {
501 assert(init_param.input_count == 2 && init_param.output_count == 1);
503 // Each input should be interpreted as follows:
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.
508 OperandIndexSequence inputs{init_param.inputs[0]};
509 OperandIndexSequence outputs{init_param.outputs[0]};
511 const auto beta_index = OperandIndex{init_param.inputs[1]};
513 operation::Softmax::Param param;
514 param.beta = operands.at(beta_index).asScalar<float>();
516 return new operation::Softmax{inputs, outputs, param};
519 _map[ANEURALNETWORKS_CAST] =
520 getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::CAST);
522 // ANEURALNETWORKS_CAST_EX is deprecated
523 // TODO Remove ANEURALNETWORKS_CAST_EX
524 _map[ANEURALNETWORKS_CAST_EX] = _map[ANEURALNETWORKS_CAST];
526 _map[ANEURALNETWORKS_CONV_2D] = [](const OperationFactory::Param &init_param,
527 Operands &operands) {
528 using operation::Conv2D;
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);
537 // 0 -> IFM Tensor Index
538 // 1 -> Kernel Tensor Index
539 // 2 -> Bias Tensor Index
541 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
542 OperandIndexSequence outputs{init_param.outputs[0]};
545 if (init_param.input_count == 7) // support implicit padding
547 // Each input should be interpreted as follows:
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
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]};
560 NNAPIConvert::getPaddingType(operands.at(padding_index).asScalar<PaddingCode>());
561 param.stride = makeStride(operands, hstride_index, vstride_index);
563 param.dilation.width_factor = 1;
564 param.dilation.height_factor = 1;
567 NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
569 else if (init_param.input_count == 10) // support explicit padding
571 // Each input should be interpreted as follows:
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
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]};
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);
594 param.dilation.width_factor = 1;
595 param.dilation.height_factor = 1;
598 NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
600 else if (init_param.input_count == 13) // support dilation
602 // Each input should be interpreted as follows:
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
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]};
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);
629 auto width_factor = operands.at(width_factor_index).asScalar<int32_t>();
630 auto height_factor = operands.at(height_factor_index).asScalar<int32_t>();
632 param.dilation.width_factor = width_factor;
633 param.dilation.height_factor = height_factor;
636 NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
640 throw std::runtime_error{"Conv2D: unsupported input operand count"};
643 return new Conv2D{inputs, outputs, param};
646 _map[ANEURALNETWORKS_ADD] =
647 getBinaryArithmeticGenerator(onert::ir::operation::BinaryArithmetic::ArithmeticType::ADD);
649 _map[ANEURALNETWORKS_ADDV2_EX] = _map[ANEURALNETWORKS_ADD];
651 _map[ANEURALNETWORKS_REDUCE_SUM] =
652 getReduceGenerator(onert::ir::operation::Reduce::ReduceType::SUM);
654 // ANEURALNETWORKS_REDUCE_SUM_EX is deprecated
655 // TODO Remove ANEURALNETWORKS_REDUCE_SUM_EX
656 _map[ANEURALNETWORKS_REDUCE_SUM_EX] = _map[ANEURALNETWORKS_REDUCE_SUM];
658 _map[ANEURALNETWORKS_SUB] =
659 getBinaryArithmeticGenerator(onert::ir::operation::BinaryArithmetic::ArithmeticType::SUB);
661 _map[ANEURALNETWORKS_SLICE] = [](const OperationFactory::Param &init_param, Operands &) {
662 assert(init_param.input_count == 3 && init_param.output_count == 1);
664 OperandIndexSequence outputs{init_param.outputs[0]};
666 // Each input should be interpreted as follows:
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]};
673 return new operation::Slice{inputs, outputs};
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);
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]};
684 // Each input should be interpreted as follows:
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
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
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
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.
706 operation::StridedSlice::Param param;
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>();
713 return new operation::StridedSlice{inputs, outputs, param};
716 _map[ANEURALNETWORKS_TRANSPOSE] = createSimpleBinaryOp<operation::Transpose>;
718 _map[ANEURALNETWORKS_MUL] =
719 getBinaryArithmeticGenerator(onert::ir::operation::BinaryArithmetic::ArithmeticType::MUL);
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);
726 OperandIndexSequence outputs{init_param.outputs[0]};
728 // Each input should be interpreted as follows:
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
736 // Add mandatory input index
737 OperandIndexSequence inputs{init_param.inputs[0]};
739 // Add dims index if specified
740 operation::Squeeze::Param param{};
741 if (init_param.input_count == 2)
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)) <=
748 param.ndim = operands.at(squeeze_dims_idx).shape().dim(0);
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]));
757 return new operation::Squeeze{inputs, outputs, param};
760 _map[ANEURALNETWORKS_TANH] = getElementwiseActivationGenerator(
761 onert::ir::operation::ElementwiseActivation::Type::TANH, 1.f, 1.f);
763 _map[ANEURALNETWORKS_LOG] = getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::LOG);
765 _map[ANEURALNETWORKS_LOGISTIC] = getElementwiseActivationGenerator(
766 onert::ir::operation::ElementwiseActivation::Type::LOGISTIC);
768 _map[ANEURALNETWORKS_DIV] =
769 getBinaryArithmeticGenerator(onert::ir::operation::BinaryArithmetic::ArithmeticType::DIV);
771 _map[ANEURALNETWORKS_EXP] = getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::EXP);
773 // ANEURALNETWORKS_EXP_EX is deprecated
774 // TODO Remove ANEURALNETWORKS_EXP_EX
775 _map[ANEURALNETWORKS_EXP_EX] = _map[ANEURALNETWORKS_EXP];
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>;
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);
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);
800 OperandIndexSequence outputs{init_param.outputs[0]};
802 // Each input should be interpreted as follows:
804 // 0 -> input0 Tensor Index
805 // 1 -> input1 Tensor Index
806 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
808 operation::Comparison::Param param;
809 param.comparison_type = operation::Comparison::ComparisonType::GreaterEqual;
811 // Output operand type must be boolean
812 replaceDataType(operands, outputs.at(0), DataType::BOOL8);
814 return new operation::Comparison{inputs, outputs, param};
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);
823 OperandIndexSequence outputs{init_param.outputs[0]};
825 // Each input should be interpreted as follows:
827 // 0 -> input0 Tensor Index
828 // 1 -> input1 Tensor Index
829 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
831 operation::Comparison::Param param;
832 param.comparison_type = operation::Comparison::ComparisonType::Less;
834 // Output operand type must be boolean
835 replaceDataType(operands, outputs.at(0), DataType::BOOL8);
837 return new operation::Comparison{inputs, outputs, param};
840 _map[ANEURALNETWORKS_REDUCE_ALL] =
841 getReduceGenerator(onert::ir::operation::Reduce::ReduceType::ALL);
843 _map[ANEURALNETWORKS_REDUCE_ANY] =
844 getReduceGenerator(onert::ir::operation::Reduce::ReduceType::ANY);
846 _map[ANEURALNETWORKS_REDUCE_MAX] =
847 getReduceGenerator(onert::ir::operation::Reduce::ReduceType::MAX);
849 // ANEURALNETWORKS_REDUCE_MAX_EX is deprecated
850 // TODO Remove ANEURALNETWORKS_REDUCE_MAX_EX
851 _map[ANEURALNETWORKS_REDUCE_MAX_EX] = _map[ANEURALNETWORKS_REDUCE_MAX];
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);
859 OperandIndexSequence outputs{init_param.outputs[0]};
861 // Each input should be interpreted as follows:
863 // 0 -> input1 Tensor Index
864 // 1 -> input2 Tensor Index
865 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
867 operation::Comparison::Param param;
868 param.comparison_type = operation::Comparison::ComparisonType::NotEqual;
870 // Output operand type must be boolean
871 replaceDataType(operands, outputs.at(0), DataType::BOOL8);
873 return new operation::Comparison{inputs, outputs, param};
876 _map[ANEURALNETWORKS_LOGICAL_AND] = getElementwiseBinaryGenerator(
877 operation::ElementwiseBinary::ElementwiseBinaryType::LOGICAL_AND);
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);
885 OperandIndexSequence outputs{init_param.outputs[0]};
887 // Each input should be interpreted as follows:
889 // 0 -> input0 Tensor Index
890 // 1 -> input1 Tensor Index
891 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
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);
898 operation::ElementwiseBinary::Param param;
899 param.op_type = operation::ElementwiseBinary::ElementwiseBinaryType::LOGICAL_AND;
901 return new operation::ElementwiseBinary{inputs, outputs, param};
904 _map[ANEURALNETWORKS_RSQRT] =
905 getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::RSQRT);
907 _map[ANEURALNETWORKS_SELECT] = [](const OperationFactory::Param &init_param, Operands &) {
908 assert(init_param.input_count == 3 && init_param.output_count == 1);
910 OperandIndexSequence outputs{init_param.outputs[0]};
912 // Each input should be interpreted as follows:
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]};
919 return new operation::Select{inputs, outputs};
922 _map[ANEURALNETWORKS_SELECT_V2_EX] = [](const OperationFactory::Param &init_param, Operands &) {
923 assert(init_param.input_count == 3 && init_param.output_count == 1);
925 OperandIndexSequence outputs{init_param.outputs[0]};
927 // Each input should be interpreted as follows:
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]};
934 return new operation::Select{inputs, outputs};
937 // ANEURALNETWORKS_RSQRT_EX is deprecated
938 // TODO Remove ANEURALNETWORKS_RSQRT_EX
939 _map[ANEURALNETWORKS_RSQRT_EX] = _map[ANEURALNETWORKS_RSQRT];
941 _map[ANEURALNETWORKS_RELU] =
942 getElementwiseActivationGenerator(onert::ir::operation::ElementwiseActivation::Type::RELU,
943 onert::ir::operation::ElementwiseActivation::infinity, 0);
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);
949 OperandIndexSequence outputs{init_param.outputs[0]};
951 // Each input should be interpreted as follows:
956 OperandIndexSequence inputs{init_param.inputs[0]};
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};
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);
971 OperandIndexSequence outputs{init_param.outputs[0]};
973 // Each input should be interpreted as follows:
978 OperandIndexSequence inputs{init_param.inputs[0]};
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};
988 _map[ANEURALNETWORKS_RELU1] = getElementwiseActivationGenerator(
989 onert::ir::operation::ElementwiseActivation::Type::RELU, 1.f, -1.f);
991 _map[ANEURALNETWORKS_RELU6] = getElementwiseActivationGenerator(
992 onert::ir::operation::ElementwiseActivation::Type::RELU, 6.f, 0.f);
994 _map[ANEURALNETWORKS_REVERSE_EX] = [](const OperationFactory::Param &init_param, Operands &) {
995 assert(init_param.input_count == 2 && init_param.output_count == 1);
997 // Each input should be interpreted as follows:
999 // 0 -> Input Tensor Index
1000 // 1 -> Axis Tensor Index
1002 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
1003 OperandIndexSequence outputs{init_param.outputs[0]};
1005 return new operation::Reverse{inputs, outputs};
1008 _map[ANEURALNETWORKS_RNN] = [](const OperationFactory::Param &init_param, Operands &operands) {
1009 assert(init_param.input_count == 6 && init_param.output_count == 2);
1011 // Each input should be interpreted as follows:
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
1020 OperandIndexSequence inputs;
1021 for (uint32_t n = 0; n < init_param.input_count - 1; ++n)
1023 inputs.append(OperandIndex{init_param.inputs[n]});
1025 OperandIndexSequence outputs;
1026 for (uint32_t n = 0; n < init_param.output_count; ++n)
1028 outputs.append(OperandIndex{init_param.outputs[n]});
1031 operation::RNN::Param param;
1032 const auto activation_index = OperandIndex{init_param.inputs[5]};
1034 NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
1036 return new operation::RNN{inputs, outputs, param};
1039 _map[ANEURALNETWORKS_FLOOR] =
1040 getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::FLOOR);
1042 _map[ANEURALNETWORKS_SPACE_TO_BATCH_ND] = [](const OperationFactory::Param &init_param,
1044 assert(init_param.input_count == 3 && init_param.output_count == 1);
1046 OperandIndexSequence outputs{init_param.outputs[0]};
1048 // Each input should be interpreted as follows:
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)
1056 inputs.append(OperandIndex{init_param.inputs[n]});
1059 return new operation::SpaceToBatchND{inputs, outputs};
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);
1066 OperandIndexSequence outputs{init_param.outputs[0]};
1068 // Each input should be interpreted as follows:
1070 // 0 -> Input Tensor Index
1071 // 1 -> Block size Index
1072 OperandIndexSequence inputs{init_param.inputs[0]};
1074 operation::SpaceToDepth::Param param;
1075 param.block_size = operands.at(OperandIndex{init_param.inputs[1]}).asScalar<std::int32_t>();
1077 return new operation::SpaceToDepth{inputs, outputs, param};
1080 _map[ANEURALNETWORKS_L2_POOL_2D] = getPool2DGenerator(operation::Pool2D::PoolType::L2);
1082 _map[ANEURALNETWORKS_EMBEDDING_LOOKUP] = [](const OperationFactory::Param &init_param,
1084 assert(init_param.input_count == 2 && init_param.output_count == 1);
1086 OperandIndexSequence outputs{init_param.outputs[0]};
1088 // Each input should be interpreted as follows:
1090 // 0 -> Lookups Index
1091 // 1 -> Values Index
1092 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
1094 return new operation::EmbeddingLookup{inputs, outputs};
1097 _map[ANEURALNETWORKS_L2_NORMALIZATION] = [](const OperationFactory::Param &init_param,
1099 assert(init_param.input_count == 1 && init_param.output_count == 1);
1101 OperandIndexSequence outputs{init_param.outputs[0]};
1103 // Each input should be interpreted as follows:
1104 // 0 -> input Tensor Index
1105 OperandIndexSequence inputs{init_param.inputs[0]};
1107 return new operation::L2Normalization{inputs, outputs};
1110 _map[ANEURALNETWORKS_HASHTABLE_LOOKUP] = [](const OperationFactory::Param &init_param,
1112 assert(init_param.input_count == 3 && init_param.output_count == 2);
1114 // Each output should be interpreted as follows:
1116 // 0 -> Output Index
1118 OperandIndexSequence outputs{init_param.outputs[0], init_param.outputs[1]};
1120 // Each input should be interpreted as follows:
1122 // 0 -> Lookups Index
1124 // 2 -> Values Index
1125 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
1127 return new operation::HashtableLookup{inputs, outputs};
1130 _map[ANEURALNETWORKS_PRELU] = [](const OperationFactory::Param &init_param, Operands &) {
1131 assert(init_param.input_count == 2 && init_param.output_count == 1);
1133 OperandIndexSequence outputs{init_param.outputs[0]};
1135 // Each input should be interpreted as follows:
1137 // 0 -> input Tensor Index
1138 // 1 -> alpha Tensor Index
1139 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
1141 return new operation::PReLU{inputs, outputs};
1144 // ANEURALNETWORKS_PRELU_EX is deprecated
1145 // TODO Remove ANEURALNETWORKS_PRELU_EX
1146 _map[ANEURALNETWORKS_PRELU_EX] = _map[ANEURALNETWORKS_PRELU];
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);
1152 OperandIndexSequence outputs{init_param.outputs[0]};
1154 // Each input should be interpreted as follows:
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
1163 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
1165 operation::TransposeConv::Param param;
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]};
1171 param.padding.type =
1172 NNAPIConvert::getPaddingType(operands.at(padding_index).asScalar<PaddingCode>());
1173 param.stride = makeStride(operands, hstride_index, vstride_index);
1175 return new operation::TransposeConv{inputs, outputs, param};
1178 _map[ANEURALNETWORKS_SQRT] =
1179 getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::SQRT);
1181 // ANEURALNETWORKS_SQRT_EX is deprecated
1182 // TODO Remove ANEURALNETWORKS_SQRT_EX
1183 _map[ANEURALNETWORKS_SQRT_EX] = _map[ANEURALNETWORKS_SQRT];
1185 _map[ANEURALNETWORKS_LOGICAL_OR] = getElementwiseBinaryGenerator(
1186 operation::ElementwiseBinary::ElementwiseBinaryType::LOGICAL_OR);
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);
1194 OperandIndexSequence outputs{init_param.outputs[0]};
1196 // Each input should be interpreted as follows:
1198 // 0 -> input0 Tensor Index
1199 // 1 -> input1 Tensor Index
1200 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
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);
1207 operation::ElementwiseBinary::Param param;
1208 param.op_type = operation::ElementwiseBinary::ElementwiseBinaryType::LOGICAL_OR;
1210 return new operation::ElementwiseBinary{inputs, outputs, param};
1213 _map[ANEURALNETWORKS_LOGICAL_NOT] =
1214 getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::LOGICAL_NOT);
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);
1222 OperandIndexSequence outputs{init_param.outputs[0]};
1224 // Each input should be interpreted as follows:
1226 // 0 -> input Tensor Index
1227 OperandIndexSequence inputs{init_param.inputs[0]};
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);
1233 operation::ElementwiseUnary::Param param;
1234 param.op_type = operation::ElementwiseUnary::Type::LOGICAL_NOT;
1236 return new operation::ElementwiseUnary{inputs, outputs, param};
1239 _map[ANEURALNETWORKS_LSTM] = [](const OperationFactory::Param &init_param, Operands &operands) {
1240 assert(init_param.input_count == 23 && init_param.output_count == 4);
1242 // Each input should be interpreted as follows:
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)
1267 inputs.append(OperandIndex{init_param.inputs[n]});
1270 // Each output should be interpreted as follows:
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)
1279 outputs.append(OperandIndex{init_param.outputs[n]});
1282 operation::LSTM::Param param;
1283 const auto activation_index = OperandIndex{init_param.inputs[20]};
1284 switch (operands.at(activation_index).asScalar<int32_t>())
1287 param.activation = Activation::NONE;
1290 param.activation = Activation::RELU;
1293 param.activation = Activation::RELU1;
1296 param.activation = Activation::RELU6;
1299 param.activation = Activation::TANH;
1302 param.activation = Activation::SIGMOID;
1305 throw std::runtime_error("Unsupported activation type");
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;
1314 return new operation::LSTM{inputs, outputs, param};
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));
1322 // Each input should be interpreted as follows:
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)
1348 inputs.append(OperandIndex{init_param.inputs[n]});
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)
1357 for (uint32_t n = 24; n < 28; ++n)
1359 if (init_param.input_count > n)
1361 inputs.append(OperandIndex{init_param.inputs[n]});
1366 // Each output should be interpreted as follows:
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,
1380 operation::LSTM::Param param;
1381 const auto activation_index = OperandIndex{init_param.inputs[20]};
1382 switch (operands.at(activation_index).asScalar<int32_t>())
1385 param.activation = Activation::NONE;
1388 param.activation = Activation::RELU;
1391 param.activation = Activation::RELU1;
1394 param.activation = Activation::RELU6;
1397 param.activation = Activation::TANH;
1400 param.activation = Activation::SIGMOID;
1403 throw std::runtime_error("Unsupported activation type");
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>();
1410 return new operation::LSTM{inputs, outputs, param};
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);
1419 OperandIndexSequence outputs{init_param.outputs[0]};
1421 // Each input should be interpreted as follows:
1423 // 0 -> input0 Tensor Index
1424 // 1 -> input1 Tensor Index
1425 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
1427 operation::Comparison::Param param;
1428 param.comparison_type = operation::Comparison::ComparisonType::Equal;
1430 // Output operand type must be boolean
1431 replaceDataType(operands, outputs.at(0), DataType::BOOL8);
1433 return new operation::Comparison{inputs, outputs, param};
1436 _map[ANEURALNETWORKS_SQUARED_DIFFERENCE_EX] = [](const OperationFactory::Param &init_param,
1438 assert(init_param.input_count == 2 && init_param.output_count == 1);
1440 OperandIndexSequence outputs{init_param.outputs[0]};
1442 // Each input should be interpreted as follows:
1444 // 0 -> LHS Tensor Index
1445 // 1 -> RHS Tensor Index
1446 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
1448 return new operation::SquaredDifference{inputs, outputs};
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);
1455 // Each output should be interpreted as follows:
1457 // 0 -> Index for Output Values
1458 // 1 -> Index for Output Indices
1459 OperandIndexSequence outputs{init_param.outputs[0], init_param.outputs[1]};
1461 // Each input should be interpreted as follows:
1463 // 0 -> Index for Input Data
1465 OperandIndexSequence inputs{init_param.inputs[0]};
1467 operation::TopKV2::Param param;
1468 param.k = operands.at(OperandIndex{init_param.inputs[1]}).asScalar<std::int32_t>();
1470 return new operation::TopKV2{inputs, outputs, param};
1473 // ANEURALNETWORKS_CAST_EX is deprecated
1474 // TODO Remove ANEURALNETWORKS_CAST_EX
1475 _map[ANEURALNETWORKS_TOPK_V2_EX] = _map[ANEURALNETWORKS_TOPK_V2];
1477 _map[ANEURALNETWORKS_GATHER] = [](const OperationFactory::Param &init_param, Operands &operands) {
1478 assert(init_param.input_count == 3 && init_param.output_count == 1);
1480 OperandIndexSequence outputs{init_param.outputs[0]};
1482 // Each input should be interpreted as follows:
1484 // 0 -> input Tensor Index
1486 // 2 -> indices Tensor Index
1487 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[2]};
1489 operation::Gather::Param param;
1490 param.axis = operands.at(OperandIndex{init_param.inputs[1]}).asScalar<int32_t>();
1492 return new operation::Gather{inputs, outputs, param};
1495 // ANEURALNETWORKS_GATHER_EX is deprecated
1496 // TODO Remove ANEURALNETWORKS_GATHER_EX
1497 _map[ANEURALNETWORKS_GATHER_EX] = _map[ANEURALNETWORKS_GATHER];
1499 _map[ANEURALNETWORKS_NEG] = getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::NEG);
1501 // ANEURALNETWORKS_NEG_EX is deprecated
1502 // TODO Remove ANEURALNETWORKS_NEG_EX
1503 _map[ANEURALNETWORKS_NEG_EX] = _map[ANEURALNETWORKS_NEG];
1505 _map[ANEURALNETWORKS_ABS] = getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::ABS);
1507 // ANEURALNETWORKS_ABS_EX is deprecated
1508 // TODO Remove ANEURALNETWORKS_ABS_EX
1509 _map[ANEURALNETWORKS_ABS_EX] = _map[ANEURALNETWORKS_ABS];
1511 _map[ANEURALNETWORKS_ARGMAX] = [](const OperationFactory::Param &init_param, Operands &) {
1512 assert(init_param.input_count == 2 && init_param.output_count == 1);
1514 OperandIndexSequence outputs{init_param.outputs[0]};
1516 // Each input should be interpreted as follows:
1518 // 0 -> Input Tensor Index
1519 // 1 -> Axis Tensor Index
1520 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
1522 operation::ArgMax::Param param;
1523 // NNAPI ARGMAX output type is always int32
1524 param.output_type = DataType::INT32;
1526 return new operation::ArgMax{inputs, outputs, param};
1529 // ANEURALNETWORKS_ARGMAX_EX is deprecated
1530 // TODO Remove ANEURALNETWORKS_ARGMAX_EX
1531 _map[ANEURALNETWORKS_ARGMAX_EX] = _map[ANEURALNETWORKS_ARGMAX];
1533 _map[ANEURALNETWORKS_DEQUANTIZE] =
1534 getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::DEQUANTIZE);
1536 _map[ANEURALNETWORKS_MEAN] = [](const OperationFactory::Param &init_param, Operands &operands) {
1537 assert(init_param.input_count == 3 && init_param.output_count == 1);
1539 OperandIndexSequence outputs{init_param.outputs[0]};
1541 // Each input should be interpreted as follows:
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]};
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;
1552 return new operation::Reduce{inputs, outputs, param};
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);
1559 OperandIndexSequence outputs{init_param.outputs[0]};
1561 OperandIndexSequence inputs{init_param.inputs[0]};
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>();
1569 return new operation::LocalResponseNormalization{inputs, outputs, param};
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);
1576 OperandIndexSequence outputs{init_param.outputs[0]};
1578 // Each input should be interpreted as follows:
1580 // 0 -> Input Tensor Index
1581 // 1 -> Block size Index
1582 OperandIndexSequence inputs{init_param.inputs[0]};
1584 operation::DepthToSpace::Param param;
1585 param.block_size = operands.at(OperandIndex{init_param.inputs[1]}).asScalar<std::int32_t>();
1587 return new operation::DepthToSpace{inputs, outputs, param};
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);
1594 OperandIndexSequence outputs{init_param.outputs[0]};
1595 OperandIndexSequence inputs;
1596 for (uint32_t n = 0; n < init_param.input_count - 2; ++n)
1598 inputs.append(OperandIndex{init_param.inputs[n]});
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>();
1607 return new operation::Pack{inputs, outputs, param};
1610 _map[ANEURALNETWORKS_REDUCE_MIN] =
1611 getReduceGenerator(onert::ir::operation::Reduce::ReduceType::MIN);
1613 // ANEURALNETWORKS_REDUCE_MIN_EX is deprecated
1614 // TODO Remove ANEURALNETWORKS_REDUCE_MIN_EX
1615 _map[ANEURALNETWORKS_REDUCE_MIN_EX] = _map[ANEURALNETWORKS_REDUCE_MIN];
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
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)
1625 outputs.append(OperandIndex{init_param.outputs[n]});
1628 operation::Split::Param param;
1629 param.num_splits = operands.at(OperandIndex{init_param.inputs[2]}).asScalar<std::int32_t>();
1631 return new operation::Split{inputs, outputs, param};
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
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)
1643 outputs.append(OperandIndex{init_param.outputs[n]});
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};
1651 // ANEURALNETWORKS_SPLIT_EX is deprecated
1652 // TODO Remove ANEURALNETWORKS_SPLIT_EX
1653 _map[ANEURALNETWORKS_SPLIT_EX] = _map[ANEURALNETWORKS_SPLIT];
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);
1659 OperandIndexSequence inputs{init_param.inputs[0]};
1660 OperandIndexSequence outputs;
1661 for (uint32_t n = 0; n < init_param.output_count; ++n)
1663 outputs.append(OperandIndex{init_param.outputs[n]});
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>();
1672 return new operation::Unpack{inputs, outputs, param};
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);
1679 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
1680 if (init_param.input_count == 3)
1682 inputs.append(OperandIndex{init_param.inputs[2]});
1684 OperandIndexSequence outputs{init_param.outputs[0]};
1686 return new operation::Pad{inputs, outputs};
1689 _map[ANEURALNETWORKS_PAD_V2] = _map[ANEURALNETWORKS_PAD];
1691 _map[ANEURALNETWORKS_MINIMUM] =
1692 getElementwiseBinaryGenerator(operation::ElementwiseBinary::ElementwiseBinaryType::MIN);
1694 _map[ANEURALNETWORKS_MAXIMUM] =
1695 getElementwiseBinaryGenerator(operation::ElementwiseBinary::ElementwiseBinaryType::MAX);
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:
1703 // 0 -> indices tensor
1704 // 1 -> depth tensor
1705 // 2 -> on_value tensor
1706 // 3 -> off_value tensor
1708 OperandIndexSequence inputs;
1709 for (uint32_t n = 0; n < init_param.input_count - 1; ++n)
1711 inputs.append(OperandIndex{init_param.inputs[n]});
1713 OperandIndexSequence outputs{init_param.outputs[0]};
1715 operation::OneHot::Param param;
1716 param.axis = operands.at(OperandIndex{init_param.inputs[4]}).asScalar<std::int32_t>();
1718 return new operation::OneHot{inputs, outputs, param};
1721 _map[ANEURALNETWORKS_COS_EX] =
1722 getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::COS);
1724 _map[ANEURALNETWORKS_SIN] = getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::SIN);
1726 _map[ANEURALNETWORKS_SHAPE_EX] = [](const OperationFactory::Param &init_param, Operands &) {
1727 assert(init_param.input_count == 1 && init_param.output_count == 1);
1729 OperandIndexSequence inputs{init_param.inputs[0]};
1730 OperandIndexSequence outputs{init_param.outputs[0]};
1732 return new operation::Shape{inputs, outputs};
1735 _map[ANEURALNETWORKS_REDUCE_PROD] =
1736 getReduceGenerator(onert::ir::operation::Reduce::ReduceType::PROD);
1738 _map[ANEURALNETWORKS_ROUND_EX] =
1739 getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::ROUND);
1741 _map[ANEURALNETWORKS_RANGE_EX] = [](const OperationFactory::Param &init_param, Operands &) {
1742 assert(init_param.input_count == 3 && init_param.output_count == 1);
1744 OperandIndexSequence outputs{init_param.outputs[0]};
1746 // Each input should be interpreted as follows:
1747 // 0 -> start Tensor Index
1748 // 1 -> limit Tensor Index
1749 // 2 -> delta Tensor Index
1751 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
1753 return new operation::Range{inputs, outputs};
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>;
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>;
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>;
1773 _map[ANEURALNETWORKS_MATRIX_BAND_PART_EX] = [](const OperationFactory::Param &init_param,
1775 assert(init_param.input_count == 3);
1776 assert(init_param.output_count == 1);
1777 // Each input should be interpreted as follows:
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]};
1785 return new operation::MatrixBandPart{inputs, outputs};
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);
1792 OperandIndexSequence outputs{init_param.outputs[0]};
1794 // Each input should be interpreted as follows:
1796 // 0 -> Lhs Tensor Index
1797 // 1 -> Rhs Tensor Index
1798 // 2 -> adj_x boolean scalar Index
1799 // 3 -> adj_y boolean scalar Index
1801 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
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>();
1807 return new operation::BatchMatMul{inputs, outputs, param};
1810 _map[ANEURALNETWORKS_EINSUM_EX] = [](const OperationFactory::Param &init_param,
1811 Operands &operands) {
1812 // Each input should be interpreted as follows:
1814 // 0....n - 1 -> n Input Tensors Index
1816 assert(init_param.input_count >= 1 && init_param.output_count == 1);
1818 OperandIndexSequence inputs;
1819 for (uint32_t n = 0; n < init_param.input_count - 1; ++n)
1821 inputs.append(OperandIndex{init_param.inputs[n]});
1823 OperandIndexSequence outputs{init_param.outputs[0]};
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());
1830 return new operation::Einsum{inputs, outputs, param};
1833 // 0 -> Input Tensor Index
1834 // 1 -> int32, int64, An 1-D int tensor Index
1835 _map[ANEURALNETWORKS_BROADCAST_TO_EX] = createSimpleBinaryOp<operation::BroadcastTo>;
1837 _map[ANEURALNETWORKS_STATELESS_RANDOM_UNIFORM_EX] = [](const OperationFactory::Param &init_param,
1839 assert(init_param.input_count == 2 && init_param.output_count == 1);
1840 OperandIndexSequence outputs{init_param.outputs[0]};
1842 // Each input should be interpreted as follows:
1844 // 0 -> Shape Tensor Index
1845 // 1 -> int32, int64, An 1-D int tensor Index
1847 OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
1849 return new operation::StatelessRandomUniform{inputs, outputs};
1852 _map[ANEURALNETWORKS_FUSED_BATCH_NORM_V3_EX] = [](const OperationFactory::Param &init_param,
1853 Operands &operands) {
1854 // Each input should be interpreted as follows:
1856 // 0....4 -> 5 Input Tensors Index
1857 // n-2 -> is_training
1858 // n-1 -> data_format
1861 assert(init_param.input_count == 8 && init_param.output_count == 1);
1863 OperandIndexSequence inputs;
1864 for (uint32_t n = 0; n < init_param.input_count - 3; ++n)
1866 inputs.append(OperandIndex{init_param.inputs[n]});
1868 OperandIndexSequence outputs{init_param.outputs[0]};
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>();
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());
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};
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);
1887 // Each input should be interpreted as follows:
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.
1893 OperandIndexSequence inputs{init_param.inputs[0]};
1894 OperandIndexSequence outputs{init_param.outputs[0]};
1896 const auto beta_index = OperandIndex{init_param.inputs[1]};
1897 const auto axis_index = OperandIndex{init_param.inputs[2]};
1899 operation::LogSoftmax::Param param;
1900 param.beta = operands.at(beta_index).asScalar<float>();
1901 param.axis = operands.at(axis_index).asScalar<int>();
1903 return new operation::LogSoftmax{inputs, outputs, param};
1906 _map[ANEURALNETWORKS_QUANTIZE] =
1907 getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::QUANTIZE);
1910 Operation *OperationFactory::create(ANeuralNetworksOperationType type,
1911 const OperationFactory::Param ¶m, Operands &operands)
1913 auto it = _map.find(type);
1914 if (it == _map.end())
1916 throw std::runtime_error("Unsupported operation type: " + std::to_string(type));
1918 return it->second(param, operands);