summaryrefslogtreecommitdiff
path: root/runtime/onert/frontend/nnapi
diff options
context:
space:
mode:
authorChunseok Lee <chunseok.lee@samsung.com>2020-12-14 14:43:04 +0900
committerChunseok Lee <chunseok.lee@samsung.com>2020-12-14 14:43:04 +0900
commit12d88feea8573f8490629cf62fc342b152e57d65 (patch)
tree3c734cc4d629834d2d523f4575ef84cd64684e57 /runtime/onert/frontend/nnapi
parentd6b371e095d737922187a518b8faba1ef6f3a2b1 (diff)
downloadnnfw-12d88feea8573f8490629cf62fc342b152e57d65.tar.gz
nnfw-12d88feea8573f8490629cf62fc342b152e57d65.tar.bz2
nnfw-12d88feea8573f8490629cf62fc342b152e57d65.zip
Imported Upstream version 1.11.0upstream/1.11.0
Diffstat (limited to 'runtime/onert/frontend/nnapi')
-rw-r--r--runtime/onert/frontend/nnapi/CMakeLists.txt27
-rw-r--r--runtime/onert/frontend/nnapi/compilation.cc106
-rw-r--r--runtime/onert/frontend/nnapi/event.cc36
-rw-r--r--runtime/onert/frontend/nnapi/execution.cc504
-rw-r--r--runtime/onert/frontend/nnapi/memory.cc42
-rw-r--r--runtime/onert/frontend/nnapi/model.cc416
-rw-r--r--runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksCompilation.cc45
-rw-r--r--runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksCompilation.h47
-rw-r--r--runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksEvent.cc42
-rw-r--r--runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksEvent.h44
-rw-r--r--runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksExecution.cc335
-rw-r--r--runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksExecution.h77
-rw-r--r--runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksMemory.cc46
-rw-r--r--runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksMemory.h39
-rw-r--r--runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksModel.cc287
-rw-r--r--runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksModel.h75
-rw-r--r--runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksModel.test.cc25
-rw-r--r--runtime/onert/frontend/nnapi/wrapper/NNAPIConvert.cc100
-rw-r--r--runtime/onert/frontend/nnapi/wrapper/NNAPIConvert.h79
-rw-r--r--runtime/onert/frontend/nnapi/wrapper/OperationFactory.cc1919
-rw-r--r--runtime/onert/frontend/nnapi/wrapper/OperationFactory.h60
21 files changed, 4351 insertions, 0 deletions
diff --git a/runtime/onert/frontend/nnapi/CMakeLists.txt b/runtime/onert/frontend/nnapi/CMakeLists.txt
new file mode 100644
index 000000000..dafd84ccf
--- /dev/null
+++ b/runtime/onert/frontend/nnapi/CMakeLists.txt
@@ -0,0 +1,27 @@
+file(GLOB_RECURSE SOURCES_FRONTEND "*.cc")
+file(GLOB_RECURSE TESTS_FRONTEND "*.test.cc")
+list(REMOVE_ITEM SOURCES_FRONTEND ${TESTS_FRONTEND})
+
+set(LIB_ONERT onert)
+
+add_library(${LIB_ONERT} SHARED ${SOURCES_FRONTEND})
+target_link_libraries(${LIB_ONERT} PUBLIC nnfw-nnapi-header)
+target_link_libraries(${LIB_ONERT} PUBLIC onert_core) # TODO Link PRIVATE onert_core
+target_link_libraries(${LIB_ONERT} PRIVATE nnfw_common)
+target_link_libraries(${LIB_ONERT} PRIVATE nnfw_coverage)
+
+set_target_properties(${LIB_ONERT} PROPERTIES OUTPUT_NAME neuralnetworks)
+
+install(TARGETS ${LIB_ONERT} DESTINATION lib)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+add_executable(test_onert_frontend_nnapi ${TESTS_FRONTEND})
+
+target_link_libraries(test_onert_frontend_nnapi PRIVATE ${LIB_ONERT} dl)
+target_link_libraries(test_onert_frontend_nnapi PRIVATE gtest)
+target_link_libraries(test_onert_frontend_nnapi PRIVATE gtest_main)
+
+install(TARGETS test_onert_frontend_nnapi DESTINATION unittest_standalone)
diff --git a/runtime/onert/frontend/nnapi/compilation.cc b/runtime/onert/frontend/nnapi/compilation.cc
new file mode 100644
index 000000000..871c040ef
--- /dev/null
+++ b/runtime/onert/frontend/nnapi/compilation.cc
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <NeuralNetworks.h>
+
+#include <new>
+
+#include "wrapper/ANeuralNetworksModel.h"
+#include "wrapper/ANeuralNetworksCompilation.h"
+#include "util/logging.h"
+
+//
+// NNAPI Implementation
+//
+int ANeuralNetworksCompilation_create(ANeuralNetworksModel *model,
+ ANeuralNetworksCompilation **compilation)
+{
+ if ((model == nullptr) || (compilation == nullptr))
+ {
+ VERBOSE(NNAPI::Compilation) << "create: Incorrect null pointer parameter(s)" << std::endl;
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ if (!model->isFinished())
+ {
+ VERBOSE(NNAPI::Compilation) << "create: Model define is not finished" << std::endl;
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+
+ *compilation = new (std::nothrow) ANeuralNetworksCompilation(model);
+ if (*compilation == nullptr)
+ {
+ VERBOSE(NNAPI::Compilation) << "create: ail to create compilation object" << std::endl;
+ return ANEURALNETWORKS_OUT_OF_MEMORY;
+ }
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksCompilation_finish(ANeuralNetworksCompilation *compilation)
+{
+ if (compilation == nullptr)
+ {
+ VERBOSE(NNAPI::Compilation) << "finish: Incorrect null pointer parameter" << std::endl;
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ if (compilation->state() != ::onert::compiler::State::CREATED)
+ {
+ VERBOSE(NNAPI::Compilation) << "finish: Already finished" << std::endl;
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+
+ if (!compilation->finish())
+ {
+ VERBOSE(NNAPI::Compilation) << "finish: Fail to compile" << std::endl;
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+void ANeuralNetworksCompilation_free(ANeuralNetworksCompilation *compilation)
+{
+ delete compilation;
+}
+
+int ANeuralNetworksCompilation_setPreference(ANeuralNetworksCompilation *compilation,
+ int32_t preference)
+{
+ if (compilation == nullptr)
+ {
+ VERBOSE(NNAPI::Compilation) << "setPreference: Incorrect null pointer parameter" << std::endl;
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ if (compilation->state() != ::onert::compiler::State::CREATED)
+ {
+ VERBOSE(NNAPI::Compilation) << "setPreference: Already finished" << std::endl;
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+
+ const PreferenceCode FIRST_PREFERENCE_CODE = ANEURALNETWORKS_PREFER_LOW_POWER;
+ const PreferenceCode LAST_PREFERENCE_CODE = ANEURALNETWORKS_PREFER_SUSTAINED_SPEED;
+ if ((preference < FIRST_PREFERENCE_CODE) || (preference > LAST_PREFERENCE_CODE))
+ {
+ VERBOSE(NNAPI::Compilation) << "setPreference: Incorrect preference code" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ // NYI: nothing to set
+ return ANEURALNETWORKS_NO_ERROR;
+}
diff --git a/runtime/onert/frontend/nnapi/event.cc b/runtime/onert/frontend/nnapi/event.cc
new file mode 100644
index 000000000..593b74e90
--- /dev/null
+++ b/runtime/onert/frontend/nnapi/event.cc
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <NeuralNetworks.h>
+
+#include "wrapper/ANeuralNetworksEvent.h"
+
+int ANeuralNetworksEvent_wait(ANeuralNetworksEvent *event)
+{
+ if (event == nullptr)
+ {
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ if (!event->waitFinish())
+ {
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+void ANeuralNetworksEvent_free(ANeuralNetworksEvent *event) { delete event; }
diff --git a/runtime/onert/frontend/nnapi/execution.cc b/runtime/onert/frontend/nnapi/execution.cc
new file mode 100644
index 000000000..56ca5ef00
--- /dev/null
+++ b/runtime/onert/frontend/nnapi/execution.cc
@@ -0,0 +1,504 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <NeuralNetworks.h>
+
+#include <new>
+
+#include "wrapper/ANeuralNetworksCompilation.h"
+#include "wrapper/ANeuralNetworksExecution.h"
+#include "wrapper/ANeuralNetworksMemory.h"
+#include "wrapper/ANeuralNetworksEvent.h"
+#include "wrapper/NNAPIConvert.h"
+#include "util/logging.h"
+
+//
+// NNAPI Implementation
+//
+int ANeuralNetworksExecution_create(ANeuralNetworksCompilation *compilation,
+ ANeuralNetworksExecution **execution)
+{
+ if ((compilation == nullptr) || (execution == nullptr))
+ {
+ VERBOSE(NNAPI::Execution) << "create: Incorrect null pointer parameter(s)" << std::endl;
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ std::shared_ptr<onert::exec::ExecutorMap> executors;
+
+ compilation->publish(executors);
+
+ if (executors == nullptr)
+ {
+ VERBOSE(NNAPI::Execution) << "create: Never compiled yet" << std::endl;
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+
+ *execution = new (std::nothrow) ANeuralNetworksExecution{executors};
+ if (*execution == nullptr)
+ {
+ VERBOSE(NNAPI::Execution) << "create: Fail to create execution object" << std::endl;
+ return ANEURALNETWORKS_OUT_OF_MEMORY;
+ }
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+// NOTE Handle optional input
+// Unspecified shape on model build
+// Optional and omitted input on execution: skip input setting (workaround for LSTM)
+// Optional but not omitted input on execution: cannot handle
+// Normal input on execution: cannot handle
+// Fully specified shape on model build
+// Optional input on execution: cannot handle
+// Normal input: handle normally
+int ANeuralNetworksExecution_setInput(ANeuralNetworksExecution *execution, int32_t index,
+ const ANeuralNetworksOperandType *type, const void *buffer,
+ size_t length)
+{
+ // Don't check type
+ // Comment about ANeuralNetworksOperandType in NeuralNetworks.h:
+ // If the input or output is optional and omitted then it need not have a fully specified tensor
+ // operand type
+ if ((execution == nullptr) || ((buffer == nullptr) && (length != 0)))
+ {
+ VERBOSE(NNAPI::Execution) << "setInput: Incorrect null pointer parameter(s)" << std::endl;
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ if ((buffer != nullptr) && (length == 0))
+ {
+ VERBOSE(NNAPI::Execution) << "setInput: Zero length input" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ const auto operand_index = execution->getInputOperandIndex(index);
+ if (!operand_index.valid())
+ {
+ VERBOSE(NNAPI::Execution) << "setInput: Invalid input index" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ // Omitted optional input
+ // LSTM operation's some inputs can be optional input
+ // Transpose operation's permutation input can be optional input
+ if ((buffer == nullptr) && (length == 0))
+ {
+ uint32_t dims[1] = {0};
+ ANeuralNetworksOperandType compared_shape;
+ compared_shape.dimensionCount = 1;
+ compared_shape.dimensions = dims;
+ if (execution->hasUnspecifiedDims(operand_index))
+ {
+ return ANEURALNETWORKS_NO_ERROR;
+ }
+ else if (type == nullptr && execution->IsOptionalInput(operand_index))
+ {
+ if (!execution->setOptionalInput(index, type, buffer, length))
+ {
+ VERBOSE(NNAPI::Execution) << "setInput: Fail to set optional input" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ return ANEURALNETWORKS_NO_ERROR;
+ }
+ // TODO Changes the condition to check zero sized
+ else if (execution->compareShape(&compared_shape, operand_index))
+ {
+ if (!execution->setInput(index, type, buffer, length))
+ {
+ VERBOSE(NNAPI::Execution) << "setInput: Fail to set input" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ return ANEURALNETWORKS_NO_ERROR;
+ }
+ else
+ {
+ VERBOSE(NNAPI::Execution) << "setInput: Cannot handle fully-specified shape on model build "
+ "but omitted input on execution"
+ << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ }
+
+ if (type != nullptr)
+ {
+ if (!execution->compareDataType(type, operand_index))
+ {
+ VERBOSE(NNAPI::Execution) << "setInput: Data type mismatch" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (!execution->compareShape(type, operand_index))
+ {
+ VERBOSE(NNAPI::Execution) << "setInput: Shape mismatch" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (NNAPIConvert::calculateSizeFromType(type) != length)
+ {
+ VERBOSE(NNAPI::Execution) << "setInput: Invalid length" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ }
+ else
+ {
+ if (execution->hasUnspecifiedDims(operand_index))
+ {
+ VERBOSE(NNAPI::Execution) << "setInput: Unspecified dimension value" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (execution->getOperandSize(operand_index) != length)
+ {
+ VERBOSE(NNAPI::Execution) << "setInput: Invalid length" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ }
+
+ if (!execution->setInput(index, type, buffer, length))
+ {
+ VERBOSE(NNAPI::Execution) << "setInput: Fail to set input" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksExecution_setOutput(ANeuralNetworksExecution *execution, int32_t index,
+ const ANeuralNetworksOperandType *type, void *buffer,
+ size_t length)
+{
+ // Don't check type
+ // Comment about ANeuralNetworksOperandType in NeuralNetworks.h:
+ // If the input or output is optional and omitted then it need not have a fully specified tensor
+ // operand type
+ if ((execution == nullptr) || ((buffer == nullptr) && (length != 0)))
+ {
+ VERBOSE(NNAPI::Execution) << "setOutput: Incorrect null pointer parameter(s)" << std::endl;
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ if ((buffer != nullptr) && (length == 0))
+ {
+ VERBOSE(NNAPI::Execution) << "setOutput: Zero length output" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ // Handle optional output
+ if (buffer == nullptr)
+ {
+ return ANEURALNETWORKS_NO_ERROR;
+ }
+
+ const auto operand_index = execution->getOutputOperandIndex(index);
+ if (!operand_index.valid())
+ {
+ VERBOSE(NNAPI::Execution) << "setOutput: Invalid output index" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (type != nullptr)
+ {
+ if (!execution->compareDataType(type, operand_index))
+ {
+ VERBOSE(NNAPI::Execution) << "setOutput: Data type mismatch" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (!execution->compareShape(type, operand_index))
+ {
+ VERBOSE(NNAPI::Execution) << "setOutput: Shape mismatch" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (NNAPIConvert::calculateSizeFromType(type) != length)
+ {
+ VERBOSE(NNAPI::Execution) << "setOutput: Invalid length" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ }
+ else
+ {
+ if (execution->hasUnspecifiedDims(operand_index))
+ {
+ VERBOSE(NNAPI::Execution) << "setOutput: Unspecified dimension value" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (execution->getOperandSize(operand_index) != length)
+ {
+ VERBOSE(NNAPI::Execution) << "setOutput: Invalid length" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ }
+
+ if (!execution->setOutput(index, type, buffer, length))
+ {
+ VERBOSE(NNAPI::Execution) << "setOutput: Fail to set output" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksExecution_startCompute(ANeuralNetworksExecution *execution,
+ ANeuralNetworksEvent **event)
+{
+ if ((execution == nullptr) || (event == nullptr))
+ {
+ VERBOSE(NNAPI::Execution) << "startCompute: Incorrect null pointer parameter(s)" << std::endl;
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ // TODO: Handle event
+ auto instance = execution->instance();
+ *event = new (std::nothrow) ANeuralNetworksEvent{instance};
+ if (*event == nullptr)
+ {
+ VERBOSE(NNAPI::Execution) << "startCompute: Fail to create event" << std::endl;
+ return ANEURALNETWORKS_OUT_OF_MEMORY;
+ }
+
+ if (!execution->startExecute())
+ {
+ VERBOSE(NNAPI::Execution) << "startCompute: Fail to start execution" << std::endl;
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksExecution_compute(ANeuralNetworksExecution *execution)
+{
+ if (execution == nullptr)
+ {
+ VERBOSE(NNAPI::Execution) << "Compute: Incorrect null pointer parameter" << std::endl;
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ if (!execution->execute())
+ {
+ VERBOSE(NNAPI::Execution) << "Compute: Fail to execution" << std::endl;
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+void ANeuralNetworksExecution_free(ANeuralNetworksExecution *execution) { delete execution; }
+
+int ANeuralNetworksExecution_setInputFromMemory(ANeuralNetworksExecution *execution, int32_t index,
+ const ANeuralNetworksOperandType *type,
+ const ANeuralNetworksMemory *memory, size_t offset,
+ size_t length)
+{
+ if ((execution == nullptr) || (memory == nullptr))
+ {
+ VERBOSE(NNAPI::Execution) << "setInputFromMemory: Incorrect null pointer parameter(s)"
+ << std::endl;
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ if (length == 0)
+ {
+ VERBOSE(NNAPI::Execution) << "setInputFromMemory: Zero length input" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ const auto operand_index = execution->getInputOperandIndex(index);
+ if (!operand_index.valid())
+ {
+ VERBOSE(NNAPI::Execution) << "setInputFromMemory: Invalid input index" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (type != nullptr)
+ {
+ if (!execution->compareDataType(type, operand_index))
+ {
+ VERBOSE(NNAPI::Execution) << "setInputFromMemory: Data type mismatch" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (!execution->compareShape(type, operand_index))
+ {
+ VERBOSE(NNAPI::Execution) << "setInputFromMemory: Shape mismatch" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (NNAPIConvert::calculateSizeFromType(type) != length)
+ {
+ VERBOSE(NNAPI::Execution) << "setInputFromMemory: Invalid length" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ }
+ else
+ {
+ if (execution->hasUnspecifiedDims(operand_index))
+ {
+ VERBOSE(NNAPI::Execution) << "setInputFromMemory: Unspecified dimension value" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (execution->getOperandSize(operand_index) != length)
+ {
+ VERBOSE(NNAPI::Execution) << "setInputFromMemory: Invalid length" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ }
+
+ if (!memory->vaildAccess(offset, length))
+ {
+ VERBOSE(NNAPI::Execution) << "setInputFromMemory: Invalid memory access" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (!execution->setInput(index, type, reinterpret_cast<const void *>(memory->base() + offset),
+ length))
+ {
+ VERBOSE(NNAPI::Execution) << "setInputFromMemory: Fail to set input" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksExecution_setOutputFromMemory(ANeuralNetworksExecution *execution, int32_t index,
+ const ANeuralNetworksOperandType *type,
+ const ANeuralNetworksMemory *memory, size_t offset,
+ size_t length)
+{
+ if ((execution == nullptr) || (memory == nullptr))
+ {
+ VERBOSE(NNAPI::Execution) << "setOutputFromMemory: Incorrect null pointer parameter(s)"
+ << std::endl;
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ if (length == 0)
+ {
+ VERBOSE(NNAPI::Execution) << "setOutputFromMemory: Zero length input" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ const auto operand_index = execution->getOutputOperandIndex(index);
+ if (!operand_index.valid())
+ {
+ VERBOSE(NNAPI::Execution) << "setOutputFromMemory: Invalid output index" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (type != nullptr)
+ {
+ if (!execution->compareDataType(type, operand_index))
+ {
+ VERBOSE(NNAPI::Execution) << "setOutputFromMemory: Data type mismatch" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (!execution->compareShape(type, operand_index))
+ {
+ VERBOSE(NNAPI::Execution) << "setOutputFromMemory: Shape mismatch" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (NNAPIConvert::calculateSizeFromType(type) != length)
+ {
+ VERBOSE(NNAPI::Execution) << "setOutputFromMemory: Invalid length" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ }
+ else
+ {
+ if (execution->hasUnspecifiedDims(operand_index))
+ {
+ VERBOSE(NNAPI::Execution) << "setOutputFromMemory: Unspecified dimension value" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (execution->getOperandSize(operand_index) != length)
+ {
+ VERBOSE(NNAPI::Execution) << "setOutputFromMemory: Invalid length" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ }
+
+ if (!memory->vaildAccess(offset, length))
+ {
+ VERBOSE(NNAPI::Execution) << "setOutputFromMemory: Invalid memory access" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (!execution->setOutput(index, type, reinterpret_cast<void *>(memory->base() + offset), length))
+ {
+ VERBOSE(NNAPI::Execution) << "setOutputFromMemory: Fail to set input" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksExecution_getOutputOperandRank(ANeuralNetworksExecution *execution,
+ int32_t index, uint32_t *rank)
+{
+ if ((execution == nullptr) || (rank == nullptr))
+ {
+ VERBOSE(NNAPI::Execution) << "getOutputOperandRank: Incorrect null pointer parameter(s)"
+ << std::endl;
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ const auto operand_index = execution->getOutputOperandIndex(index);
+ if (!operand_index.valid())
+ {
+ VERBOSE(NNAPI::Execution) << "getOutputOperandRank: Invalid output index" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (!execution->getOutputOperandRank(index, rank))
+ {
+ VERBOSE(NNAPI::Execution) << "getOutputOperandRank: Fail to get rank" << std::endl;
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksExecution_getOutputOperandDimensions(ANeuralNetworksExecution *execution,
+ int32_t index, uint32_t *dimensions)
+{
+ if ((execution == nullptr) || (dimensions == nullptr))
+ {
+ VERBOSE(NNAPI::Execution) << "getOutputOperandDimensions: Incorrect null pointer parameter(s)"
+ << std::endl;
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ const auto operand_index = execution->getOutputOperandIndex(index);
+ if (!operand_index.valid())
+ {
+ VERBOSE(NNAPI::Execution) << "getOutputOperandDimensions: Invalid output index" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (!execution->getOutputOperandDimensions(index, dimensions))
+ {
+ VERBOSE(NNAPI::Execution) << "getOutputOperandDimensions: Fail to get rank" << std::endl;
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
diff --git a/runtime/onert/frontend/nnapi/memory.cc b/runtime/onert/frontend/nnapi/memory.cc
new file mode 100644
index 000000000..6e568a926
--- /dev/null
+++ b/runtime/onert/frontend/nnapi/memory.cc
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <NeuralNetworks.h>
+#include <sys/mman.h>
+#include <new>
+#include <memory>
+
+#include <memory>
+#include "wrapper/ANeuralNetworksMemory.h"
+
+int ANeuralNetworksMemory_createFromFd(size_t size, int protect, int fd, size_t offset,
+ ANeuralNetworksMemory **memory)
+{
+ if (memory == nullptr)
+ {
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ *memory = new (std::nothrow) ANeuralNetworksMemory{size, protect, fd, offset};
+ if (*memory == nullptr)
+ {
+ return ANEURALNETWORKS_OUT_OF_MEMORY;
+ }
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+void ANeuralNetworksMemory_free(ANeuralNetworksMemory *memory) { delete memory; }
diff --git a/runtime/onert/frontend/nnapi/model.cc b/runtime/onert/frontend/nnapi/model.cc
new file mode 100644
index 000000000..8c7bd1789
--- /dev/null
+++ b/runtime/onert/frontend/nnapi/model.cc
@@ -0,0 +1,416 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <NeuralNetworks.h>
+#include <NeuralNetworksEx.h>
+
+#include <new>
+
+#include "wrapper/ANeuralNetworksModel.h"
+#include "wrapper/ANeuralNetworksMemory.h"
+#include "util/logging.h"
+
+int ANeuralNetworksModel_create(ANeuralNetworksModel **model)
+{
+ if (model == nullptr)
+ {
+ VERBOSE(NNAPI::Model) << "create: Incorrect null pointer parameter" << std::endl;
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ *model = new (std::nothrow) ANeuralNetworksModel{};
+ if (*model == nullptr)
+ {
+ VERBOSE(NNAPI::Model) << "create: Fail to create model object" << std::endl;
+ return ANEURALNETWORKS_OUT_OF_MEMORY;
+ }
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+void ANeuralNetworksModel_free(ANeuralNetworksModel *model) { delete model; }
+
+int ANeuralNetworksModel_addOperand(ANeuralNetworksModel *model,
+ const ANeuralNetworksOperandType *type)
+{
+ if ((model == nullptr) || (type == nullptr))
+ {
+ VERBOSE(NNAPI::Model) << "addOperand: Incorrect null pointer parameter(s)" << std::endl;
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ if (model->isFinished())
+ {
+ VERBOSE(NNAPI::Model) << "addOperand: Already finished" << std::endl;
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+
+ // scale and zeroPoint should be zero for scalars and non-fixed point tensors
+ // Quantized:
+ // scale: a 32 bit floating point value greater than zero
+ // zeroPoint: a 32 bit integer, in range [0, 255]
+ if (type->type == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM)
+ {
+ if (!(type->scale > 0.0f))
+ {
+ VERBOSE(NNAPI::Model) << "addOperand: Incorrect scale value for quantization" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if ((type->zeroPoint < 0) || (type->zeroPoint > 255))
+ {
+ VERBOSE(NNAPI::Model) << "addOperand: Incorrect zeroPoint value for quantization"
+ << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ }
+ // NOTE Validation of scale and zeroPoint would be skipped for a while.
+ // We do not know whether scalar type can have scale and zeroPoint.
+ // To pass ValidationTest and GeneratedTest, this validation code
+ // would not be implemented until we can define this issue clearly.
+ //
+ // scale and zeroPoint should be zero for scalars and non-fixed point tensors
+ // else if ((type->scale != 0.0f) || (type->zeroPoint != 0))
+ // {
+ // return ANEURALNETWORKS_BAD_DATA;
+ // }
+
+ // dimensionCount should be zero for scalars
+ if ((type->dimensionCount != 0) &&
+ ((type->type == ANEURALNETWORKS_FLOAT32) || (type->type == ANEURALNETWORKS_INT32) ||
+ (type->type == ANEURALNETWORKS_UINT32)))
+ {
+ VERBOSE(NNAPI::Model) << "addOperand: Incorrect data type" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (!model->addOperand(type))
+ {
+ VERBOSE(NNAPI::Model) << "addOperand: Fail to add operand" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksModel_setOperandValue(ANeuralNetworksModel *model, int32_t index,
+ const void *buffer, size_t length)
+{
+ const bool optional_operand = ((buffer == nullptr) && (length == 0));
+
+ if ((model == nullptr) || ((buffer == nullptr) && (length != 0)))
+ {
+ VERBOSE(NNAPI::Model) << "setOperandValue: Incorrect null pointer parameter(s)" << std::endl;
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ if (model->isFinished())
+ {
+ VERBOSE(NNAPI::Model) << "setOperandValue: Already finished" << std::endl;
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+
+ // Negative index value is not allowed
+ if (index < 0)
+ {
+ VERBOSE(NNAPI::Model) << "setOperandValue: Invalid index value (negative)" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ // NOTE OperandIndex uses uint32_t as its underlying type as various NNAPI
+ // functions such as ANeuralNetworksModel_addOperation use uint32_t to represent operand
+ // index
+ // ANeuralNetworksModel_setOperandValue, however, uses int32_t to represent operand index.
+ //
+ // Below, static_cast<uint32_t>(...) is introduced to eliminate compiler warning.
+ uint32_t ind = static_cast<uint32_t>(index);
+
+ if (!model->isExistOperand(ind))
+ {
+ VERBOSE(NNAPI::Model) << "setOperandValue: Invalid index value (not exist)" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (!optional_operand && (model->operandSize(ind) != length))
+ {
+ VERBOSE(NNAPI::Model) << "setOperandValue: Invalid data length" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (model->isUsageSet(ind))
+ {
+ VERBOSE(NNAPI::Model) << "setOperandValue: Already set operand" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ // NNAPI spec in NeuralNetworks.h
+ // For values of length greater than ANEURALNETWORKS_MAX_SIZE_OF_IMMEDIATELY_COPIED_VALUES,
+ // the application is responsible for not changing the content of this region
+ // until all executions using this model have completed
+ bool copy_value = false;
+ if (length <= ANEURALNETWORKS_MAX_SIZE_OF_IMMEDIATELY_COPIED_VALUES)
+ {
+ copy_value = true;
+ }
+
+ if (!model->setOperandValue(ind, buffer, length, optional_operand, copy_value))
+ {
+ VERBOSE(NNAPI::Model) << "setOperandValue: Fail to set operand value" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksModel_setOperandValueFromMemory(ANeuralNetworksModel *model, int32_t index,
+ const ANeuralNetworksMemory *memory,
+ size_t offset, size_t length)
+{
+ if ((model == nullptr) || (memory == nullptr))
+ {
+ VERBOSE(NNAPI::Model) << "setOperandValueFromMemory: Incorrect null pointer parameter(s)"
+ << std::endl;
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ if (model->isFinished())
+ {
+ VERBOSE(NNAPI::Model) << "setOperandValueFromMemory: Already finished" << std::endl;
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+
+ // Negative index value is not allowed
+ if (index < 0)
+ {
+ VERBOSE(NNAPI::Model) << "setOperandValueFromMemory: Invalid index value (negative)"
+ << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ // NOTE OperandIndex uses uint32_t as its underlying type as various NNAPI
+ // functions such as ANeuralNetworksModel_addOperation use uint32_t to represent operand
+ // index
+ // ANeuralNetworksModel_setOperandValue, however, uses int32_t to represent operand index.
+ //
+ // Below, static_cast<uint32_t>(...) is introduced to eliminate compiler warning.
+ uint32_t ind = static_cast<uint32_t>(index);
+
+ if (!model->isExistOperand(ind))
+ {
+ VERBOSE(NNAPI::Model) << "setOperandValueFromMemory: Invalid index value (not exist)"
+ << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if ((model->operandSize(ind) != length) || (memory->size() < (offset + length)))
+ {
+ VERBOSE(NNAPI::Model) << "setOperandValueFromMemory: Invalid data length" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (model->isUsageSet(ind))
+ {
+ VERBOSE(NNAPI::Model) << "setOperandValueFromMemory: Already set operand" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (!model->setOperandValue(ind, memory->base() + offset, length))
+ {
+ VERBOSE(NNAPI::Model) << "setOperandValueFromMemory: Fail to set operand value" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksModel_addOperation(ANeuralNetworksModel *model,
+ ANeuralNetworksOperationType type, uint32_t inputCount,
+ const uint32_t *inputs, uint32_t outputCount,
+ const uint32_t *outputs)
+{
+ if ((model == nullptr) || (inputs == nullptr) || (outputs == nullptr))
+ {
+ VERBOSE(NNAPI::Model) << "addOperation: Incorrect null pointer parameter(s)" << std::endl;
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ if (model->isFinished())
+ {
+ VERBOSE(NNAPI::Model) << "addOperation: Already finished" << std::endl;
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+
+ const ANeuralNetworksOperationType FIRST_OPERATION = ANEURALNETWORKS_ADD;
+ const ANeuralNetworksOperationType LAST_OPERATION = ANEURALNETWORKS_RESIZE_NEAREST_NEIGHBOR;
+ if ((type < FIRST_OPERATION) || (type > LAST_OPERATION))
+ {
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ for (uint32_t i = 0; i < outputCount; i++)
+ {
+ if (model->isUsageSet(outputs[i]))
+ {
+ VERBOSE(NNAPI::Model) << "addOperation: Already set output operand" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ }
+
+ if (!model->addOperation(type, inputCount, inputs, outputCount, outputs))
+ {
+ VERBOSE(NNAPI::Model) << "addOperation: Fail to add operation" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksModel_addOperationEx(ANeuralNetworksModel *model,
+ ANeuralNetworksOperationTypeEx type, uint32_t inputCount,
+ const uint32_t *inputs, uint32_t outputCount,
+ const uint32_t *outputs)
+{
+ if ((model == nullptr) || (inputs == nullptr) || (outputs == nullptr))
+ {
+ VERBOSE(NNAPI::Model) << "addOperation: Incorrect null pointer parameter(s)" << std::endl;
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ if (model->isFinished())
+ {
+ VERBOSE(NNAPI::Model) << "addOperation: Already finished" << std::endl;
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+
+ const ANeuralNetworksOperationTypeEx FIRST_OPERATION = ANEURALNETWORKS_CAST_EX;
+ const ANeuralNetworksOperationTypeEx LAST_OPERATION = ANEURALNETWORKS_SPLIT_V_EX;
+ if ((type < FIRST_OPERATION) || (type > LAST_OPERATION))
+ {
+ VERBOSE(NNAPI::Model) << "addOperation: Invalid operation type" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ for (uint32_t i = 0; i < outputCount; i++)
+ {
+ if (model->isUsageSet(outputs[i]))
+ {
+ VERBOSE(NNAPI::Model) << "addOperation: Already set output operand" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ }
+
+ if (!model->addOperationEx(type, inputCount, inputs, outputCount, outputs))
+ {
+ VERBOSE(NNAPI::Model) << "addOperation: Fail to add operation" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksModel_identifyInputsAndOutputs(ANeuralNetworksModel *model, uint32_t inputCount,
+ const uint32_t *inputs, uint32_t outputCount,
+ const uint32_t *outputs)
+{
+ if ((model == nullptr) || (inputs == nullptr) || (outputs == nullptr))
+ {
+ VERBOSE(NNAPI::Model) << "identifyInputsAndOutputs: Incorrect null pointer parameter(s)"
+ << std::endl;
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ if (model->isFinished())
+ {
+ VERBOSE(NNAPI::Model) << "identifyInputsAndOutputs: Already finished" << std::endl;
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+
+ for (uint32_t n = 0; n < inputCount; ++n)
+ {
+ uint32_t ind = inputs[n];
+ if (model->isUsageSet(ind))
+ {
+ VERBOSE(NNAPI::Model) << "identifyInputsAndOutputs: Already set input operand" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (!model->addModelInput(ind))
+ {
+ VERBOSE(NNAPI::Model) << "identifyInputsAndOutputs: Fail to add input" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ }
+
+ for (uint32_t n = 0; n < outputCount; ++n)
+ {
+ uint32_t ind = outputs[n];
+
+ if (!model->isOperationOutput(ind))
+ {
+ VERBOSE(NNAPI::Model) << "identifyInputsAndOutputs: Need to set output operand" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ if (!model->addModelOutput(ind))
+ {
+ VERBOSE(NNAPI::Model) << "identifyInputsAndOutputs: Fail to add output" << std::endl;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ }
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksModel_finish(ANeuralNetworksModel *model)
+{
+ if (model == nullptr)
+ {
+ VERBOSE(NNAPI::Model) << "finish: Incorrect null pointer parameter" << std::endl;
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ if (model->isFinished())
+ {
+ VERBOSE(NNAPI::Model) << "finish: Already finished" << std::endl;
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+
+ if (!model->finish())
+ {
+ VERBOSE(NNAPI::Model) << "finish: Fail to generate internal graph" << std::endl;
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+int ANeuralNetworksModel_relaxComputationFloat32toFloat16(ANeuralNetworksModel *model, bool allow)
+{
+ if (model == nullptr)
+ {
+ VERBOSE(NNAPI::Model) << "relaxComputationFloat32toFloat16: Incorrect null pointer parameter"
+ << std::endl;
+ return ANEURALNETWORKS_UNEXPECTED_NULL;
+ }
+
+ if (model->isFinished())
+ {
+ VERBOSE(NNAPI::Model) << "relaxComputationFloat32toFloat16: Already finished" << std::endl;
+ return ANEURALNETWORKS_BAD_STATE;
+ }
+
+ model->allowFloat32toFloat16(allow);
+
+ return ANEURALNETWORKS_NO_ERROR;
+}
diff --git a/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksCompilation.cc b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksCompilation.cc
new file mode 100644
index 000000000..81cd38f4f
--- /dev/null
+++ b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksCompilation.cc
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ANeuralNetworksCompilation.h"
+
+#include "util/logging.h"
+
+// TODO Support multiple subgraphs
+ANeuralNetworksCompilation::ANeuralNetworksCompilation(const ANeuralNetworksModel *model) noexcept
+ : _subgraphs{model->getSubGraphs()}, _compiler{new onert::compiler::Compiler{_subgraphs}}
+{
+ if (model->allowedToFp16())
+ {
+ _compiler->enableToFp16();
+ }
+}
+
+bool ANeuralNetworksCompilation::finish() noexcept
+{
+ try
+ {
+ _executors = _compiler->compile();
+ }
+ catch (const std::exception &e)
+ {
+ VERBOSE(EXCEPTION) << e.what() << std::endl;
+
+ return false;
+ }
+
+ return true;
+}
diff --git a/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksCompilation.h b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksCompilation.h
new file mode 100644
index 000000000..5f0650b9a
--- /dev/null
+++ b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksCompilation.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __COMPILATION_H__
+#define __COMPILATION_H__
+
+#include "ANeuralNetworksModel.h"
+
+#include "compiler/Compiler.h"
+#include "ir/Graph.h"
+#include "ir/Subgraphs.h"
+#include "exec/IExecutor.h"
+
+struct ANeuralNetworksCompilation
+{
+public:
+ ANeuralNetworksCompilation(const ANeuralNetworksModel *model) noexcept;
+
+public:
+ bool finish() noexcept;
+
+ onert::compiler::State state(void) noexcept { return _compiler->state(); }
+ void publish(std::shared_ptr<onert::exec::ExecutorMap> &executors) noexcept
+ {
+ executors = _executors;
+ }
+
+private:
+ std::shared_ptr<onert::ir::Subgraphs> _subgraphs;
+ std::shared_ptr<onert::compiler::Compiler> _compiler;
+ std::shared_ptr<onert::exec::ExecutorMap> _executors;
+};
+
+#endif
diff --git a/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksEvent.cc b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksEvent.cc
new file mode 100644
index 000000000..2bea729be
--- /dev/null
+++ b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksEvent.cc
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ANeuralNetworksEvent.h"
+
+#include "exec/Execution.h"
+#include "util/logging.h"
+
+ANeuralNetworksEvent::ANeuralNetworksEvent(const std::shared_ptr<onert::exec::Execution> &execution)
+ : _execution{execution}
+{
+ // DO NOTHING
+}
+
+bool ANeuralNetworksEvent::waitFinish(void) noexcept
+{
+ try
+ {
+ _execution->waitFinish();
+ }
+ catch (const std::exception &e)
+ {
+ VERBOSE(EXCEPTION) << e.what() << std::endl;
+
+ return false;
+ }
+
+ return true;
+}
diff --git a/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksEvent.h b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksEvent.h
new file mode 100644
index 000000000..7b462d3d6
--- /dev/null
+++ b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksEvent.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __EVENT_H__
+#define __EVENT_H__
+
+#include <NeuralNetworks.h>
+
+#include <memory>
+
+namespace onert
+{
+namespace exec
+{
+class Execution;
+} // namespace exec
+} // namespace onert
+
+struct ANeuralNetworksEvent
+{
+public:
+ ANeuralNetworksEvent(const std::shared_ptr<onert::exec::Execution> &execution);
+
+public:
+ bool waitFinish(void) noexcept;
+
+private:
+ const std::shared_ptr<onert::exec::Execution> _execution;
+};
+
+#endif
diff --git a/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksExecution.cc b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksExecution.cc
new file mode 100644
index 000000000..6114b74b0
--- /dev/null
+++ b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksExecution.cc
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ANeuralNetworksExecution.h"
+#include "NNAPIConvert.h"
+#include "util/logging.h"
+
+const onert::ir::OperandIndex ANeuralNetworksExecution::getInputOperandIndex(int32_t index) noexcept
+{
+ if (index < 0)
+ {
+ // Negative index: return invalid index
+ return onert::ir::OperandIndex{};
+ }
+
+ uint32_t cast_index = static_cast<uint32_t>(index);
+ if (cast_index >= _execution->primary_subgraph().getInputs().size())
+ {
+ // Return invalid index
+ return onert::ir::OperandIndex{};
+ }
+
+ onert::ir::IOIndex input_index{cast_index};
+ const auto operand_index = _execution->primary_subgraph().getInputs().at(input_index);
+ return operand_index;
+}
+
+const onert::ir::OperandIndex
+ANeuralNetworksExecution::getOutputOperandIndex(int32_t index) noexcept
+{
+ if (index < 0)
+ {
+ // Negative index: return invalid index
+ return onert::ir::OperandIndex{};
+ }
+
+ uint32_t cast_index = static_cast<uint32_t>(index);
+ if (cast_index >= _execution->primary_subgraph().getOutputs().size())
+ {
+ // Return invalid index
+ return onert::ir::OperandIndex{};
+ }
+
+ onert::ir::IOIndex output_index{cast_index};
+ const auto operand_index = _execution->primary_subgraph().getOutputs().at(output_index);
+ return operand_index;
+}
+
+bool ANeuralNetworksExecution::compareDataType(const ANeuralNetworksOperandType *type,
+ const onert::ir::OperandIndex index) noexcept
+{
+ try
+ {
+ const auto operand_type = _execution->primary_subgraph().operands().at(index).typeInfo();
+ const auto typeInfo = NNAPIConvert::getTypeInfo(type);
+
+ if (operand_type != typeInfo)
+ {
+ // Data type mismatch
+ return false;
+ }
+ }
+ catch (const std::exception &e)
+ {
+ VERBOSE(EXCEPTION) << e.what() << std::endl;
+
+ return false;
+ }
+
+ return true;
+}
+
+bool ANeuralNetworksExecution::compareShape(const ANeuralNetworksOperandType *type,
+ const onert::ir::OperandIndex index) noexcept
+{
+ // Passed shape should be specified
+ if (hasUnspecifiedDims(index))
+ {
+ return false;
+ }
+
+ const auto &operand_shape = _execution->primary_subgraph().operands().at(index).shape();
+ const auto &shape_from_type = NNAPIConvert::getShape(type);
+
+ return operand_shape == shape_from_type;
+}
+
+bool ANeuralNetworksExecution::IsOptionalInput(const onert::ir::OperandIndex index) noexcept
+{
+ const auto &operand_shape = _execution->primary_subgraph().operands().at(index).shape();
+ for (int32_t i = 0; i < operand_shape.rank(); ++i)
+ {
+ if (operand_shape.dim(i) != 0)
+ return false;
+ }
+ return true;
+}
+
+bool ANeuralNetworksExecution::hasUnspecifiedDims(const onert::ir::OperandIndex index) noexcept
+{
+ const auto operand_shape = _execution->primary_subgraph().operands().at(index).shape();
+
+ return operand_shape.hasUnspecifiedDims();
+}
+
+size_t ANeuralNetworksExecution::getOperandSize(const onert::ir::OperandIndex index) noexcept
+{
+ try
+ {
+ return _execution->primary_subgraph().operands().at(index).operandSize();
+ }
+ catch (const std::exception &e)
+ {
+ VERBOSE(EXCEPTION) << e.what() << std::endl;
+
+ return 0;
+ }
+}
+
+bool ANeuralNetworksExecution::setInput(uint32_t index, const ANeuralNetworksOperandType *type,
+ const void *buffer, size_t length) noexcept
+{
+ try
+ {
+ onert::ir::IOIndex input_index{index};
+ const auto operand_index = getInputOperandIndex(index);
+
+ const auto type_info = _execution->primary_subgraph().operands().at(operand_index).typeInfo();
+ const auto shape = (type != nullptr)
+ ? NNAPIConvert::getShape(type)
+ : _execution->primary_subgraph().operands().at(operand_index).shape();
+
+ // NOTE The nnapi does not provide setting io_layout and not support changing layout. In other
+ // words, we can assume that io_layout from nnapi always is the same as layout of the used
+ // model.
+ // TODO Set layout of model
+ _execution->setInput(input_index, type_info, shape, buffer, length, onert::ir::Layout::NHWC);
+ }
+ catch (const std::exception &e)
+ {
+ VERBOSE(EXCEPTION) << e.what() << std::endl;
+
+ return false;
+ }
+
+ return true;
+}
+
+bool ANeuralNetworksExecution::setOptionalInput(uint32_t index,
+ const ANeuralNetworksOperandType *type,
+ const void *buffer, size_t length) noexcept
+{
+ assert(type == nullptr);
+ assert(buffer == nullptr);
+ assert(length == 0);
+ try
+ {
+ onert::ir::IOIndex input_index{index};
+ const auto operand_index = getInputOperandIndex(index);
+
+ const auto type_info = _execution->primary_subgraph().operands().at(operand_index).typeInfo();
+ const auto shape = (type != nullptr)
+ ? NNAPIConvert::getShape(type)
+ : _execution->primary_subgraph().operands().at(operand_index).shape();
+
+ // ANeuralNetworksExecution::setInput() uses only shape information
+ ANeuralNetworksOperandType optional_input_type;
+ optional_input_type.dimensionCount = shape.rank();
+ std::vector<uint32_t> dims(optional_input_type.dimensionCount);
+ for (uint32_t i = 0; i < optional_input_type.dimensionCount; ++i)
+ {
+ dims.at(i) = shape.dim(i);
+ }
+ optional_input_type.dimensions = dims.data();
+
+ return setInput(index, &optional_input_type, buffer, length);
+ }
+ catch (const std::exception &e)
+ {
+ VERBOSE(EXCEPTION) << e.what() << std::endl;
+
+ return false;
+ }
+
+ return true;
+}
+
+bool ANeuralNetworksExecution::setOutput(uint32_t index, const ANeuralNetworksOperandType *type,
+ void *buffer, size_t length) noexcept
+{
+ try
+ {
+ onert::ir::IOIndex output_index{index};
+ const auto operand_index = getOutputOperandIndex(index);
+
+ const auto type_info = _execution->primary_subgraph().operands().at(operand_index).typeInfo();
+ const auto shape = (type != nullptr)
+ ? NNAPIConvert::getShape(type)
+ : _execution->primary_subgraph().operands().at(operand_index).shape();
+
+ // NOTE The nnapi does not provide setting io_layout and not support changing layout. In other
+ // words, we can assume that io_layout from nnapi always is the same as layout of the used
+ // model.
+ // TODO Set layout of model
+ _execution->setOutput(output_index, type_info, shape, buffer, length, onert::ir::Layout::NHWC);
+ }
+ catch (const std::exception &e)
+ {
+ VERBOSE(EXCEPTION) << e.what() << std::endl;
+
+ return false;
+ }
+
+ return true;
+}
+
+bool ANeuralNetworksExecution::startExecute(void) noexcept
+{
+ try
+ {
+ _execution->startExecute();
+ }
+ catch (const std::exception &e)
+ {
+ VERBOSE(EXCEPTION) << e.what() << std::endl;
+
+ return false;
+ }
+
+ return true;
+}
+
+bool ANeuralNetworksExecution::execute(void) noexcept
+{
+ try
+ {
+ _execution->execute();
+ }
+ catch (const std::exception &e)
+ {
+ VERBOSE(EXCEPTION) << e.what() << std::endl;
+
+ return false;
+ }
+
+ return true;
+}
+
+const std::shared_ptr<onert::exec::Execution> ANeuralNetworksExecution::instance(void) noexcept
+{
+ return _execution;
+}
+
+bool ANeuralNetworksExecution::getOutputOperandRank(uint32_t index, uint32_t *rank) noexcept
+{
+ try
+ {
+ onert::ir::IOIndex output_index{index};
+
+ // Check execution is finished
+ if (!_execution->isFinished())
+ {
+ return false;
+ }
+
+ const auto shape = _execution->getOutputShape(output_index);
+ if (shape.hasUnspecifiedDims())
+ {
+ throw std::runtime_error{"Internal error: Output tensor has unspecified dims"};
+ }
+
+ *rank = shape.rank();
+ }
+ catch (const std::exception &e)
+ {
+ VERBOSE(EXCEPTION) << e.what() << std::endl;
+
+ return false;
+ }
+
+ return true;
+}
+
+bool ANeuralNetworksExecution::getOutputOperandDimensions(uint32_t index, uint32_t *dimensions)
+{
+ try
+ {
+ onert::ir::IOIndex output_index{index};
+
+ // Check execution is finished
+ if (!_execution->isFinished())
+ {
+ return false;
+ }
+
+ const auto shape = _execution->getOutputShape(output_index);
+ if (shape.hasUnspecifiedDims())
+ {
+ throw std::runtime_error{"Internal error: Output tensor has unspecified dims"};
+ }
+
+ for (int i = 0; i < shape.rank(); i++)
+ {
+ auto dim = shape.dim(i);
+
+ if (dim <= 0)
+ {
+ throw std::runtime_error{"Invalid dimension value"};
+ }
+
+ dimensions[i] = static_cast<uint32_t>(dim);
+ }
+ }
+ catch (const std::exception &e)
+ {
+ VERBOSE(EXCEPTION) << e.what() << std::endl;
+
+ return false;
+ }
+
+ return true;
+}
diff --git a/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksExecution.h b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksExecution.h
new file mode 100644
index 000000000..1f4b868f6
--- /dev/null
+++ b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksExecution.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __EXECUTION_H__
+#define __EXECUTION_H__
+
+#include <NeuralNetworks.h>
+
+#include <memory>
+
+#include "exec/Execution.h"
+
+struct ANeuralNetworksExecution
+{
+public:
+ ANeuralNetworksExecution(const std::shared_ptr<onert::exec::ExecutorMap> &executors)
+ : _execution{std::make_shared<onert::exec::Execution>(executors)}
+ {
+ // DO NOTHING
+ }
+
+public:
+ bool setInput(uint32_t index, const ANeuralNetworksOperandType *type, const void *buffer,
+ size_t length) noexcept;
+ bool setOptionalInput(uint32_t index, const ANeuralNetworksOperandType *type, const void *buffer,
+ size_t length) noexcept;
+ bool setOutput(uint32_t index, const ANeuralNetworksOperandType *type, void *buffer,
+ size_t length) noexcept;
+ bool startExecute(void) noexcept;
+ bool execute(void) noexcept;
+
+ const onert::ir::OperandIndex getInputOperandIndex(int32_t index) noexcept;
+ const onert::ir::OperandIndex getOutputOperandIndex(int32_t index) noexcept;
+ bool compareDataType(const ANeuralNetworksOperandType *type,
+ const onert::ir::OperandIndex index) noexcept;
+ bool compareShape(const ANeuralNetworksOperandType *type,
+ const onert::ir::OperandIndex index) noexcept;
+ bool IsOptionalInput(const onert::ir::OperandIndex index) noexcept;
+ bool hasUnspecifiedDims(const onert::ir::OperandIndex index) noexcept;
+ size_t getOperandSize(const onert::ir::OperandIndex index) noexcept;
+ const std::shared_ptr<onert::exec::Execution> instance(void) noexcept;
+
+ /**
+ * @brief Get output operand's rank
+ * @param[in] index Output index
+ * @param[out] rank Output operand's rank
+ * @return @c true if success to get rank, otherwise @c false
+ */
+ bool getOutputOperandRank(uint32_t index, uint32_t *rank) noexcept;
+ /**
+ * @brief Get dimensions of the output operand
+ * @param[in] index Output index
+ * @param[out] dimensions Output operand's dimensions
+ * @return @c true if success to get rank, otherwise @c false
+ * @note This must be called after execution is finished to get resolved output shape
+ * unspecified in model
+ */
+ bool getOutputOperandDimensions(uint32_t index, uint32_t *dimensions);
+
+private:
+ std::shared_ptr<onert::exec::Execution> _execution;
+};
+
+#endif
diff --git a/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksMemory.cc b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksMemory.cc
new file mode 100644
index 000000000..9cc100585
--- /dev/null
+++ b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksMemory.cc
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <NeuralNetworks.h>
+#include <sys/mman.h>
+
+#include "ANeuralNetworksMemory.h"
+
+//
+// ANeuralNetworksMemory
+//
+ANeuralNetworksMemory::ANeuralNetworksMemory(size_t size, int protect, int fd, size_t offset)
+{
+ _base = reinterpret_cast<uint8_t *>(mmap(nullptr, size, protect, MAP_PRIVATE, fd, offset));
+ _size = size;
+}
+
+ANeuralNetworksMemory::~ANeuralNetworksMemory() { munmap(reinterpret_cast<void *>(_base), _size); }
+
+bool ANeuralNetworksMemory::vaildAccess(size_t offset, size_t length) const
+{
+ if ((offset >= _size) || (length > _size))
+ {
+ return false;
+ }
+
+ if ((offset + length) >= _size)
+ {
+ return false;
+ }
+
+ return true;
+}
diff --git a/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksMemory.h b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksMemory.h
new file mode 100644
index 000000000..48a1bc5fc
--- /dev/null
+++ b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksMemory.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MEMORY_H__
+#define __MEMORY_H__
+
+#include <cstdint>
+
+struct ANeuralNetworksMemory
+{
+public:
+ ANeuralNetworksMemory(size_t size, int protect, int fd, size_t offset);
+ ~ANeuralNetworksMemory();
+
+public:
+ size_t size(void) const { return _size; }
+ uint8_t *base(void) { return _base; }
+ uint8_t *base(void) const { return _base; }
+ bool vaildAccess(size_t offset, size_t length) const;
+
+private:
+ size_t _size;
+ uint8_t *_base;
+};
+
+#endif // __MEMORY_H__
diff --git a/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksModel.cc b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksModel.cc
new file mode 100644
index 000000000..97b820aea
--- /dev/null
+++ b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksModel.cc
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ANeuralNetworksModel.h"
+#include "OperationFactory.h"
+#include "NNAPIConvert.h"
+
+#include "ir/Operations.Include.h"
+#include "util/logging.h"
+
+#include <memory>
+
+//
+// ANeuralNetworksModel
+//
+ANeuralNetworksModel::ANeuralNetworksModel() noexcept
+ : _optional_operands{}, _operand_usages{}, _allowFloat32toFloat16{false}
+{
+ _graph = std::make_shared<onert::ir::Graph>();
+}
+
+bool ANeuralNetworksModel::addOperand(const ANeuralNetworksOperandType *type) noexcept
+{
+ try
+ {
+ const auto shape = NNAPIConvert::getShape(type);
+ const auto typeInfo = NNAPIConvert::getTypeInfo(type);
+ _graph->addOperand(shape, typeInfo);
+ _operand_usages.emplace_back(OperandUsage::NOT_DEFINED);
+ }
+ catch (const std::exception &e)
+ {
+ VERBOSE(EXCEPTION) << e.what() << std::endl;
+
+ return false;
+ }
+
+ return true;
+}
+
+bool ANeuralNetworksModel::setOperandValue(uint32_t index, const void *buffer, size_t length,
+ bool optional, bool copy) noexcept
+{
+ const onert::ir::OperandIndex ind{index};
+
+ try
+ {
+ _operand_usages[index] = OperandUsage::CONSTANT;
+
+ // Remain operands.at(ind).data()->base() as nullptr for optional operand
+ // This will be filled when model finished
+ if (optional)
+ {
+ setOptionalOperand(ind);
+ }
+
+ using onert::ir::CachedData;
+ using onert::ir::ExternalData;
+ if (copy)
+ {
+ _graph->operands().at(ind).data(
+ std::make_unique<CachedData>(reinterpret_cast<const uint8_t *>(buffer), length));
+ }
+ else
+ {
+ _graph->operands().at(ind).data(
+ std::make_unique<ExternalData>(reinterpret_cast<const uint8_t *>(buffer), length));
+ }
+ }
+ catch (const std::exception &e)
+ {
+ VERBOSE(EXCEPTION) << e.what() << std::endl;
+
+ return false;
+ }
+
+ return true;
+}
+
+bool ANeuralNetworksModel::addOperation(ANeuralNetworksOperationType type, uint32_t inputCount,
+ const uint32_t *inputs, uint32_t outputCount,
+ const uint32_t *outputs) noexcept
+{
+ try
+ {
+ for (uint32_t i = 0; i < outputCount; i++)
+ {
+ _operand_usages[outputs[i]] = OperandUsage::OPERATION_OUTPUT;
+ }
+
+ auto &factory = OperationFactory::get();
+ OperationFactory::Param param{inputCount, inputs, outputCount, outputs};
+
+ auto node = factory.create(type, param, _graph->operands());
+ _graph->addOperation(std::unique_ptr<onert::ir::Operation>{node});
+
+ // TODO Move these codes to delegate.cpp
+ if (type == ANEURALNETWORKS_FULLY_CONNECTED)
+ {
+ const auto &input_operand =
+ _graph->operands().at(node->getInputs().at(onert::ir::operation::FullyConnected::INPUT));
+ auto &weights_operand =
+ _graph->operands().at(node->getInputs().at(onert::ir::operation::FullyConnected::WEIGHT));
+ if (input_operand.typeInfo().type() == onert::ir::DataType::FLOAT32 &&
+ weights_operand.typeInfo().type() == onert::ir::DataType::QUANT_UINT8_ASYMM)
+ {
+ weights_operand.type(onert::ir::DataType::QUANT_INT8_SYMM);
+ }
+ }
+ }
+ catch (const std::exception &e)
+ {
+ VERBOSE(EXCEPTION) << e.what() << std::endl;
+
+ return false;
+ }
+
+ return true;
+}
+
+bool ANeuralNetworksModel::addOperationEx(ANeuralNetworksOperationTypeEx type, uint32_t inputCount,
+ const uint32_t *inputs, uint32_t outputCount,
+ const uint32_t *outputs) noexcept
+{
+ try
+ {
+ for (uint32_t i = 0; i < outputCount; i++)
+ {
+ _operand_usages[outputs[i]] = OperandUsage::OPERATION_OUTPUT;
+ }
+
+ auto &factory = OperationFactory::get();
+ OperationFactory::Param param{inputCount, inputs, outputCount, outputs};
+
+ auto node = factory.create(type, param, _graph->operands());
+ _graph->addOperation(std::unique_ptr<onert::ir::Operation>{node});
+ }
+ catch (const std::exception &e)
+ {
+ return false;
+ }
+ return true;
+}
+
+bool ANeuralNetworksModel::addModelInput(uint32_t index) noexcept
+{
+ try
+ {
+ _operand_usages[index] = OperandUsage::MODEL_INPUT;
+
+ const onert::ir::OperandIndex ind{index};
+ _graph->addInput(ind);
+ }
+ catch (const std::exception &e)
+ {
+ VERBOSE(EXCEPTION) << e.what() << std::endl;
+
+ return false;
+ }
+
+ return true;
+}
+bool ANeuralNetworksModel::addModelOutput(uint32_t index) noexcept
+{
+ try
+ {
+ const onert::ir::OperandIndex ind{index};
+
+ // Duplicated output is not allowed
+ if (_graph->getOutputs().contains(ind))
+ {
+ return false;
+ }
+
+ _graph->addOutput(ind);
+ }
+ catch (const std::exception &e)
+ {
+ VERBOSE(EXCEPTION) << e.what() << std::endl;
+
+ return false;
+ }
+
+ return true;
+}
+
+void ANeuralNetworksModel::allowFloat32toFloat16(bool allow) noexcept
+{
+ _allowFloat32toFloat16 = allow;
+}
+
+bool ANeuralNetworksModel::finish() noexcept
+{
+ try
+ {
+ fillOptionalOperand();
+
+ _graph->finishBuilding();
+
+ _operand_usages.clear();
+ }
+ catch (const std::exception &e)
+ {
+ VERBOSE(EXCEPTION) << e.what() << '\n';
+
+ return false;
+ }
+
+ return true;
+}
+
+bool ANeuralNetworksModel::isFinished() noexcept { return !_graph->isBuildingPhase(); }
+
+bool ANeuralNetworksModel::isExistOperand(uint32_t index) noexcept
+{
+ return _graph->operands().exist(onert::ir::OperandIndex{index});
+}
+
+size_t ANeuralNetworksModel::operandSize(uint32_t index) noexcept
+{
+ try
+ {
+ return _graph->operands().at(onert::ir::OperandIndex{index}).operandSize();
+ }
+ catch (const std::exception &e)
+ {
+ VERBOSE(EXCEPTION) << e.what() << '\n';
+
+ return 0;
+ }
+}
+
+bool ANeuralNetworksModel::isUsageSet(uint32_t index) noexcept
+{
+ return (_operand_usages[index] != OperandUsage::NOT_DEFINED);
+}
+
+bool ANeuralNetworksModel::isOperationOutput(uint32_t index) noexcept
+{
+ return (_operand_usages[index] == OperandUsage::OPERATION_OUTPUT);
+}
+
+void ANeuralNetworksModel::setOptionalOperand(const onert::ir::OperandIndex idx)
+{
+ _optional_operands.insert(idx);
+}
+
+void ANeuralNetworksModel::fillOptionalOperand(void)
+{
+ _graph->operations().iterate([&](const onert::ir::OperationIndex &, onert::ir::Operation &node) {
+ for (auto input : node.getInputs())
+ {
+ // TODO fill default value for optional operands
+ if (_optional_operands.find(input) != _optional_operands.end())
+ {
+ throw std::runtime_error{"Optional operand is not supported yet"};
+ }
+ }
+ });
+}
+
+std::shared_ptr<onert::ir::Subgraphs> ANeuralNetworksModel::getSubGraphs() const
+{
+ auto all_subgs = std::make_shared<onert::ir::Subgraphs>();
+
+ all_subgs->push(onert::ir::SubgraphIndex{0}, _graph);
+ // TODO Find all child subgraphs and copy them to all_subgs
+ // Must find the same subgraph by using to compare pointer of subgraphs and set subgraph's index
+ // to operands of control flow operations
+ // Must clean all child subgraphs's pointer to prevent memory leak in case of that graph has
+ // subgraph itself recursively
+
+ return all_subgs;
+}
diff --git a/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksModel.h b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksModel.h
new file mode 100644
index 000000000..df6c97c44
--- /dev/null
+++ b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksModel.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MODEL_H__
+#define __MODEL_H__
+
+#include <unordered_set>
+#include <NeuralNetworks.h>
+#include <NeuralNetworksEx.h>
+
+#include "ir/Graph.h"
+#include "ir/Subgraphs.h"
+
+struct ANeuralNetworksModel
+{
+public:
+ enum class OperandUsage
+ {
+ NOT_DEFINED = 0,
+ MODEL_INPUT,
+ CONSTANT,
+ OPERATION_OUTPUT,
+ };
+
+public:
+ ANeuralNetworksModel() noexcept;
+
+public:
+ bool addOperand(const ANeuralNetworksOperandType *type) noexcept;
+ bool setOperandValue(uint32_t index, const void *buffer, size_t length, bool optional = false,
+ bool copy = false) noexcept;
+ bool addOperation(ANeuralNetworksOperationType type, uint32_t inputCount, const uint32_t *inputs,
+ uint32_t outputCount, const uint32_t *outputs) noexcept;
+ bool addOperationEx(ANeuralNetworksOperationTypeEx type, uint32_t inputCount,
+ const uint32_t *inputs, uint32_t outputCount,
+ const uint32_t *outputs) noexcept;
+ bool addModelInput(uint32_t index) noexcept;
+ bool addModelOutput(uint32_t index) noexcept;
+ void allowFloat32toFloat16(bool allow) noexcept;
+ bool allowedToFp16() const noexcept { return _allowFloat32toFloat16; }
+ bool finish() noexcept;
+
+ onert::ir::Graph &deref(void) { return *_graph; }
+ bool isFinished() noexcept;
+ bool isExistOperand(uint32_t index) noexcept;
+ size_t operandSize(uint32_t index) noexcept;
+ bool isUsageSet(uint32_t index) noexcept;
+ bool isOperationOutput(uint32_t index) noexcept;
+ std::shared_ptr<onert::ir::Subgraphs> getSubGraphs() const;
+
+private:
+ void setOptionalOperand(const onert::ir::OperandIndex idx);
+ void fillOptionalOperand(void);
+
+private:
+ std::shared_ptr<onert::ir::Graph> _graph;
+ std::unordered_set<onert::ir::OperandIndex> _optional_operands;
+ std::vector<OperandUsage> _operand_usages;
+ bool _allowFloat32toFloat16;
+};
+
+#endif // __MODEL_H__
diff --git a/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksModel.test.cc b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksModel.test.cc
new file mode 100644
index 000000000..bb42f2b08
--- /dev/null
+++ b/runtime/onert/frontend/nnapi/wrapper/ANeuralNetworksModel.test.cc
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "ANeuralNetworksModel.h"
+
+TEST(MODEL, neg_model_build)
+{
+ ANeuralNetworksModel model;
+ ASSERT_FALSE(model.isFinished());
+}
diff --git a/runtime/onert/frontend/nnapi/wrapper/NNAPIConvert.cc b/runtime/onert/frontend/nnapi/wrapper/NNAPIConvert.cc
new file mode 100644
index 000000000..63d4e3c09
--- /dev/null
+++ b/runtime/onert/frontend/nnapi/wrapper/NNAPIConvert.cc
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NNAPIConvert.h"
+
+#include <numeric>
+
+using namespace onert::ir;
+
+DataType NNAPIConvert::getDataType(OperandCode type)
+{
+ switch (type)
+ {
+ case ANEURALNETWORKS_FLOAT32:
+ case ANEURALNETWORKS_TENSOR_FLOAT32:
+ return DataType::FLOAT32;
+ case ANEURALNETWORKS_INT32:
+ case ANEURALNETWORKS_TENSOR_INT32:
+ return DataType::INT32;
+ case ANEURALNETWORKS_UINT32:
+ return DataType::UINT32;
+ case ANEURALNETWORKS_TENSOR_QUANT8_ASYMM:
+ return DataType::QUANT_UINT8_ASYMM;
+ case ANEURALNETWORKS_TENSOR_QUANT8_SYMM:
+ return DataType::QUANT_INT8_SYMM;
+ case ANEURALNETWORKS_BOOL:
+ case ANEURALNETWORKS_TENSOR_BOOL8:
+ return DataType::BOOL8;
+ default:
+ throw std::runtime_error("Unsupported type");
+ }
+}
+
+TypeInfo NNAPIConvert::getTypeInfo(const ANeuralNetworksOperandType *type)
+{
+ return TypeInfo(getDataType((OperandCode)(type->type)), type->scale, type->zeroPoint);
+}
+
+Shape NNAPIConvert::getShape(const ANeuralNetworksOperandType *type)
+{
+ Shape shape(type->dimensionCount);
+
+ for (uint32_t axis = 0; axis < type->dimensionCount; ++axis)
+ {
+ shape.dim(axis) = type->dimensions[axis];
+ }
+
+ return shape;
+}
+
+size_t NNAPIConvert::calculateSizeFromType(const ANeuralNetworksOperandType *type)
+{
+ auto shape = getShape(type);
+ auto data_type = getDataType((OperandCode)(type->type));
+
+ return shape.num_elements() * sizeOfDataType(data_type);
+}
+
+Activation NNAPIConvert::getFusedActivation(FuseCode act)
+{
+ switch (act)
+ {
+ case ANEURALNETWORKS_FUSED_NONE:
+ return Activation::NONE;
+ case ANEURALNETWORKS_FUSED_RELU:
+ return Activation::RELU;
+ case ANEURALNETWORKS_FUSED_RELU1:
+ return Activation::RELU1;
+ case ANEURALNETWORKS_FUSED_RELU6:
+ return Activation::RELU6;
+ default:
+ throw std::runtime_error("Unsupported activation type");
+ }
+}
+
+PaddingType NNAPIConvert::getPaddingType(PaddingCode type)
+{
+ switch (type)
+ {
+ case ANEURALNETWORKS_PADDING_SAME:
+ return PaddingType::SAME;
+ case ANEURALNETWORKS_PADDING_VALID:
+ return PaddingType::VALID;
+ default:
+ throw std::runtime_error("Unsupported type");
+ }
+}
diff --git a/runtime/onert/frontend/nnapi/wrapper/NNAPIConvert.h b/runtime/onert/frontend/nnapi/wrapper/NNAPIConvert.h
new file mode 100644
index 000000000..4fd985e6e
--- /dev/null
+++ b/runtime/onert/frontend/nnapi/wrapper/NNAPIConvert.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file NNAPIConvert.h
+ * @brief This file contains convereter(s)\n
+ * from NNAPI frontend's struct to onert's internal struct
+ */
+#ifndef __ONERT_NNAPI_CONVERT_H__
+#define __ONERT_NNAPI_CONVERT_H__
+
+#include <NeuralNetworks.h>
+
+#include <ir/TypeInfo.h>
+#include <ir/Shape.h>
+#include <ir/Padding.h>
+#include <ir/InternalType.h>
+
+class NNAPIConvert
+{
+
+public:
+ /**
+ * @brief Convert data type from NNAPI to internal data type
+ * @param[in] type NNAPI's data type
+ * @return onert's internal data type
+ */
+ static onert::ir::DataType getDataType(OperandCode type);
+
+ /**
+ * @brief Convert operand type info from NNAPI to interanl operand type info
+ * @param[in] type NNAPI's operand type
+ * @return onert's internal operand type info
+ */
+ static onert::ir::TypeInfo getTypeInfo(const ANeuralNetworksOperandType *type);
+
+ /**
+ * @brief Convert operand shape info from NNAPI to internal operand shape
+ * @param[in] type NNAPI's operand type
+ * @return onert's internal operand shape
+ */
+ static onert::ir::Shape getShape(const ANeuralNetworksOperandType *type);
+
+ /**
+ * @brief Calcaulate operand size from NNAPI type
+ * @param[in] type NNAPI's operand type
+ * @return Operand size
+ */
+ static size_t calculateSizeFromType(const ANeuralNetworksOperandType *type);
+
+ /**
+ * @brief Convert NNAPI FuseCode to internal activation type
+ * @param[in] act NNAPI's FuseCode type
+ * @return onert's internal activation type
+ */
+ static onert::ir::Activation getFusedActivation(FuseCode act);
+
+ /**
+ * @brief Convert NNAPI PaddingCode to internal padding type
+ * @param[in] type NNAPI's PaddingCode type
+ * @return onert's internal padding type
+ */
+ static onert::ir::PaddingType getPaddingType(PaddingCode type);
+};
+
+#endif // __ONERT_NNAPI_CONVERT_H__
diff --git a/runtime/onert/frontend/nnapi/wrapper/OperationFactory.cc b/runtime/onert/frontend/nnapi/wrapper/OperationFactory.cc
new file mode 100644
index 000000000..a84ce1b8d
--- /dev/null
+++ b/runtime/onert/frontend/nnapi/wrapper/OperationFactory.cc
@@ -0,0 +1,1919 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "OperationFactory.h"
+#include "NNAPIConvert.h"
+
+#include <ir/Operations.Include.h>
+#include <string.h>
+
+namespace
+{
+using namespace onert::ir;
+
+void replaceDataType(Operands &operands, const OperandIndex &index, const DataType type)
+{
+ assert(operands.exist(index));
+ operands.at(index).type(type);
+}
+
+ExplicitPadding makeExplicitPadding(Operands &operands, const OperandIndex &left_index,
+ const OperandIndex &right_index, const OperandIndex &top_index,
+ const OperandIndex &bottom_index)
+{
+ auto left = operands.at(left_index).asScalar<int32_t>();
+ auto right = operands.at(right_index).asScalar<int32_t>();
+ auto top = operands.at(top_index).asScalar<int32_t>();
+ auto bottom = operands.at(bottom_index).asScalar<int32_t>();
+
+ if (left < 0 || right < 0 || top < 0 || bottom < 0)
+ {
+ throw std::runtime_error{"Cannot handle negative explicit padding value"};
+ }
+
+ ExplicitPadding param;
+ param.left = static_cast<uint32_t>(left);
+ param.right = static_cast<uint32_t>(right);
+ param.top = static_cast<uint32_t>(top);
+ param.bottom = static_cast<uint32_t>(bottom);
+
+ return param;
+}
+
+Stride makeStride(Operands &operands, const OperandIndex &horizontal_index,
+ const OperandIndex &vertical_index)
+{
+ auto horizontal = operands.at(horizontal_index).asScalar<int32_t>();
+ auto vertical = operands.at(vertical_index).asScalar<int32_t>();
+
+ if (vertical < 0 || horizontal < 0)
+ {
+ throw std::runtime_error{"Cannot handle negative stride value"};
+ }
+
+ Stride stride;
+ stride.horizontal = static_cast<uint32_t>(horizontal);
+ stride.vertical = static_cast<uint32_t>(vertical);
+
+ return stride;
+}
+
+uint32_t getUint32Scalar(Operands &operands, const OperandIndex index)
+{
+ auto int32_value = operands.at(index).asScalar<int32_t>();
+ if (int32_value < 0)
+ {
+ throw std::runtime_error{"Cannot handle negative value"};
+ }
+
+ return static_cast<uint32_t>(int32_value);
+}
+
+OperationFactory::Generator
+getElementwiseActivationGenerator(const onert::ir::operation::ElementwiseActivation::Type op_type,
+ float alpha = 0.f, float beta = 0.f)
+{
+ return [op_type, alpha, beta](const OperationFactory::Param &init_param, Operands &) {
+ assert(init_param.input_count == 1);
+ assert(init_param.output_count == 1);
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Input Tensor Index
+
+ OperandIndexSequence inputs{init_param.inputs[0]};
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ operation::ElementwiseActivation::Param param;
+ param.op_type = op_type;
+ param.alpha = alpha;
+ param.beta = beta;
+
+ return new operation::ElementwiseActivation{inputs, outputs, param};
+ };
+}
+
+OperationFactory::Generator getElementwiseBinaryGenerator(
+ const onert::ir::operation::ElementwiseBinary::ElementwiseBinaryType op_type)
+{
+ return [op_type](const OperationFactory::Param &init_param, Operands &) {
+ assert(init_param.input_count == 2);
+ assert(init_param.output_count == 1);
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Lefthand side operand
+ // 1 -> Righthand side operand
+
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ operation::ElementwiseBinary::Param param;
+ param.op_type = op_type;
+
+ return new operation::ElementwiseBinary{inputs, outputs, param};
+ };
+}
+
+OperationFactory::Generator
+getElementwiseUnaryGenerator(const onert::ir::operation::ElementwiseUnary::Type op_type)
+{
+ return [op_type](const OperationFactory::Param &init_param, Operands &operands) {
+ assert(init_param.input_count == 1);
+ assert(init_param.output_count == 1);
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Input Tensor Index
+
+ OperandIndexSequence inputs{init_param.inputs[0]};
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ operation::ElementwiseUnary::Param param;
+ param.op_type = op_type;
+
+ if (op_type == operation::ElementwiseUnary::Type::CAST)
+ {
+ // NNAPI uses QUANT_UINT8_ASYMM to represent UINT8 type for ANEURALNETWORKS_CAST's
+ // input/output
+ if (operands.at(inputs.at(0)).typeInfo().type() == DataType::QUANT_UINT8_ASYMM)
+ {
+ replaceDataType(operands, inputs.at(0), DataType::UINT8);
+ }
+ if (operands.at(outputs.at(0)).typeInfo().type() == DataType::QUANT_UINT8_ASYMM)
+ {
+ replaceDataType(operands, outputs.at(0), DataType::UINT8);
+ }
+ }
+
+ return new operation::ElementwiseUnary{inputs, outputs, param};
+ };
+}
+
+OperationFactory::Generator
+getBinaryArithmeticGenerator(const onert::ir::operation::BinaryArithmetic::ArithmeticType op_type)
+{
+ return [op_type](const OperationFactory::Param &init_param, Operands &operands) {
+ assert(init_param.input_count == 3);
+ assert(init_param.output_count == 1);
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Lefthand side operand
+ // 1 -> Righthand side operand
+
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ operation::BinaryArithmetic::Param param;
+ param.arithmetic_type = op_type;
+ const auto activation_index = OperandIndex{init_param.inputs[2]};
+ param.activation =
+ NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
+
+ return new operation::BinaryArithmetic{inputs, outputs, param};
+ };
+}
+
+OperationFactory::Generator
+getPool2DGenerator(const onert::ir::operation::Pool2D::PoolType pool_type)
+{
+ return [pool_type](const OperationFactory::Param &init_param, Operands &operands) {
+ assert(init_param.input_count == 7 || init_param.input_count == 10);
+ assert(init_param.output_count == 1);
+
+ // In common
+ // 0 -> IFM Tensor Index
+ OperandIndexSequence inputs{init_param.inputs[0]};
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ operation::Pool2D::Param param;
+ param.op_type = pool_type;
+ if (init_param.input_count == 7) // support implicit padding
+ {
+ // Each input should be interpreted as follows:
+ //
+ // 1 -> Padding Code (ANEURALNETWORKS_PADDING_SAME or ANEURALNETWORKS_PADDING_VALID) Index
+ // 2 -> Horizontal (over width) Stride Index
+ // 3 -> Vertial (over height) Stride Index
+ // 4 -> Filter Width Index
+ // 5 -> Filter Height Index
+ // 6 -> FuseCode (activation) Index
+
+ const auto padding_index = OperandIndex{init_param.inputs[1]};
+ const auto hstride_index = OperandIndex{init_param.inputs[2]};
+ const auto vstride_index = OperandIndex{init_param.inputs[3]};
+ const auto kw_index = OperandIndex{init_param.inputs[4]};
+ const auto kh_index = OperandIndex{init_param.inputs[5]};
+ const auto activation_index = OperandIndex{init_param.inputs[6]};
+
+ param.padding.type =
+ NNAPIConvert::getPaddingType(operands.at(padding_index).asScalar<PaddingCode>());
+ param.stride = makeStride(operands, hstride_index, vstride_index);
+ param.kw = getUint32Scalar(operands, kw_index);
+ param.kh = operands.at(kh_index).asScalar<uint32_t>();
+ param.activation =
+ NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
+ }
+ else // support explicit padding
+ {
+ // Each input should be interpreted as follows:
+ //
+ // 1 -> Padding_left index
+ // 2 -> Padding_right index
+ // 3 -> Padding_top index
+ // 4 -> Padding_bottom index
+ // 5 -> Horizontal (over width) Stride Index
+ // 6 -> Vertial (over height) Stride Index
+ // 7 -> Filter Width Index
+ // 8 -> Filter Height Index
+ // 9 -> FuseCode (activation) Index
+
+ const auto padding_left_index = OperandIndex{init_param.inputs[1]};
+ const auto padding_right_index = OperandIndex{init_param.inputs[2]};
+ const auto padding_top_index = OperandIndex{init_param.inputs[3]};
+ const auto padding_bottom_index = OperandIndex{init_param.inputs[4]};
+ const auto hstride_index = OperandIndex{init_param.inputs[5]};
+ const auto vstride_index = OperandIndex{init_param.inputs[6]};
+ const auto kw_index = OperandIndex{init_param.inputs[7]};
+ const auto kh_index = OperandIndex{init_param.inputs[8]};
+ const auto activation_index = OperandIndex{init_param.inputs[9]};
+
+ param.padding.type = PaddingType::EXPLICIT;
+ param.padding.param = makeExplicitPadding(operands, padding_left_index, padding_right_index,
+ padding_top_index, padding_bottom_index);
+ param.stride = makeStride(operands, hstride_index, vstride_index);
+ param.kw = getUint32Scalar(operands, kw_index);
+ param.kh = getUint32Scalar(operands, kh_index);
+ param.activation =
+ NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
+ }
+ return new operation::Pool2D{inputs, outputs, param};
+ };
+}
+
+OperationFactory::Generator
+getReduceGenerator(const onert::ir::operation::Reduce::ReduceType reduce_type)
+{
+ return [reduce_type](const OperationFactory::Param &init_param, Operands &operands) {
+ assert(init_param.input_count == 3);
+ assert(init_param.output_count == 1);
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Input Tensor Index
+ // 1 -> Reduced Axes Tensor Index
+ // 2 -> keep_dims Index
+
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ operation::Reduce::Param param;
+ param.reduce_type = reduce_type;
+ param.keep_dims = operands.at(OperandIndex{init_param.inputs[2]}).asScalar<int8_t>() != 0;
+
+ return new operation::Reduce{inputs, outputs, param};
+ };
+}
+
+template <typename T>
+Operation *CreateSimpleUnaryOp(const OperationFactory::Param &init_param, Operands &)
+{
+ assert(init_param.input_count == 1 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Input Tensor Index
+ OperandIndexSequence inputs{init_param.inputs[0]};
+
+ return new T{inputs, outputs};
+}
+
+// A generator function for binary ops with no params
+template <typename T>
+Operation *createSimpleBinaryOp(const OperationFactory::Param &init_param, Operands &)
+{
+ assert(init_param.input_count == 2 && init_param.output_count == 1);
+
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ return new T{inputs, outputs};
+}
+
+OperationFactory::Generator getComparisonGenerator(operation::Comparison::ComparisonType type)
+{
+ return [type](const OperationFactory::Param &init_param, Operands &) -> Operation * {
+ assert(init_param.input_count == 2 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> input0 Tensor Index
+ // 1 -> input1 Tensor Index
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
+
+ operation::Comparison::Param param;
+ param.comparison_type = type;
+
+ return new operation::Comparison{inputs, outputs, param};
+ };
+}
+
+} // namespace
+
+OperationFactory &OperationFactory::get()
+{
+ static OperationFactory factory;
+ return factory;
+}
+
+OperationFactory::OperationFactory()
+{
+ // Each input should be interpreted as follows:
+ // 0 -> Input Tensor Index
+ // 1 -> Block size Index
+ _map[ANEURALNETWORKS_BATCH_TO_SPACE_ND] = createSimpleBinaryOp<operation::BatchToSpaceND>;
+
+ _map[ANEURALNETWORKS_DEPTHWISE_CONV_2D] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert((init_param.input_count == 8 || init_param.input_count == 11) &&
+ init_param.output_count == 1);
+
+ // In common
+ // 0 -> IFM Tensor Index
+ // 1 -> Kernel Tensor Index
+ // 2 -> Bias Tensor Index
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ operation::DepthwiseConv2D::Param param;
+ if (init_param.input_count == 8)
+ {
+ // Imlicit Padding case
+ // Each input should be interpreted as follows:
+ //
+ // 3 -> Padding Code (ANEURALNETWORKS_PADDING_SAME or ANEURALNETWORKS_PADDING_VALID) Index
+ // 4 -> Stride (width) Index
+ // 5 -> Stride (height) INdex
+ // 6 -> Depthwise multiplier
+ // 7 -> Activation Index
+
+ const auto padding_index = OperandIndex{init_param.inputs[3]};
+ const auto hstride_index = OperandIndex{init_param.inputs[4]};
+ const auto vstride_index = OperandIndex{init_param.inputs[5]};
+ const auto multiplier_index = OperandIndex{init_param.inputs[6]};
+ const auto activation_index = OperandIndex{init_param.inputs[7]};
+
+ param.padding.type =
+ NNAPIConvert::getPaddingType(operands.at(padding_index).asScalar<PaddingCode>());
+ param.stride = makeStride(operands, hstride_index, vstride_index);
+ param.multiplier = getUint32Scalar(operands, multiplier_index);
+ param.activation =
+ NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
+ }
+ else
+ {
+ // Explicit Padding case
+ // Each input should be interpreted as follows:
+ //
+ // 3 -> Padding On the Left
+ // 4 -> Padding On the Right
+ // 5 -> Padding On the Top
+ // 6 -> Padding On the Bottom
+ // 7 -> Stride (width) Index
+ // 8 -> Stride (height) Index
+ // 9 -> Depthwise multiplier
+ // 10-> Activation Index
+
+ const auto padding_left_index = OperandIndex{init_param.inputs[3]};
+ const auto padding_right_index = OperandIndex{init_param.inputs[4]};
+ const auto padding_top_index = OperandIndex{init_param.inputs[5]};
+ const auto padding_bottom_index = OperandIndex{init_param.inputs[6]};
+ const auto hstride_index = OperandIndex{init_param.inputs[7]};
+ const auto vstride_index = OperandIndex{init_param.inputs[8]};
+ const auto multiplier_index = OperandIndex{init_param.inputs[9]};
+ const auto activation_index = OperandIndex{init_param.inputs[10]};
+
+ param.padding.type = PaddingType::EXPLICIT;
+ param.padding.param = makeExplicitPadding(operands, padding_left_index, padding_right_index,
+ padding_top_index, padding_bottom_index);
+ param.stride = makeStride(operands, hstride_index, vstride_index);
+ param.multiplier = getUint32Scalar(operands, multiplier_index);
+ param.activation =
+ NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
+ }
+
+ // TODO set dilation
+ param.dilation.width_factor = 1;
+ param.dilation.height_factor = 1;
+
+ return new operation::DepthwiseConv2D{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_MAX_POOL_2D] = getPool2DGenerator(operation::Pool2D::PoolType::MAX);
+
+ _map[ANEURALNETWORKS_AVERAGE_POOL_2D] = getPool2DGenerator(operation::Pool2D::PoolType::AVG);
+
+ _map[ANEURALNETWORKS_CONCATENATION] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count >= 2); // At least one one input tensor and axis
+ assert(init_param.output_count == 1);
+
+ // When there are N + 1 inputs, each input should be interpreted as follows:
+ //
+ // [0, N) -> Input tensors
+ // N -> Axis
+ //
+
+ OperandIndexSequence inputs;
+ for (uint32_t n = 0; n < init_param.input_count - 1; ++n)
+ {
+ inputs.append(OperandIndex{init_param.inputs[n]});
+ }
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ operation::Concat::Param param;
+ const OperandIndex axis_index{init_param.inputs[init_param.input_count - 1]};
+ param.axis = operands.at(axis_index).asScalar<int32_t>();
+
+ return new operation::Concat{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_RESHAPE] = [](const OperationFactory::Param &init_param, Operands &) {
+ assert(init_param.input_count == 2 && init_param.output_count == 1);
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> A tensor, specifying the tensor to be reshaped.
+ // 1 -> A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32, defining the shape of the output
+ // tensor
+
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ operation::Reshape::Param param{};
+
+ return new operation::Reshape{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_FULLY_CONNECTED] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 4 && init_param.output_count == 1);
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> A tensor, specifying the input.
+ // 1 -> A 2-D tensor, specifying the weights
+ // 2 -> A 1-D tensor, specifying the bias
+ // 3 -> An INT32 value, and has to be one of the FuseCode values
+
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ operation::FullyConnected::Param param;
+ const auto activation_index = OperandIndex{init_param.inputs[3]};
+ param.activation =
+ NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
+ param.weights_format = FullyConnectedWeightsFormat::Default;
+
+ return new operation::FullyConnected{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_SOFTMAX] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 2 && init_param.output_count == 1);
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> A 2-D or 4-D tensor, specifying the tensor to be reshaped.
+ // 1 -> FLOAT32 value, specifying the positive scaling factor for the exponent, beta.
+
+ OperandIndexSequence inputs{init_param.inputs[0]};
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ const auto beta_index = OperandIndex{init_param.inputs[1]};
+
+ operation::Softmax::Param param;
+ param.beta = operands.at(beta_index).asScalar<float>();
+
+ return new operation::Softmax{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_CAST] =
+ getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::CAST);
+
+ // ANEURALNETWORKS_CAST_EX is deprecated
+ // TODO Remove ANEURALNETWORKS_CAST_EX
+ _map[ANEURALNETWORKS_CAST_EX] = _map[ANEURALNETWORKS_CAST];
+
+ _map[ANEURALNETWORKS_CONV_2D] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ using operation::Conv2D;
+
+ // inputCount is either 7 or 10 acccording to NN API specification.
+ // - Padding is implicit when inputCount is 7
+ // - Padding is explicit when inputCount is 10
+ assert(init_param.input_count == 7 || init_param.input_count == 10 ||
+ init_param.input_count == 13);
+ assert(init_param.output_count == 1);
+
+ // 0 -> IFM Tensor Index
+ // 1 -> Kernel Tensor Index
+ // 2 -> Bias Tensor Index
+
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ Conv2D::Param param;
+ if (init_param.input_count == 7) // support implicit padding
+ {
+ // Each input should be interpreted as follows:
+ //
+ // 3 -> Padding Code (ANEURALNETWORKS_PADDING_SAME or ANEURALNETWORKS_PADDING_VALID) Index
+ // 4 -> Stride (width) Index
+ // 5 -> Stride (height) INdex
+ // 6 -> Activation Index
+
+ const auto padding_index = OperandIndex{init_param.inputs[3]};
+ const auto hstride_index = OperandIndex{init_param.inputs[4]};
+ const auto vstride_index = OperandIndex{init_param.inputs[5]};
+ const auto activation_index = OperandIndex{init_param.inputs[6]};
+
+ param.padding.type =
+ NNAPIConvert::getPaddingType(operands.at(padding_index).asScalar<PaddingCode>());
+ param.stride = makeStride(operands, hstride_index, vstride_index);
+
+ param.dilation.width_factor = 1;
+ param.dilation.height_factor = 1;
+
+ param.activation =
+ NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
+ }
+ else if (init_param.input_count == 10) // support explicit padding
+ {
+ // Each input should be interpreted as follows:
+ //
+ // 3 -> Padding_left index
+ // 4 -> Padding_right index
+ // 5 -> Padding_top index
+ // 6 -> Padding_bottom index
+ // 7 -> Stride (width) Index
+ // 8 -> Stride (height) INdex
+ // 9 -> Activation Index
+
+ const auto padding_left_index = OperandIndex{init_param.inputs[3]};
+ const auto padding_right_index = OperandIndex{init_param.inputs[4]};
+ const auto padding_top_index = OperandIndex{init_param.inputs[5]};
+ const auto padding_bottom_index = OperandIndex{init_param.inputs[6]};
+ const auto hstride_index = OperandIndex{init_param.inputs[7]};
+ const auto vstride_index = OperandIndex{init_param.inputs[8]};
+ const auto activation_index = OperandIndex{init_param.inputs[9]};
+
+ param.padding.type = PaddingType::EXPLICIT;
+ param.padding.param = makeExplicitPadding(operands, padding_left_index, padding_right_index,
+ padding_top_index, padding_bottom_index);
+ param.stride = makeStride(operands, hstride_index, vstride_index);
+
+ param.dilation.width_factor = 1;
+ param.dilation.height_factor = 1;
+
+ param.activation =
+ NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
+ }
+ else if (init_param.input_count == 13) // support dilation
+ {
+ // Each input should be interpreted as follows:
+ //
+ // 3 -> Padding_left Index
+ // 4 -> Padding_right Index
+ // 5 -> Padding_top Index
+ // 6 -> Padding_bottom Index
+ // 7 -> Stride (width) Index
+ // 8 -> Stride (height) Index
+ // 9 -> Activation Index
+ // 11 -> Dilation (width_factor) Index
+ // 12 -> Dilation (height_factor) INdex
+
+ const auto padding_left_index = OperandIndex{init_param.inputs[3]};
+ const auto padding_right_index = OperandIndex{init_param.inputs[4]};
+ const auto padding_top_index = OperandIndex{init_param.inputs[5]};
+ const auto padding_bottom_index = OperandIndex{init_param.inputs[6]};
+ const auto hstride_index = OperandIndex{init_param.inputs[7]};
+ const auto vstride_index = OperandIndex{init_param.inputs[8]};
+ const auto activation_index = OperandIndex{init_param.inputs[9]};
+ const auto width_factor_index = OperandIndex{init_param.inputs[11]};
+ const auto height_factor_index = OperandIndex{init_param.inputs[12]};
+
+ param.padding.type = PaddingType::EXPLICIT;
+ param.padding.param = makeExplicitPadding(operands, padding_left_index, padding_right_index,
+ padding_top_index, padding_bottom_index);
+ param.stride = makeStride(operands, hstride_index, vstride_index);
+
+ auto width_factor = operands.at(width_factor_index).asScalar<int32_t>();
+ auto height_factor = operands.at(height_factor_index).asScalar<int32_t>();
+
+ param.dilation.width_factor = width_factor;
+ param.dilation.height_factor = height_factor;
+
+ param.activation =
+ NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
+ }
+ else
+ {
+ throw std::runtime_error{"Conv2D: unsupported input operand count"};
+ }
+
+ return new Conv2D{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_ADD] =
+ getBinaryArithmeticGenerator(onert::ir::operation::BinaryArithmetic::ArithmeticType::ADD);
+
+ _map[ANEURALNETWORKS_ADDV2_EX] = _map[ANEURALNETWORKS_ADD];
+
+ _map[ANEURALNETWORKS_REDUCE_SUM] =
+ getReduceGenerator(onert::ir::operation::Reduce::ReduceType::SUM);
+
+ // ANEURALNETWORKS_REDUCE_SUM_EX is deprecated
+ // TODO Remove ANEURALNETWORKS_REDUCE_SUM_EX
+ _map[ANEURALNETWORKS_REDUCE_SUM_EX] = _map[ANEURALNETWORKS_REDUCE_SUM];
+
+ _map[ANEURALNETWORKS_SUB] =
+ getBinaryArithmeticGenerator(onert::ir::operation::BinaryArithmetic::ArithmeticType::SUB);
+
+ _map[ANEURALNETWORKS_SLICE] = [](const OperationFactory::Param &init_param, Operands &) {
+ assert(init_param.input_count == 3 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Input Tensor Index
+ // 1 -> Begins Tensor Index
+ // 2 -> Sizes Tensor Index
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
+
+ return new operation::Slice{inputs, outputs};
+ };
+
+ _map[ANEURALNETWORKS_STRIDED_SLICE] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 7 && init_param.output_count == 1);
+
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2],
+ init_param.inputs[3]};
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 1 -> A 1-D Tensor of {@link ANEURALNETWORKS_TENSOR_INT32}, the starts of
+ // the dimensions of the input tensor to be sliced. The length must be
+ // of rank(input0).
+ // 2 -> A 1-D Tensor of {@link ANEURALNETWORKS_TENSOR_INT32}, the ends of
+ // the dimensions of the input tensor to be sliced. The length must be
+ // of rank(input0).
+ // 3 -> A 1-D Tensor of {@link ANEURALNETWORKS_TENSOR_INT32}, the strides of
+ // the dimensions of the input tensor to be sliced. The length must be
+ // of rank(input0).
+ // 4 -> An {@link ANEURALNETWORKS_INT32} scalar, begin_mask. If the ith bit
+ // of begin_mask is set, begin[i] is ignored and the fullest possible
+ // range in that dimension is used instead.
+ // 5 -> An {@link ANEURALNETWORKS_INT32} scalar, end_mask. If the ith bit of
+ // end_mask is set, end[i] is ignored and the fullest possible range in
+ // that dimension is used instead.
+ // 6 -> An {@link ANEURALNETWORKS_INT32} scalar, shrink_axis_mask. An int32
+ // mask. If the ith bit of shrink_axis_mask is set, it implies that the
+ // ith specification shrinks the dimensionality by 1. A slice of size 1
+ // starting from begin[i] in the dimension must be preserved.
+
+ operation::StridedSlice::Param param;
+
+ param.begin_mask = operands.at(OperandIndex{init_param.inputs[4]}).asScalar<std::int32_t>();
+ param.end_mask = operands.at(OperandIndex{init_param.inputs[5]}).asScalar<std::int32_t>();
+ param.shrink_axis_mask =
+ operands.at(OperandIndex{init_param.inputs[6]}).asScalar<std::int32_t>();
+
+ return new operation::StridedSlice{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_TRANSPOSE] = createSimpleBinaryOp<operation::Transpose>;
+
+ _map[ANEURALNETWORKS_MUL] =
+ getBinaryArithmeticGenerator(onert::ir::operation::BinaryArithmetic::ArithmeticType::MUL);
+
+ _map[ANEURALNETWORKS_SQUEEZE] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 1 || init_param.input_count == 2);
+ assert(init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> An n-D tensor, the tensor to be squeezed.
+ // 1 -> An optional 1-D tensor of ANEURALNETWORKS_TENSOR_INT32. The dimensions to squeeze.
+ // If specified only squeezes the dimensions listed. Otherwise, squeezes all dimensions.
+ // The dimension index starts at 0. An error must be reported if squeezing a dimension that
+ // is not 1.
+
+ // Add mandatory input index
+ OperandIndexSequence inputs{init_param.inputs[0]};
+
+ // Add dims index if specified
+ operation::Squeeze::Param param{};
+ if (init_param.input_count == 2)
+ {
+ auto squeeze_dims_idx = OperandIndex{init_param.inputs[1]};
+ assert(operands.at(squeeze_dims_idx).shape().rank() == 1);
+ assert(operands.at(squeeze_dims_idx).shape().dim(0) >= 0);
+ assert(static_cast<uint32_t>(operands.at(squeeze_dims_idx).shape().dim(0)) <=
+ sizeof(param.dims));
+ param.ndim = operands.at(squeeze_dims_idx).shape().dim(0);
+ if (param.ndim > 0)
+ {
+ assert(operands.at(squeeze_dims_idx).data());
+ memcpy(param.dims, operands.at(squeeze_dims_idx).data()->base(),
+ param.ndim * sizeof(param.dims[0]));
+ }
+ }
+
+ return new operation::Squeeze{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_TANH] = getElementwiseActivationGenerator(
+ onert::ir::operation::ElementwiseActivation::Type::TANH, 1.f, 1.f);
+
+ _map[ANEURALNETWORKS_LOG] = getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::LOG);
+
+ _map[ANEURALNETWORKS_LOGISTIC] = getElementwiseActivationGenerator(
+ onert::ir::operation::ElementwiseActivation::Type::LOGISTIC);
+
+ _map[ANEURALNETWORKS_DIV] =
+ getBinaryArithmeticGenerator(onert::ir::operation::BinaryArithmetic::ArithmeticType::DIV);
+
+ _map[ANEURALNETWORKS_EXP] = getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::EXP);
+
+ // ANEURALNETWORKS_EXP_EX is deprecated
+ // TODO Remove ANEURALNETWORKS_EXP_EX
+ _map[ANEURALNETWORKS_EXP_EX] = _map[ANEURALNETWORKS_EXP];
+
+ // Each input should be interpreted as follows:
+ // 0 -> Input Tensor Index
+ // 1 -> Axis Tensor Index
+ _map[ANEURALNETWORKS_EXPAND_DIMS] = createSimpleBinaryOp<operation::ExpandDims>;
+
+ _map[ANEURALNETWORKS_GREATER] =
+ getComparisonGenerator(operation::Comparison::ComparisonType::Greater);
+ _map[ANEURALNETWORKS_GREATER_EQUAL] =
+ getComparisonGenerator(operation::Comparison::ComparisonType::GreaterEqual);
+ _map[ANEURALNETWORKS_LESS] = getComparisonGenerator(operation::Comparison::ComparisonType::Less);
+ _map[ANEURALNETWORKS_LESS_EQUAL] =
+ getComparisonGenerator(operation::Comparison::ComparisonType::LessEqual);
+ _map[ANEURALNETWORKS_NOT_EQUAL] =
+ getComparisonGenerator(operation::Comparison::ComparisonType::NotEqual);
+ _map[ANEURALNETWORKS_EQUAL] =
+ getComparisonGenerator(operation::Comparison::ComparisonType::Equal);
+
+ // ANEURALNETWORKS_GREATER_EQUAL_EX is deprecated
+ // TODO Remove ANEURALNETWORKS_GREATER_EQUAL_EX
+ _map[ANEURALNETWORKS_GREATER_EQUAL_EX] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 2 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> input0 Tensor Index
+ // 1 -> input1 Tensor Index
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
+
+ operation::Comparison::Param param;
+ param.comparison_type = operation::Comparison::ComparisonType::GreaterEqual;
+
+ // Output operand type must be boolean
+ replaceDataType(operands, outputs.at(0), DataType::BOOL8);
+
+ return new operation::Comparison{inputs, outputs, param};
+ };
+
+ // ANEURALNETWORKS_LESS_EX is deprecated
+ // TODO Remove ANEURALNETWORKS_LESS_EX
+ _map[ANEURALNETWORKS_LESS_EX] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 2 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> input0 Tensor Index
+ // 1 -> input1 Tensor Index
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
+
+ operation::Comparison::Param param;
+ param.comparison_type = operation::Comparison::ComparisonType::Less;
+
+ // Output operand type must be boolean
+ replaceDataType(operands, outputs.at(0), DataType::BOOL8);
+
+ return new operation::Comparison{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_REDUCE_ALL] =
+ getReduceGenerator(onert::ir::operation::Reduce::ReduceType::ALL);
+
+ _map[ANEURALNETWORKS_REDUCE_ANY] =
+ getReduceGenerator(onert::ir::operation::Reduce::ReduceType::ANY);
+
+ _map[ANEURALNETWORKS_REDUCE_MAX] =
+ getReduceGenerator(onert::ir::operation::Reduce::ReduceType::MAX);
+
+ // ANEURALNETWORKS_REDUCE_MAX_EX is deprecated
+ // TODO Remove ANEURALNETWORKS_REDUCE_MAX_EX
+ _map[ANEURALNETWORKS_REDUCE_MAX_EX] = _map[ANEURALNETWORKS_REDUCE_MAX];
+
+ // ANEURALNETWORKS_NOT_EQUAL_EX is deprecated
+ // TODO Remove ANEURALNETWORKS_NOT_EQUAL_EX
+ _map[ANEURALNETWORKS_NOT_EQUAL_EX] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 2 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> input1 Tensor Index
+ // 1 -> input2 Tensor Index
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
+
+ operation::Comparison::Param param;
+ param.comparison_type = operation::Comparison::ComparisonType::NotEqual;
+
+ // Output operand type must be boolean
+ replaceDataType(operands, outputs.at(0), DataType::BOOL8);
+
+ return new operation::Comparison{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_LOGICAL_AND] = getElementwiseBinaryGenerator(
+ operation::ElementwiseBinary::ElementwiseBinaryType::LOGICAL_AND);
+
+ // ANEURALNETWORKS_LOGICAL_AND_EX is deprecated
+ // TODO Remove ANEURALNETWORKS_LOGICAL_AND_EX
+ _map[ANEURALNETWORKS_LOGICAL_AND_EX] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 2 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> input0 Tensor Index
+ // 1 -> input1 Tensor Index
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
+
+ // This operation's operands must be boolean type.
+ replaceDataType(operands, inputs.at(0), DataType::BOOL8);
+ replaceDataType(operands, inputs.at(1), DataType::BOOL8);
+ replaceDataType(operands, outputs.at(0), DataType::BOOL8);
+
+ operation::ElementwiseBinary::Param param;
+ param.op_type = operation::ElementwiseBinary::ElementwiseBinaryType::LOGICAL_AND;
+
+ return new operation::ElementwiseBinary{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_RSQRT] =
+ getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::RSQRT);
+
+ _map[ANEURALNETWORKS_SELECT] = [](const OperationFactory::Param &init_param, Operands &) {
+ assert(init_param.input_count == 3 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Condition Tensor Index
+ // 1 -> Input X(true) Tensor Index
+ // 2 -> Input Y(false) Tensor Index
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
+
+ return new operation::Select{inputs, outputs};
+ };
+
+ _map[ANEURALNETWORKS_SELECT_V2_EX] = [](const OperationFactory::Param &init_param, Operands &) {
+ assert(init_param.input_count == 3 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Condition Tensor Index
+ // 1 -> Input X(true) Tensor Index
+ // 2 -> Input Y(false) Tensor Index
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
+
+ return new operation::Select{inputs, outputs};
+ };
+
+ // ANEURALNETWORKS_RSQRT_EX is deprecated
+ // TODO Remove ANEURALNETWORKS_RSQRT_EX
+ _map[ANEURALNETWORKS_RSQRT_EX] = _map[ANEURALNETWORKS_RSQRT];
+
+ _map[ANEURALNETWORKS_RELU] =
+ getElementwiseActivationGenerator(onert::ir::operation::ElementwiseActivation::Type::RELU,
+ onert::ir::operation::ElementwiseActivation::infinity, 0);
+
+ _map[ANEURALNETWORKS_RESIZE_BILINEAR] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 3 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> IFM Index
+ // 1 -> Height Index
+ // 2 -> Width Index
+ OperandIndexSequence inputs{init_param.inputs[0]};
+
+ operation::ResizeBilinear::Param param;
+ param.height_out = operands.at(OperandIndex{init_param.inputs[1]}).asScalar<int32_t>();
+ param.width_out = operands.at(OperandIndex{init_param.inputs[2]}).asScalar<int32_t>();
+ param.align_corners = false;
+ param.half_pixel_centers = false;
+ return new operation::ResizeBilinear{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_RESIZE_NEAREST_NEIGHBOR] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert((init_param.input_count == 3 || init_param.input_count == 4) &&
+ init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> IFM Index
+ // 1 -> Height Index
+ // 2 -> Width Index
+ OperandIndexSequence inputs{init_param.inputs[0]};
+
+ operation::ResizeNearestNeighbor::Param param;
+ param.height_out = operands.at(OperandIndex{init_param.inputs[1]}).asScalar<int32_t>();
+ param.width_out = operands.at(OperandIndex{init_param.inputs[2]}).asScalar<int32_t>();
+ param.align_corners = false;
+ // The layout input is not supported yet
+ return new operation::ResizeNearestNeighbor{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_RELU1] = getElementwiseActivationGenerator(
+ onert::ir::operation::ElementwiseActivation::Type::RELU, 1.f, -1.f);
+
+ _map[ANEURALNETWORKS_RELU6] = getElementwiseActivationGenerator(
+ onert::ir::operation::ElementwiseActivation::Type::RELU, 6.f, 0.f);
+
+ _map[ANEURALNETWORKS_REVERSE_EX] = [](const OperationFactory::Param &init_param, Operands &) {
+ assert(init_param.input_count == 2 && init_param.output_count == 1);
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Input Tensor Index
+ // 1 -> Axis Tensor Index
+
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ return new operation::Reverse{inputs, outputs};
+ };
+
+ _map[ANEURALNETWORKS_RNN] = [](const OperationFactory::Param &init_param, Operands &operands) {
+ assert(init_param.input_count == 6 && init_param.output_count == 2);
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Input Tensor Index
+ // 1 -> Weights Tensor Index
+ // 2 -> Recurrent Weights Tensor Index
+ // 3 -> Bias Tensor Index
+ // 4 -> Hidden state (in) Index
+ // 5 -> Activation Index
+
+ OperandIndexSequence inputs;
+ for (uint32_t n = 0; n < init_param.input_count - 1; ++n)
+ {
+ inputs.append(OperandIndex{init_param.inputs[n]});
+ }
+ OperandIndexSequence outputs;
+ for (uint32_t n = 0; n < init_param.output_count; ++n)
+ {
+ outputs.append(OperandIndex{init_param.outputs[n]});
+ }
+
+ operation::RNN::Param param;
+ const auto activation_index = OperandIndex{init_param.inputs[5]};
+ param.activation =
+ NNAPIConvert::getFusedActivation(operands.at(activation_index).asScalar<FuseCode>());
+
+ return new operation::RNN{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_FLOOR] =
+ getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::FLOOR);
+
+ _map[ANEURALNETWORKS_SPACE_TO_BATCH_ND] = [](const OperationFactory::Param &init_param,
+ Operands &) {
+ assert(init_param.input_count == 3 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Input Tensor Index
+ // 1 -> Block size Index
+ // 2 -> Paddings Index
+ OperandIndexSequence inputs;
+ for (uint32_t n = 0; n < init_param.input_count; ++n)
+ {
+ inputs.append(OperandIndex{init_param.inputs[n]});
+ }
+
+ return new operation::SpaceToBatchND{inputs, outputs};
+ };
+
+ _map[ANEURALNETWORKS_SPACE_TO_DEPTH] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 2 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Input Tensor Index
+ // 1 -> Block size Index
+ OperandIndexSequence inputs{init_param.inputs[0]};
+
+ operation::SpaceToDepth::Param param;
+ param.block_size = operands.at(OperandIndex{init_param.inputs[1]}).asScalar<std::int32_t>();
+
+ return new operation::SpaceToDepth{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_L2_POOL_2D] = getPool2DGenerator(operation::Pool2D::PoolType::L2);
+
+ _map[ANEURALNETWORKS_EMBEDDING_LOOKUP] = [](const OperationFactory::Param &init_param,
+ Operands &) {
+ assert(init_param.input_count == 2 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Lookups Index
+ // 1 -> Values Index
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
+
+ return new operation::EmbeddingLookup{inputs, outputs};
+ };
+
+ _map[ANEURALNETWORKS_L2_NORMALIZATION] = [](const OperationFactory::Param &init_param,
+ Operands &) {
+ assert(init_param.input_count == 1 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ // 0 -> input Tensor Index
+ OperandIndexSequence inputs{init_param.inputs[0]};
+
+ return new operation::L2Normalization{inputs, outputs};
+ };
+
+ _map[ANEURALNETWORKS_HASHTABLE_LOOKUP] = [](const OperationFactory::Param &init_param,
+ Operands &) {
+ assert(init_param.input_count == 3 && init_param.output_count == 2);
+
+ // Each output should be interpreted as follows:
+ //
+ // 0 -> Output Index
+ // 1 -> Hits Index
+ OperandIndexSequence outputs{init_param.outputs[0], init_param.outputs[1]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Lookups Index
+ // 1 -> Keys Index
+ // 2 -> Values Index
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
+
+ return new operation::HashtableLookup{inputs, outputs};
+ };
+
+ _map[ANEURALNETWORKS_PRELU] = [](const OperationFactory::Param &init_param, Operands &) {
+ assert(init_param.input_count == 2 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> input Tensor Index
+ // 1 -> alpha Tensor Index
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
+
+ return new operation::PReLU{inputs, outputs};
+ };
+
+ // ANEURALNETWORKS_PRELU_EX is deprecated
+ // TODO Remove ANEURALNETWORKS_PRELU_EX
+ _map[ANEURALNETWORKS_PRELU_EX] = _map[ANEURALNETWORKS_PRELU];
+
+ _map[ANEURALNETWORKS_TRANSPOSE_CONV_EX] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 6 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Output Shape Index
+ // 1 -> Weights Index
+ // 2 -> Input Tensor Index
+ // 3 -> Padding Type
+ // 4 -> Stride width
+ // 5 -> Stride height
+
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
+
+ operation::TransposeConv::Param param;
+
+ const auto padding_index = OperandIndex{init_param.inputs[3]};
+ const auto hstride_index = OperandIndex{init_param.inputs[4]};
+ const auto vstride_index = OperandIndex{init_param.inputs[5]};
+
+ param.padding.type =
+ NNAPIConvert::getPaddingType(operands.at(padding_index).asScalar<PaddingCode>());
+ param.stride = makeStride(operands, hstride_index, vstride_index);
+
+ return new operation::TransposeConv{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_SQRT] =
+ getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::SQRT);
+
+ // ANEURALNETWORKS_SQRT_EX is deprecated
+ // TODO Remove ANEURALNETWORKS_SQRT_EX
+ _map[ANEURALNETWORKS_SQRT_EX] = _map[ANEURALNETWORKS_SQRT];
+
+ _map[ANEURALNETWORKS_LOGICAL_OR] = getElementwiseBinaryGenerator(
+ operation::ElementwiseBinary::ElementwiseBinaryType::LOGICAL_OR);
+
+ // ANEURALNETWORKS_LOGICAL_OR_EX is deprecated
+ // TODO Remove ANEURALNETWORKS_LOGICAL_OR_EX
+ _map[ANEURALNETWORKS_LOGICAL_OR_EX] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 2 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> input0 Tensor Index
+ // 1 -> input1 Tensor Index
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
+
+ // This operation's operands must be boolean type.
+ replaceDataType(operands, inputs.at(0), DataType::BOOL8);
+ replaceDataType(operands, inputs.at(1), DataType::BOOL8);
+ replaceDataType(operands, outputs.at(0), DataType::BOOL8);
+
+ operation::ElementwiseBinary::Param param;
+ param.op_type = operation::ElementwiseBinary::ElementwiseBinaryType::LOGICAL_OR;
+
+ return new operation::ElementwiseBinary{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_LOGICAL_NOT] =
+ getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::LOGICAL_NOT);
+
+ // ANEURALNETWORKS_LOGICAL_NOT_EX is deprecated
+ // TODO Remove ANEURALNETWORKS_LOGICAL_NOT_EX
+ _map[ANEURALNETWORKS_LOGICAL_NOT_EX] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 1 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> input Tensor Index
+ OperandIndexSequence inputs{init_param.inputs[0]};
+
+ // This operation's operands must be boolean type.
+ replaceDataType(operands, inputs.at(0), DataType::BOOL8);
+ replaceDataType(operands, outputs.at(0), DataType::BOOL8);
+
+ operation::ElementwiseUnary::Param param;
+ param.op_type = operation::ElementwiseUnary::Type::LOGICAL_NOT;
+
+ return new operation::ElementwiseUnary{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_LSTM] = [](const OperationFactory::Param &init_param, Operands &operands) {
+ assert(init_param.input_count == 23 && init_param.output_count == 4);
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Input Tensor Index
+ // 1 -> Input to Input Tensor Index
+ // 2 -> Input to Forget Tensor Index
+ // 3 -> Input to Cell Tensor Index
+ // 4 -> Input to Output Tensor Index
+ // 5 -> Recurrent to Input Weights Tensor Index
+ // 6 -> Recurrent to Forget Weights Tensor Index
+ // 7 -> Recurrent to Cell Weights Tensor Index
+ // 8 -> Recurrent to Output Weights Tensor Index
+ // 9 -> Cell to Input Weights Tensor Index
+ // 10 -> Cell to Forget Weights Tensor Index
+ // 11 -> Cell to Output Weights Tensor Index
+ // 12 -> Input Gate Bias Tensor Index
+ // 13 -> Forget Gate Bias Tensor Index
+ // 14 -> Cell Bias Tensor Index
+ // 15 -> Output Gate Bias Tensor Index
+ // 16 -> Projection Weights Tensor Index
+ // 17 -> Projection Bias Tensor Index
+ // 18 -> Output State In Tensor Index
+ // 19 -> Cell State In Tensor Index
+ OperandIndexSequence inputs;
+ for (uint32_t n = 0; n < init_param.input_count - 3; ++n)
+ {
+ inputs.append(OperandIndex{init_param.inputs[n]});
+ }
+
+ // Each output should be interpreted as follows:
+ //
+ // 0 -> Scratch Buffer Tensor Index
+ // 1 -> Output State Out Tensor Index
+ // 2 -> Cell State Out Tensor Index
+ // 3 -> Output Tensor Index
+ OperandIndexSequence outputs;
+ for (uint32_t n = 0; n < init_param.output_count; ++n)
+ {
+ outputs.append(OperandIndex{init_param.outputs[n]});
+ }
+
+ operation::LSTM::Param param;
+ const auto activation_index = OperandIndex{init_param.inputs[20]};
+ switch (operands.at(activation_index).asScalar<int32_t>())
+ {
+ case 0:
+ param.activation = Activation::NONE;
+ break;
+ case 1:
+ param.activation = Activation::RELU;
+ break;
+ case 2:
+ param.activation = Activation::RELU1;
+ break;
+ case 3:
+ param.activation = Activation::RELU6;
+ break;
+ case 4:
+ param.activation = Activation::TANH;
+ break;
+ case 6:
+ param.activation = Activation::SIGMOID;
+ break;
+ default:
+ throw std::runtime_error("Unsupported activation type");
+ break;
+ }
+ param.cell_threshold = operands.at(OperandIndex{init_param.inputs[21]}).asScalar<float>();
+ param.projection_threshold = operands.at(OperandIndex{init_param.inputs[22]}).asScalar<float>();
+ // This is initialization to prevent warning or error by static code analyzer. LSTM operation
+ // does not need time_major
+ param.time_major = false;
+
+ return new operation::LSTM{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_UNIDIRECTIONAL_SEQUENCE_LSTM] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert((init_param.input_count >= 24 || init_param.input_count <= 28) &&
+ (init_param.output_count >= 1 && init_param.output_count <= 3));
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Input Tensor Index
+ // 1 -> Input to Input Tensor Index
+ // 2 -> Input to Forget Tensor Index
+ // 3 -> Input to Cell Tensor Index
+ // 4 -> Input to Output Tensor Index
+ // 5 -> Recurrent to Input Weights Tensor Index
+ // 6 -> Recurrent to Forget Weights Tensor Index
+ // 7 -> Recurrent to Cell Weights Tensor Index
+ // 8 -> Recurrent to Output Weights Tensor Index
+ // 9 -> Cell to Input Weights Tensor Index
+ // 10 -> Cell to Forget Weights Tensor Index
+ // 11 -> Cell to Output Weights Tensor Index
+ // 12 -> Input Gate Bias Tensor Index
+ // 13 -> Forget Gate Bias Tensor Index
+ // 14 -> Cell Bias Tensor Index
+ // 15 -> Output Gate Bias Tensor Index
+ // 16 -> Projection Weights Tensor Index
+ // 17 -> Projection Bias Tensor Index
+ // 18 -> Output State In Tensor Index
+ // 19 -> Cell State In Tensor Index
+ assert(init_param.input_count - 3 > 20);
+ OperandIndexSequence inputs;
+ for (uint32_t n = 0; n < 20; ++n)
+ {
+ inputs.append(OperandIndex{init_param.inputs[n]});
+ }
+
+ // 24 -> Input Layer Normalization Weights Tensor Index
+ // 25 -> Forget Layer Normalization Weights Tensor Index
+ // 26 -> Cell Layer Normalization Weights Tensor Index
+ // 27 -> Output Layer Normalization Weights Tensor Index
+ if (init_param.input_count > 24)
+ {
+ for (uint32_t n = 24; n < 28; ++n)
+ {
+ if (init_param.input_count > n)
+ {
+ inputs.append(OperandIndex{init_param.inputs[n]});
+ }
+ }
+ }
+
+ // Each output should be interpreted as follows:
+ //
+ // 0 -> Output Tensor Index -> 3
+ // 1 -> Output State Out Tensor Index
+ // 2 -> Cell State Out Tensor Index
+ const OperandIndex scratch_buffer_index;
+ OperandIndex output_state_index =
+ init_param.output_count >= 2 ? OperandIndex{init_param.outputs[1]} : OperandIndex();
+ OperandIndex cell_state_index =
+ init_param.output_count >= 3 ? OperandIndex{init_param.outputs[2]} : OperandIndex();
+ const OperandIndex output_index = OperandIndex{init_param.outputs[0]};
+ OperandIndexSequence outputs{scratch_buffer_index, output_state_index, cell_state_index,
+ output_index};
+
+ operation::LSTM::Param param;
+ const auto activation_index = OperandIndex{init_param.inputs[20]};
+ switch (operands.at(activation_index).asScalar<int32_t>())
+ {
+ case 0:
+ param.activation = Activation::NONE;
+ break;
+ case 1:
+ param.activation = Activation::RELU;
+ break;
+ case 2:
+ param.activation = Activation::RELU1;
+ break;
+ case 3:
+ param.activation = Activation::RELU6;
+ break;
+ case 4:
+ param.activation = Activation::TANH;
+ break;
+ case 6:
+ param.activation = Activation::SIGMOID;
+ break;
+ default:
+ throw std::runtime_error("Unsupported activation type");
+ break;
+ }
+ param.cell_threshold = operands.at(OperandIndex{init_param.inputs[21]}).asScalar<float>();
+ param.projection_threshold = operands.at(OperandIndex{init_param.inputs[22]}).asScalar<float>();
+ param.time_major = operands.at(OperandIndex{init_param.inputs[23]}).asScalar<bool>();
+
+ return new operation::LSTM{inputs, outputs, param};
+ };
+
+ // ANEURALNETWORKS_EQUAL_EX is deprecated
+ // TODO Remove ANEURALNETWORKS_EQUAL_EX
+ _map[ANEURALNETWORKS_EQUAL_EX] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 2 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> input0 Tensor Index
+ // 1 -> input1 Tensor Index
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
+
+ operation::Comparison::Param param;
+ param.comparison_type = operation::Comparison::ComparisonType::Equal;
+
+ // Output operand type must be boolean
+ replaceDataType(operands, outputs.at(0), DataType::BOOL8);
+
+ return new operation::Comparison{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_SQUARED_DIFFERENCE_EX] = [](const OperationFactory::Param &init_param,
+ Operands &) {
+ assert(init_param.input_count == 2 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> LHS Tensor Index
+ // 1 -> RHS Tensor Index
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
+
+ return new operation::SquaredDifference{inputs, outputs};
+ };
+
+ _map[ANEURALNETWORKS_TOPK_V2] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 2 && init_param.output_count == 2);
+
+ // Each output should be interpreted as follows:
+ //
+ // 0 -> Index for Output Values
+ // 1 -> Index for Output Indices
+ OperandIndexSequence outputs{init_param.outputs[0], init_param.outputs[1]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Index for Input Data
+ // 1 -> Index for K
+ OperandIndexSequence inputs{init_param.inputs[0]};
+
+ operation::TopKV2::Param param;
+ param.k = operands.at(OperandIndex{init_param.inputs[1]}).asScalar<std::int32_t>();
+
+ return new operation::TopKV2{inputs, outputs, param};
+ };
+
+ // ANEURALNETWORKS_CAST_EX is deprecated
+ // TODO Remove ANEURALNETWORKS_CAST_EX
+ _map[ANEURALNETWORKS_TOPK_V2_EX] = _map[ANEURALNETWORKS_TOPK_V2];
+
+ _map[ANEURALNETWORKS_GATHER] = [](const OperationFactory::Param &init_param, Operands &operands) {
+ assert(init_param.input_count == 3 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> input Tensor Index
+ // 1 -> axis Index
+ // 2 -> indices Tensor Index
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[2]};
+
+ operation::Gather::Param param;
+ param.axis = operands.at(OperandIndex{init_param.inputs[1]}).asScalar<int32_t>();
+
+ return new operation::Gather{inputs, outputs, param};
+ };
+
+ // ANEURALNETWORKS_GATHER_EX is deprecated
+ // TODO Remove ANEURALNETWORKS_GATHER_EX
+ _map[ANEURALNETWORKS_GATHER_EX] = _map[ANEURALNETWORKS_GATHER];
+
+ _map[ANEURALNETWORKS_NEG] = getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::NEG);
+
+ // ANEURALNETWORKS_NEG_EX is deprecated
+ // TODO Remove ANEURALNETWORKS_NEG_EX
+ _map[ANEURALNETWORKS_NEG_EX] = _map[ANEURALNETWORKS_NEG];
+
+ _map[ANEURALNETWORKS_ABS] = getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::ABS);
+
+ // ANEURALNETWORKS_ABS_EX is deprecated
+ // TODO Remove ANEURALNETWORKS_ABS_EX
+ _map[ANEURALNETWORKS_ABS_EX] = _map[ANEURALNETWORKS_ABS];
+
+ _map[ANEURALNETWORKS_ARGMAX] = [](const OperationFactory::Param &init_param, Operands &) {
+ assert(init_param.input_count == 2 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Input Tensor Index
+ // 1 -> Axis Tensor Index
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
+
+ operation::ArgMax::Param param;
+ // NNAPI ARGMAX output type is always int32
+ param.output_type = DataType::INT32;
+
+ return new operation::ArgMax{inputs, outputs, param};
+ };
+
+ // ANEURALNETWORKS_ARGMAX_EX is deprecated
+ // TODO Remove ANEURALNETWORKS_ARGMAX_EX
+ _map[ANEURALNETWORKS_ARGMAX_EX] = _map[ANEURALNETWORKS_ARGMAX];
+
+ _map[ANEURALNETWORKS_DEQUANTIZE] =
+ getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::DEQUANTIZE);
+
+ _map[ANEURALNETWORKS_MEAN] = [](const OperationFactory::Param &init_param, Operands &operands) {
+ assert(init_param.input_count == 3 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> ifm Tensor Index
+ // 1 -> axis Tensor Index
+ // 2 -> keep_dims Index
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
+
+ operation::Reduce::Param param;
+ param.reduce_type = operation::Reduce::ReduceType::MEAN;
+ param.keep_dims = operands.at(OperandIndex{init_param.inputs[2]}).asScalar<int32_t>() != 0;
+
+ return new operation::Reduce{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_LOCAL_RESPONSE_NORMALIZATION] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 5 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ OperandIndexSequence inputs{init_param.inputs[0]};
+
+ operation::LocalResponseNormalization::Param param;
+ param.radius = operands.at(OperandIndex{init_param.inputs[1]}).asScalar<std::int32_t>();
+ param.bias = operands.at(OperandIndex{init_param.inputs[2]}).asScalar<float>();
+ param.alpha = operands.at(OperandIndex{init_param.inputs[3]}).asScalar<float>();
+ param.beta = operands.at(OperandIndex{init_param.inputs[4]}).asScalar<float>();
+
+ return new operation::LocalResponseNormalization{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_DEPTH_TO_SPACE] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 2 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Input Tensor Index
+ // 1 -> Block size Index
+ OperandIndexSequence inputs{init_param.inputs[0]};
+
+ operation::DepthToSpace::Param param;
+ param.block_size = operands.at(OperandIndex{init_param.inputs[1]}).asScalar<std::int32_t>();
+
+ return new operation::DepthToSpace{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_PACK_EX] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count >= 3 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+ OperandIndexSequence inputs;
+ for (uint32_t n = 0; n < init_param.input_count - 2; ++n)
+ {
+ inputs.append(OperandIndex{init_param.inputs[n]});
+ }
+
+ operation::Pack::Param param;
+ const auto num_index = OperandIndex{init_param.inputs[init_param.input_count - 2]};
+ const auto axis_index = OperandIndex{init_param.inputs[init_param.input_count - 1]};
+ param.num = operands.at(num_index).asScalar<int32_t>();
+ param.axis = operands.at(axis_index).asScalar<int32_t>();
+
+ return new operation::Pack{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_REDUCE_MIN] =
+ getReduceGenerator(onert::ir::operation::Reduce::ReduceType::MIN);
+
+ // ANEURALNETWORKS_REDUCE_MIN_EX is deprecated
+ // TODO Remove ANEURALNETWORKS_REDUCE_MIN_EX
+ _map[ANEURALNETWORKS_REDUCE_MIN_EX] = _map[ANEURALNETWORKS_REDUCE_MIN];
+
+ _map[ANEURALNETWORKS_SPLIT] = [](const OperationFactory::Param &init_param, Operands &operands) {
+ assert(init_param.input_count == 3);
+ assert(init_param.output_count >= 1); // At least one output tensor and axis
+
+ OperandIndexSequence inputs{init_param.inputs[1], init_param.inputs[0]};
+ OperandIndexSequence outputs;
+ for (uint32_t n = 0; n < init_param.output_count; ++n)
+ {
+ outputs.append(OperandIndex{init_param.outputs[n]});
+ }
+
+ operation::Split::Param param;
+ param.num_splits = operands.at(OperandIndex{init_param.inputs[2]}).asScalar<std::int32_t>();
+
+ return new operation::Split{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_SPLIT_V_EX] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 4);
+ assert(init_param.output_count >= 1); // At least one output tensor and axis
+
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
+ OperandIndexSequence outputs;
+ for (uint32_t n = 0; n < init_param.output_count; ++n)
+ {
+ outputs.append(OperandIndex{init_param.outputs[n]});
+ }
+
+ operation::SplitV::Param param;
+ param.num_splits = operands.at(OperandIndex{init_param.inputs[3]}).asScalar<std::int32_t>();
+ return new operation::SplitV{inputs, outputs, param};
+ };
+
+ // ANEURALNETWORKS_SPLIT_EX is deprecated
+ // TODO Remove ANEURALNETWORKS_SPLIT_EX
+ _map[ANEURALNETWORKS_SPLIT_EX] = _map[ANEURALNETWORKS_SPLIT];
+
+ _map[ANEURALNETWORKS_UNPACK_EX] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 3 && init_param.output_count >= 1);
+
+ OperandIndexSequence inputs{init_param.inputs[0]};
+ OperandIndexSequence outputs;
+ for (uint32_t n = 0; n < init_param.output_count; ++n)
+ {
+ outputs.append(OperandIndex{init_param.outputs[n]});
+ }
+
+ operation::Unpack::Param param;
+ const auto num_index = OperandIndex{init_param.inputs[1]};
+ const auto axis_index = OperandIndex{init_param.inputs[2]};
+ param.num = operands.at(num_index).asScalar<int32_t>();
+ param.axis = operands.at(axis_index).asScalar<int32_t>();
+
+ return new operation::Unpack{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_PAD] = [](const OperationFactory::Param &init_param, Operands &) {
+ assert(init_param.input_count >= 2 && init_param.input_count <= 3 &&
+ init_param.output_count >= 1);
+
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
+ if (init_param.input_count == 3)
+ {
+ inputs.append(OperandIndex{init_param.inputs[2]});
+ }
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ return new operation::Pad{inputs, outputs};
+ };
+
+ _map[ANEURALNETWORKS_PAD_V2] = _map[ANEURALNETWORKS_PAD];
+
+ _map[ANEURALNETWORKS_MINIMUM] =
+ getElementwiseBinaryGenerator(operation::ElementwiseBinary::ElementwiseBinaryType::MIN);
+
+ _map[ANEURALNETWORKS_MAXIMUM] =
+ getElementwiseBinaryGenerator(operation::ElementwiseBinary::ElementwiseBinaryType::MAX);
+
+ _map[ANEURALNETWORKS_ONE_HOT_EX] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 5);
+ assert(init_param.output_count == 1);
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> indices tensor
+ // 1 -> depth tensor
+ // 2 -> on_value tensor
+ // 3 -> off_value tensor
+ // 4 -> axis scalar
+ OperandIndexSequence inputs;
+ for (uint32_t n = 0; n < init_param.input_count - 1; ++n)
+ {
+ inputs.append(OperandIndex{init_param.inputs[n]});
+ }
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ operation::OneHot::Param param;
+ param.axis = operands.at(OperandIndex{init_param.inputs[4]}).asScalar<std::int32_t>();
+
+ return new operation::OneHot{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_COS_EX] =
+ getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::COS);
+
+ _map[ANEURALNETWORKS_SIN] = getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::SIN);
+
+ _map[ANEURALNETWORKS_SHAPE_EX] = [](const OperationFactory::Param &init_param, Operands &) {
+ assert(init_param.input_count == 1 && init_param.output_count == 1);
+
+ OperandIndexSequence inputs{init_param.inputs[0]};
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ return new operation::Shape{inputs, outputs};
+ };
+
+ _map[ANEURALNETWORKS_REDUCE_PROD] =
+ getReduceGenerator(onert::ir::operation::Reduce::ReduceType::PROD);
+
+ _map[ANEURALNETWORKS_ROUND_EX] =
+ getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::ROUND);
+
+ _map[ANEURALNETWORKS_RANGE_EX] = [](const OperationFactory::Param &init_param, Operands &) {
+ assert(init_param.input_count == 3 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ // 0 -> start Tensor Index
+ // 1 -> limit Tensor Index
+ // 2 -> delta Tensor Index
+
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
+
+ return new operation::Range{inputs, outputs};
+ };
+
+ // Each input should be interpreted as follows:
+ // 0 -> LHS Tensor Index
+ // 1 -> RHS Tensor Index
+ _map[ANEURALNETWORKS_POW] = createSimpleBinaryOp<operation::Pow>;
+
+ // Each input should be interpreted as follows:
+ // 0 -> A tensor, specifying the input.
+ // 1 -> A 1-D tensor, specifying the value
+ _map[ANEURALNETWORKS_FILL_EX] = createSimpleBinaryOp<operation::Fill>;
+
+ _map[ANEURALNETWORKS_ZEROS_LIKE_EX] =
+ getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::ZEROS_LIKE);
+ // Each input should be interpreted as follows:
+ // 0 -> Input Tensor Index
+ // 1 -> Multiple Tensor Index
+ _map[ANEURALNETWORKS_TILE] = createSimpleBinaryOp<operation::Tile>;
+
+ _map[ANEURALNETWORKS_MATRIX_BAND_PART_EX] = [](const OperationFactory::Param &init_param,
+ Operands &) {
+ assert(init_param.input_count == 3);
+ assert(init_param.output_count == 1);
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> A tensor, input
+ // 1 -> A 0-D tensor, number of lower diagnonals to keep
+ // 2 -> A 0-D tensor, number of upper diagnonals to keep
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1], init_param.inputs[2]};
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ return new operation::MatrixBandPart{inputs, outputs};
+ };
+
+ _map[ANEURALNETWORKS_BATCH_MATMUL_EX] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 4 && init_param.output_count == 1);
+
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Lhs Tensor Index
+ // 1 -> Rhs Tensor Index
+ // 2 -> adj_x boolean scalar Index
+ // 3 -> adj_y boolean scalar Index
+
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
+
+ operation::BatchMatMul::Param param;
+ param.adj_x = operands.at(OperandIndex{init_param.inputs[2]}).asScalar<bool>();
+ param.adj_y = operands.at(OperandIndex{init_param.inputs[3]}).asScalar<bool>();
+
+ return new operation::BatchMatMul{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_EINSUM_EX] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ // Each input should be interpreted as follows:
+ //
+ // 0....n - 1 -> n Input Tensors Index
+ // n -> equation
+ assert(init_param.input_count >= 1 && init_param.output_count == 1);
+
+ OperandIndexSequence inputs;
+ for (uint32_t n = 0; n < init_param.input_count - 1; ++n)
+ {
+ inputs.append(OperandIndex{init_param.inputs[n]});
+ }
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ operation::Einsum::Param param;
+ const OperandIndex equation_index{init_param.inputs[init_param.input_count - 1]};
+ std::vector<char> equation_vector = operands.at(equation_index).asVector<char>();
+ param.equation = std::string(equation_vector.begin(), equation_vector.end());
+
+ return new operation::Einsum{inputs, outputs, param};
+ };
+
+ // 0 -> Input Tensor Index
+ // 1 -> int32, int64, An 1-D int tensor Index
+ _map[ANEURALNETWORKS_BROADCAST_TO_EX] = createSimpleBinaryOp<operation::BroadcastTo>;
+
+ _map[ANEURALNETWORKS_STATELESS_RANDOM_UNIFORM_EX] = [](const OperationFactory::Param &init_param,
+ Operands &) {
+ assert(init_param.input_count == 2 && init_param.output_count == 1);
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> Shape Tensor Index
+ // 1 -> int32, int64, An 1-D int tensor Index
+
+ OperandIndexSequence inputs{init_param.inputs[0], init_param.inputs[1]};
+
+ return new operation::StatelessRandomUniform{inputs, outputs};
+ };
+
+ _map[ANEURALNETWORKS_FUSED_BATCH_NORM_V3_EX] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ // Each input should be interpreted as follows:
+ //
+ // 0....4 -> 5 Input Tensors Index
+ // n-2 -> is_training
+ // n-1 -> data_format
+ // n -> epsilon
+
+ assert(init_param.input_count == 8 && init_param.output_count == 1);
+
+ OperandIndexSequence inputs;
+ for (uint32_t n = 0; n < init_param.input_count - 3; ++n)
+ {
+ inputs.append(OperandIndex{init_param.inputs[n]});
+ }
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ operation::FusedBatchNorm::Param param;
+ const OperandIndex is_training_index{init_param.inputs[init_param.input_count - 3]};
+ param.is_training = operands.at(is_training_index).asScalar<bool>();
+
+ const OperandIndex data_format_index{init_param.inputs[init_param.input_count - 2]};
+ std::vector<char> data_format_vector = operands.at(data_format_index).asVector<char>();
+ param.data_format = std::string(data_format_vector.begin(), data_format_vector.end());
+
+ const OperandIndex epsilon_index{init_param.inputs[init_param.input_count - 1]};
+ param.epsilon = operands.at(epsilon_index).asScalar<float>();
+ return new operation::FusedBatchNorm{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_LOG_SOFTMAX] = [](const OperationFactory::Param &init_param,
+ Operands &operands) {
+ assert(init_param.input_count == 3 && init_param.output_count == 1);
+
+ // Each input should be interpreted as follows:
+ //
+ // 0 -> A tensor specifying the input logits.
+ // 1 -> A scalar, specifying the positive scaling factor for the exponent, beta.
+ // 2 -> An scalar specifying the axis to reduce across.
+
+ OperandIndexSequence inputs{init_param.inputs[0]};
+ OperandIndexSequence outputs{init_param.outputs[0]};
+
+ const auto beta_index = OperandIndex{init_param.inputs[1]};
+ const auto axis_index = OperandIndex{init_param.inputs[2]};
+
+ operation::LogSoftmax::Param param;
+ param.beta = operands.at(beta_index).asScalar<float>();
+ param.axis = operands.at(axis_index).asScalar<int>();
+
+ return new operation::LogSoftmax{inputs, outputs, param};
+ };
+
+ _map[ANEURALNETWORKS_QUANTIZE] =
+ getElementwiseUnaryGenerator(operation::ElementwiseUnary::Type::QUANTIZE);
+}
+
+Operation *OperationFactory::create(ANeuralNetworksOperationType type,
+ const OperationFactory::Param &param, Operands &operands)
+{
+ auto it = _map.find(type);
+ if (it == _map.end())
+ {
+ throw std::runtime_error("Unsupported operation type: " + std::to_string(type));
+ }
+ return it->second(param, operands);
+}
diff --git a/runtime/onert/frontend/nnapi/wrapper/OperationFactory.h b/runtime/onert/frontend/nnapi/wrapper/OperationFactory.h
new file mode 100644
index 000000000..367cf74db
--- /dev/null
+++ b/runtime/onert/frontend/nnapi/wrapper/OperationFactory.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __OPERATION_FACTORY_H__
+#define __OPERATION_FACTORY_H__
+
+#include <unordered_map>
+
+#include "ir/Operands.h"
+#include "ir/Operation.h"
+#include "NeuralNetworks.h"
+#include "NeuralNetworksEx.h"
+
+/**
+ * @brief A class to create a onert operation object from NN API input parameters
+ */
+class OperationFactory
+{
+public:
+ struct Param
+ {
+ uint32_t input_count;
+ const uint32_t *inputs;
+ uint32_t output_count;
+ const uint32_t *outputs;
+ };
+
+public:
+ using Generator =
+ std::function<onert::ir::Operation *(const OperationFactory::Param &, onert::ir::Operands &)>;
+
+public:
+ static OperationFactory &get();
+
+private:
+ OperationFactory();
+
+public:
+ onert::ir::Operation *create(ANeuralNetworksOperationType, const OperationFactory::Param &param,
+ onert::ir::Operands &operands);
+ // TODO add "register" method for separating registration, possibly supporting custom-ops
+
+private:
+ std::unordered_map<ANeuralNetworksOperationType, Generator> _map;
+};
+
+#endif // __OPERATION_FACTORY_H__