From 01e7bf92d4be8224cb6b6b443d5f02bbc08c9329 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=EB=B0=95=EC=A2=85=ED=98=84/=EB=8F=99=EC=9E=91=EC=A0=9C?= =?utf8?q?=EC=96=B4Lab=28SR=29/Staff=20Engineer/=EC=82=BC=EC=84=B1?= =?utf8?q?=EC=A0=84=EC=9E=90?= Date: Fri, 24 Aug 2018 16:53:37 +0900 Subject: [PATCH] Import reference NN runtime (#1181) This commit imports reference android NN runtime implementation. Signed-off-by: Jonghyun Park --- contrib/ann/CMakeLists.txt | 1 + contrib/ann/runtimes/CMakeLists.txt | 1 + contrib/ann/runtimes/ref/CMakeLists.txt | 28 + contrib/ann/runtimes/ref/src/Assert.h | 34 ++ .../ann/runtimes/ref/src/CompilationBuilder.cpp | 52 ++ contrib/ann/runtimes/ref/src/CompilationBuilder.h | 44 ++ contrib/ann/runtimes/ref/src/ExecutionBuilder.cpp | 196 ++++++ contrib/ann/runtimes/ref/src/ExecutionBuilder.h | 73 +++ contrib/ann/runtimes/ref/src/Executor.cpp | 671 +++++++++++++++++++++ contrib/ann/runtimes/ref/src/Executor.h | 114 ++++ contrib/ann/runtimes/ref/src/Logging.cpp | 30 + contrib/ann/runtimes/ref/src/Logging.h | 42 ++ contrib/ann/runtimes/ref/src/Macro.h | 22 + contrib/ann/runtimes/ref/src/Memory.cpp | 105 ++++ contrib/ann/runtimes/ref/src/Memory.h | 106 ++++ contrib/ann/runtimes/ref/src/MemoryTracker.cpp | 50 ++ contrib/ann/runtimes/ref/src/MemoryTracker.h | 49 ++ contrib/ann/runtimes/ref/src/Model.h | 39 ++ contrib/ann/runtimes/ref/src/ModelArgumentInfo.cpp | 121 ++++ contrib/ann/runtimes/ref/src/ModelArgumentInfo.h | 58 ++ contrib/ann/runtimes/ref/src/ModelBuilder.cpp | 483 +++++++++++++++ contrib/ann/runtimes/ref/src/ModelBuilder.h | 142 +++++ contrib/ann/runtimes/ref/src/NeuralNetworks.cpp | 348 +++++++++++ contrib/ann/runtimes/ref/src/Operand.h | 61 ++ contrib/ann/runtimes/ref/src/OperandType.cpp | 55 ++ contrib/ann/runtimes/ref/src/OperandType.h | 43 ++ contrib/ann/runtimes/ref/src/OperandType.probe.cpp | 32 + contrib/ann/runtimes/ref/src/Operation.h | 32 + contrib/ann/runtimes/ref/src/OperationType.cpp | 67 ++ contrib/ann/runtimes/ref/src/OperationType.h | 65 ++ .../ann/runtimes/ref/src/OperationType.probe.cpp | 85 +++ contrib/ann/runtimes/ref/src/Probe.cpp | 89 +++ contrib/ann/runtimes/ref/src/Request.h | 35 ++ contrib/ann/runtimes/ref/src/Shape.cpp | 68 +++ contrib/ann/runtimes/ref/src/Shape.h | 47 ++ contrib/ann/runtimes/ref/src/Validation.cpp | 263 ++++++++ contrib/ann/runtimes/ref/src/Validation.h | 34 ++ contrib/ann/runtimes/ref/src/ops/Add.cpp | 57 ++ contrib/ann/runtimes/ref/src/ops/Add.float.cpp | 119 ++++ contrib/ann/runtimes/ref/src/ops/Add.float.h | 28 + contrib/ann/runtimes/ref/src/ops/Add.h | 25 + contrib/ann/runtimes/ref/src/ops/AvgPool2D.cpp | 30 + .../ann/runtimes/ref/src/ops/AvgPool2D.float.cpp | 121 ++++ contrib/ann/runtimes/ref/src/ops/AvgPool2D.float.h | 31 + contrib/ann/runtimes/ref/src/ops/AvgPool2D.h | 30 + contrib/ann/runtimes/ref/src/ops/Concatenation.cpp | 66 ++ .../runtimes/ref/src/ops/Concatenation.float.cpp | 82 +++ .../ann/runtimes/ref/src/ops/Concatenation.float.h | 30 + contrib/ann/runtimes/ref/src/ops/Concatenation.h | 28 + contrib/ann/runtimes/ref/src/ops/Conv2D.cpp | 57 ++ contrib/ann/runtimes/ref/src/ops/Conv2D.float.cpp | 253 ++++++++ contrib/ann/runtimes/ref/src/ops/Conv2D.float.h | 31 + contrib/ann/runtimes/ref/src/ops/Conv2D.h | 29 + .../ann/runtimes/ref/src/ops/DepthwiseConv2D.cpp | 57 ++ .../runtimes/ref/src/ops/DepthwiseConv2D.float.cpp | 303 ++++++++++ .../runtimes/ref/src/ops/DepthwiseConv2D.float.h | 32 + contrib/ann/runtimes/ref/src/ops/DepthwiseConv2D.h | 30 + .../ann/runtimes/ref/src/ops/FullyConnected.cpp | 70 +++ .../runtimes/ref/src/ops/FullyConnected.float.cpp | 63 ++ .../runtimes/ref/src/ops/FullyConnected.float.h | 29 + contrib/ann/runtimes/ref/src/ops/FullyConnected.h | 28 + contrib/ann/runtimes/ref/src/ops/MaxPool2D.cpp | 30 + .../ann/runtimes/ref/src/ops/MaxPool2D.float.cpp | 116 ++++ contrib/ann/runtimes/ref/src/ops/MaxPool2D.float.h | 31 + contrib/ann/runtimes/ref/src/ops/MaxPool2D.h | 30 + contrib/ann/runtimes/ref/src/ops/Reshape.cpp | 73 +++ contrib/ann/runtimes/ref/src/ops/Reshape.h | 31 + contrib/ann/runtimes/ref/src/ops/Softmax.cpp | 25 + contrib/ann/runtimes/ref/src/ops/Softmax.float.cpp | 69 +++ contrib/ann/runtimes/ref/src/ops/Softmax.float.h | 28 + contrib/ann/runtimes/ref/src/ops/Softmax.h | 27 + .../ref/src/ops/internal/ActivationUtils.h | 59 ++ contrib/ann/runtimes/ref/src/ops/internal/Array.h | 42 ++ contrib/ann/runtimes/ref/src/ops/internal/Dims.h | 155 +++++ .../runtimes/ref/src/ops/internal/Elementwise.cpp | 25 + .../runtimes/ref/src/ops/internal/Elementwise.h | 25 + .../ann/runtimes/ref/src/ops/internal/FeatureMap.h | 26 + .../ann/runtimes/ref/src/ops/internal/Fused.cpp | 28 + contrib/ann/runtimes/ref/src/ops/internal/Fused.h | 84 +++ contrib/ann/runtimes/ref/src/ops/internal/GEMM.h | 38 ++ contrib/ann/runtimes/ref/src/ops/internal/Macro.h | 58 ++ contrib/ann/runtimes/ref/src/ops/internal/Matrix.h | 116 ++++ .../ann/runtimes/ref/src/ops/internal/NDArray.h | 126 ++++ .../ann/runtimes/ref/src/ops/internal/Pooling.cpp | 43 ++ .../ann/runtimes/ref/src/ops/internal/Pooling.h | 31 + .../ann/runtimes/ref/src/ops/internal/Spatial.h | 29 + 86 files changed, 6759 insertions(+) create mode 100644 contrib/ann/runtimes/CMakeLists.txt create mode 100644 contrib/ann/runtimes/ref/CMakeLists.txt create mode 100644 contrib/ann/runtimes/ref/src/Assert.h create mode 100644 contrib/ann/runtimes/ref/src/CompilationBuilder.cpp create mode 100644 contrib/ann/runtimes/ref/src/CompilationBuilder.h create mode 100644 contrib/ann/runtimes/ref/src/ExecutionBuilder.cpp create mode 100644 contrib/ann/runtimes/ref/src/ExecutionBuilder.h create mode 100644 contrib/ann/runtimes/ref/src/Executor.cpp create mode 100644 contrib/ann/runtimes/ref/src/Executor.h create mode 100644 contrib/ann/runtimes/ref/src/Logging.cpp create mode 100644 contrib/ann/runtimes/ref/src/Logging.h create mode 100644 contrib/ann/runtimes/ref/src/Macro.h create mode 100644 contrib/ann/runtimes/ref/src/Memory.cpp create mode 100644 contrib/ann/runtimes/ref/src/Memory.h create mode 100644 contrib/ann/runtimes/ref/src/MemoryTracker.cpp create mode 100644 contrib/ann/runtimes/ref/src/MemoryTracker.h create mode 100644 contrib/ann/runtimes/ref/src/Model.h create mode 100644 contrib/ann/runtimes/ref/src/ModelArgumentInfo.cpp create mode 100644 contrib/ann/runtimes/ref/src/ModelArgumentInfo.h create mode 100644 contrib/ann/runtimes/ref/src/ModelBuilder.cpp create mode 100644 contrib/ann/runtimes/ref/src/ModelBuilder.h create mode 100644 contrib/ann/runtimes/ref/src/NeuralNetworks.cpp create mode 100644 contrib/ann/runtimes/ref/src/Operand.h create mode 100644 contrib/ann/runtimes/ref/src/OperandType.cpp create mode 100644 contrib/ann/runtimes/ref/src/OperandType.h create mode 100644 contrib/ann/runtimes/ref/src/OperandType.probe.cpp create mode 100644 contrib/ann/runtimes/ref/src/Operation.h create mode 100644 contrib/ann/runtimes/ref/src/OperationType.cpp create mode 100644 contrib/ann/runtimes/ref/src/OperationType.h create mode 100644 contrib/ann/runtimes/ref/src/OperationType.probe.cpp create mode 100644 contrib/ann/runtimes/ref/src/Probe.cpp create mode 100644 contrib/ann/runtimes/ref/src/Request.h create mode 100644 contrib/ann/runtimes/ref/src/Shape.cpp create mode 100644 contrib/ann/runtimes/ref/src/Shape.h create mode 100644 contrib/ann/runtimes/ref/src/Validation.cpp create mode 100644 contrib/ann/runtimes/ref/src/Validation.h create mode 100644 contrib/ann/runtimes/ref/src/ops/Add.cpp create mode 100644 contrib/ann/runtimes/ref/src/ops/Add.float.cpp create mode 100644 contrib/ann/runtimes/ref/src/ops/Add.float.h create mode 100644 contrib/ann/runtimes/ref/src/ops/Add.h create mode 100644 contrib/ann/runtimes/ref/src/ops/AvgPool2D.cpp create mode 100644 contrib/ann/runtimes/ref/src/ops/AvgPool2D.float.cpp create mode 100644 contrib/ann/runtimes/ref/src/ops/AvgPool2D.float.h create mode 100644 contrib/ann/runtimes/ref/src/ops/AvgPool2D.h create mode 100644 contrib/ann/runtimes/ref/src/ops/Concatenation.cpp create mode 100644 contrib/ann/runtimes/ref/src/ops/Concatenation.float.cpp create mode 100644 contrib/ann/runtimes/ref/src/ops/Concatenation.float.h create mode 100644 contrib/ann/runtimes/ref/src/ops/Concatenation.h create mode 100644 contrib/ann/runtimes/ref/src/ops/Conv2D.cpp create mode 100644 contrib/ann/runtimes/ref/src/ops/Conv2D.float.cpp create mode 100644 contrib/ann/runtimes/ref/src/ops/Conv2D.float.h create mode 100644 contrib/ann/runtimes/ref/src/ops/Conv2D.h create mode 100644 contrib/ann/runtimes/ref/src/ops/DepthwiseConv2D.cpp create mode 100644 contrib/ann/runtimes/ref/src/ops/DepthwiseConv2D.float.cpp create mode 100644 contrib/ann/runtimes/ref/src/ops/DepthwiseConv2D.float.h create mode 100644 contrib/ann/runtimes/ref/src/ops/DepthwiseConv2D.h create mode 100644 contrib/ann/runtimes/ref/src/ops/FullyConnected.cpp create mode 100644 contrib/ann/runtimes/ref/src/ops/FullyConnected.float.cpp create mode 100644 contrib/ann/runtimes/ref/src/ops/FullyConnected.float.h create mode 100644 contrib/ann/runtimes/ref/src/ops/FullyConnected.h create mode 100644 contrib/ann/runtimes/ref/src/ops/MaxPool2D.cpp create mode 100644 contrib/ann/runtimes/ref/src/ops/MaxPool2D.float.cpp create mode 100644 contrib/ann/runtimes/ref/src/ops/MaxPool2D.float.h create mode 100644 contrib/ann/runtimes/ref/src/ops/MaxPool2D.h create mode 100644 contrib/ann/runtimes/ref/src/ops/Reshape.cpp create mode 100644 contrib/ann/runtimes/ref/src/ops/Reshape.h create mode 100644 contrib/ann/runtimes/ref/src/ops/Softmax.cpp create mode 100644 contrib/ann/runtimes/ref/src/ops/Softmax.float.cpp create mode 100644 contrib/ann/runtimes/ref/src/ops/Softmax.float.h create mode 100644 contrib/ann/runtimes/ref/src/ops/Softmax.h create mode 100644 contrib/ann/runtimes/ref/src/ops/internal/ActivationUtils.h create mode 100644 contrib/ann/runtimes/ref/src/ops/internal/Array.h create mode 100644 contrib/ann/runtimes/ref/src/ops/internal/Dims.h create mode 100644 contrib/ann/runtimes/ref/src/ops/internal/Elementwise.cpp create mode 100644 contrib/ann/runtimes/ref/src/ops/internal/Elementwise.h create mode 100644 contrib/ann/runtimes/ref/src/ops/internal/FeatureMap.h create mode 100644 contrib/ann/runtimes/ref/src/ops/internal/Fused.cpp create mode 100644 contrib/ann/runtimes/ref/src/ops/internal/Fused.h create mode 100644 contrib/ann/runtimes/ref/src/ops/internal/GEMM.h create mode 100644 contrib/ann/runtimes/ref/src/ops/internal/Macro.h create mode 100644 contrib/ann/runtimes/ref/src/ops/internal/Matrix.h create mode 100644 contrib/ann/runtimes/ref/src/ops/internal/NDArray.h create mode 100644 contrib/ann/runtimes/ref/src/ops/internal/Pooling.cpp create mode 100644 contrib/ann/runtimes/ref/src/ops/internal/Pooling.h create mode 100644 contrib/ann/runtimes/ref/src/ops/internal/Spatial.h diff --git a/contrib/ann/CMakeLists.txt b/contrib/ann/CMakeLists.txt index 53d257b..6bae718 100644 --- a/contrib/ann/CMakeLists.txt +++ b/contrib/ann/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(api) +add_subdirectory(runtimes) diff --git a/contrib/ann/runtimes/CMakeLists.txt b/contrib/ann/runtimes/CMakeLists.txt new file mode 100644 index 0000000..5ea6cda --- /dev/null +++ b/contrib/ann/runtimes/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectories() diff --git a/contrib/ann/runtimes/ref/CMakeLists.txt b/contrib/ann/runtimes/ref/CMakeLists.txt new file mode 100644 index 0000000..5558f12 --- /dev/null +++ b/contrib/ann/runtimes/ref/CMakeLists.txt @@ -0,0 +1,28 @@ +nncc_find_package(Eigen QUIET) + +if(NOT Eigen_FOUND) + return() +endif(NOT Eigen_FOUND) + +nncc_find_package(GEMMLowp QUIET) + +if(NOT GEMMLowp_FOUND) + return() +endif(NOT GEMMLowp_FOUND) + +file(GLOB_RECURSE SOURCES "src/*.cpp") + +function(ann_ref_configure TARGET) + target_include_directories(${TARGET} PRIVATE src) + target_link_libraries(${TARGET} PRIVATE ann_api) + target_link_libraries(${TARGET} PRIVATE eigen) + target_link_libraries(${TARGET} PRIVATE gemmlowp) +endfunction(ann_ref_configure) + +add_library(ann_ref_static STATIC ${SOURCES}) +set_target_properties(ann_ref_static PROPERTIES POSITION_INDEPENDENT_CODE ON) +ann_ref_configure(ann_ref_static) + +add_library(ann_ref_shared SHARED ${SOURCES}) +set_target_properties(ann_ref_shared PROPERTIES OUTPUT_NAME neuralnetworks) +ann_ref_configure(ann_ref_shared) diff --git a/contrib/ann/runtimes/ref/src/Assert.h b/contrib/ann/runtimes/ref/src/Assert.h new file mode 100644 index 0000000..7443056 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/Assert.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __ASSERT_H__ +#define __ASSERT_H__ + +#include "Logging.h" + +// Assert macro, as Android does not generally support assert. +#define ASSERT(v) \ + do \ + { \ + if (!(v)) \ + { \ + LOG(ERROR) << "'" << #v << "' failed at " << __FILE__ << ":" << __LINE__ << "'\n"; \ + abort(); \ + } \ + } while (0) + +#endif // __ASSERT_H__ diff --git a/contrib/ann/runtimes/ref/src/CompilationBuilder.cpp b/contrib/ann/runtimes/ref/src/CompilationBuilder.cpp new file mode 100644 index 0000000..a14dbc1 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/CompilationBuilder.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "ExecutionBuilder.h" +#include "CompilationBuilder.h" + +#include "Logging.h" + +CompilationBuilder::CompilationBuilder(const ModelBuilder *model) : mModel(model) +{ + VLOG(COMPILATION) << "CompilationBuilder::CompilationBuilder"; +} + +int CompilationBuilder::finish() +{ + if (mFinished) + { + LOG(ERROR) << "ANeuralNetworksCompilation_finish called more than once"; + return ANEURALNETWORKS_BAD_STATE; + } + // TODO validate the rest + + mFinished = true; + + return ANEURALNETWORKS_NO_ERROR; +} + +int CompilationBuilder::createExecution(ExecutionBuilder **execution) +{ + if (!mFinished) + { + LOG(ERROR) << "ANeuralNetworksExecution_create passed an unfinished compilation"; + *execution = nullptr; + return ANEURALNETWORKS_BAD_STATE; + } + *execution = new ExecutionBuilder(mModel); + return (*execution ? ANEURALNETWORKS_NO_ERROR : ANEURALNETWORKS_OUT_OF_MEMORY); +} diff --git a/contrib/ann/runtimes/ref/src/CompilationBuilder.h b/contrib/ann/runtimes/ref/src/CompilationBuilder.h new file mode 100644 index 0000000..92c1ab4 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/CompilationBuilder.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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_BUILDER_H__ +#define __COMPILATION_BUILDER_H__ + +#include "NeuralNetworks.h" + +class ModelBuilder; +class ExecutionBuilder; + +class CompilationBuilder +{ +public: + CompilationBuilder(const ModelBuilder *model); + +public: + int finish(); + + int createExecution(ExecutionBuilder **execution); + +private: + const ModelBuilder *mModel; + + // Once the compilation has been finished, we should not allow further + // modifications to the compilation. + bool mFinished = false; +}; + +#endif // __COMPILATION_BUILDER_H__ diff --git a/contrib/ann/runtimes/ref/src/ExecutionBuilder.cpp b/contrib/ann/runtimes/ref/src/ExecutionBuilder.cpp new file mode 100644 index 0000000..9df78bf --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ExecutionBuilder.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "ExecutionBuilder.h" +#include "CompilationBuilder.h" +#include "ModelBuilder.h" + +#include "Executor.h" + +#include "Logging.h" +#include "Validation.h" + +static void setRequestArgumentArray(const std::vector &argumentInfos, + std::vector *ioInfos) +{ + size_t count = argumentInfos.size(); + ioInfos->resize(count); + for (size_t i = 0; i < count; i++) + { + const auto &info = argumentInfos[i]; + (*ioInfos)[i] = { + .hasNoValue = info.state == ModelArgumentInfo::HAS_NO_VALUE, + .location = info.locationAndLength, + .dimensions = info.dimensions, + }; + } +} + +bool setRunTimePoolInfosFromMemories(std::vector *poolInfos, + const std::vector &pools) +{ + poolInfos->resize(pools.size()); + for (size_t i = 0; i < pools.size(); i++) + { + auto &poolInfo = (*poolInfos)[i]; + if (!poolInfo.set(pools[i])) + { + LOG(ERROR) << "Could not map pool"; + return false; + } + } + return true; +} + +ExecutionBuilder::ExecutionBuilder(const ModelBuilder *model) + : mModel(model), mInputs(mModel->inputCount()), mOutputs(mModel->outputCount()) +{ + VLOG(EXECUTION) << "ExecutionBuilder::ExecutionBuilder"; +} + +int ExecutionBuilder::setInput(uint32_t index, const ANeuralNetworksOperandType *type, + const void *buffer, size_t length) +{ + uint32_t count = static_cast(mInputs.size()); + if (index >= count) + { + LOG(ERROR) << "ANeuralNetworksExecution_setInput bad index " << index << " " << count; + return ANEURALNETWORKS_BAD_DATA; + } + if (type != nullptr) + { + int n = validateOperandType(*type, "ANeuralNetworksExecution_setInput", false); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + } + if (length > 0xFFFFFFFF) + { + LOG(ERROR) << "ANeuralNetworksExecution_setInput input exceeds max length " << length; + return ANEURALNETWORKS_BAD_DATA; + } + uint32_t l = static_cast(length); + return mInputs[index].setFromPointer(mModel->getInputOperand(index), type, + const_cast(buffer), l); +} + +int ExecutionBuilder::setInputFromMemory(uint32_t index, const ANeuralNetworksOperandType *type, + const Memory *memory, size_t offset, size_t length) +{ + uint32_t count = static_cast(mInputs.size()); + if (index >= count) + { + LOG(ERROR) << "ANeuralNetworksExecution_setInputFromMemory bad index " << index << " " << count; + return ANEURALNETWORKS_BAD_DATA; + } + if (!memory->validateSize(offset, length)) + { + return ANEURALNETWORKS_BAD_DATA; + } + uint32_t poolIndex = mMemories.add(memory); + return mInputs[index].setFromMemory(mModel->getInputOperand(index), type, poolIndex, offset, + length); +} + +int ExecutionBuilder::setOutput(uint32_t index, const ANeuralNetworksOperandType *type, + void *buffer, size_t length) +{ + uint32_t count = static_cast(mOutputs.size()); + if (index >= count) + { + LOG(ERROR) << "ANeuralNetworksExecution_setOutput bad index " << index << " " << count; + return ANEURALNETWORKS_BAD_DATA; + } + if (type != nullptr) + { + int n = validateOperandType(*type, "ANeuralNetworksExecution_setOutput", false); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + } + if (length > 0xFFFFFFFF) + { + LOG(ERROR) << "ANeuralNetworksExecution_setOutput input exceeds max length " << length; + return ANEURALNETWORKS_BAD_DATA; + } + uint32_t l = static_cast(length); + return mOutputs[index].setFromPointer(mModel->getOutputOperand(index), type, buffer, l); +} + +int ExecutionBuilder::setOutputFromMemory(uint32_t index, const ANeuralNetworksOperandType *type, + const Memory *memory, size_t offset, size_t length) +{ + // Should be similar to StepExecutor::setInputOrOutputFromTemporaryMemory() + + uint32_t count = static_cast(mOutputs.size()); + if (index >= count) + { + LOG(ERROR) << "ANeuralNetworksExecution_setOutputFromMemory bad index " << index << " " + << count; + return ANEURALNETWORKS_BAD_DATA; + } + if (!memory->validateSize(offset, length)) + { + return ANEURALNETWORKS_BAD_DATA; + } + // TODO validate the rest + uint32_t poolIndex = mMemories.add(memory); + return mOutputs[index].setFromMemory(mModel->getOutputOperand(index), type, poolIndex, offset, + length); +} + +int ExecutionBuilder::startCompute(void) +{ + Model model; + mModel->publish(&model); + + // modelPoolInfo holds the infomation of pre-allocated memory pools during model construction + std::vector modelPoolInfos; + if (!setRunTimePoolInfosFromMemories(&modelPoolInfos, model.pools)) + { + return ANEURALNETWORKS_UNMAPPABLE; + } + + std::vector requestPoolInfos; + uint32_t count = mMemories.size(); + requestPoolInfos.resize(count); + // Create as many pools as there are input / output + auto fixPointerArguments = [&requestPoolInfos](std::vector &argumentInfos) { + for (ModelArgumentInfo &argumentInfo : argumentInfos) + { + if (argumentInfo.state == ModelArgumentInfo::POINTER) + { + RunTimePoolInfo runTimeInfo; + runTimeInfo.buffer = static_cast(argumentInfo.buffer); + argumentInfo.locationAndLength.poolIndex = static_cast(requestPoolInfos.size()); + argumentInfo.locationAndLength.offset = 0; + requestPoolInfos.push_back(runTimeInfo); + } + } + }; + fixPointerArguments(mInputs); + fixPointerArguments(mOutputs); + + Request request; + setRequestArgumentArray(mInputs, &request.inputs); + setRequestArgumentArray(mOutputs, &request.outputs); + + Executor executor; + return executor.run(model, request, modelPoolInfos, requestPoolInfos); +} diff --git a/contrib/ann/runtimes/ref/src/ExecutionBuilder.h b/contrib/ann/runtimes/ref/src/ExecutionBuilder.h new file mode 100644 index 0000000..0bf5ef7 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ExecutionBuilder.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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_BUILDER_H__ +#define __EXECUTION_BUILDER_H__ + +#include "NeuralNetworks.h" + +#include "ModelBuilder.h" +#include "ModelArgumentInfo.h" + +#include "Memory.h" + +#include + +class ModelBuilder; + +class ExecutionBuilder +{ +public: + ExecutionBuilder(const ModelBuilder *); + +public: + int setInput(uint32_t index, const ANeuralNetworksOperandType *type, const void *buffer, + size_t length); + int setInputFromMemory(uint32_t index, const ANeuralNetworksOperandType *type, + const Memory *memory, size_t offset, size_t length); + +public: + int setOutput(uint32_t index, const ANeuralNetworksOperandType *type, void *buffer, + size_t length); + int setOutputFromMemory(uint32_t index, const ANeuralNetworksOperandType *type, + const Memory *memory, size_t offset, size_t length); + +public: + int startCompute(void); + +private: + const ModelBuilder *mModel; + +private: + // The information we'll send to the driver about the inputs and outputs. + // Note that we build this in two steps: + // 1. As the arguments are specified, set the corresponding mInputs or mOutputs element. + // If set from a pointer, don't set the location in the RequestArgument but store it + // instead in mInputBuffers or mOutputBuffers. + // 2. Once we have all the inputs and outputs, if needed, allocate shared memory for + // the m*Buffers entries. Copy the input values into the shared memory. + // We do this to avoid creating a lot of shared memory objects if we have a lot of + // parameters specified via pointers. We also avoid copying in the case where + // some of the nodes will interpreted on the CPU anyway. + std::vector mInputs; + std::vector mOutputs; + +private: + MemoryTracker mMemories; +}; + +#endif // __EXECUTION_BUILDER_H__ diff --git a/contrib/ann/runtimes/ref/src/Executor.cpp b/contrib/ann/runtimes/ref/src/Executor.cpp new file mode 100644 index 0000000..fde6979 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/Executor.cpp @@ -0,0 +1,671 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "Executor.h" + +#include "NeuralNetworks.h" +#include "Shape.h" + +#include "ops/Add.h" +#include "ops/Add.float.h" +#include "ops/Conv2D.h" +#include "ops/Conv2D.float.h" +#include "ops/DepthwiseConv2D.h" +#include "ops/DepthwiseConv2D.float.h" +#include "ops/AvgPool2D.h" +#include "ops/AvgPool2D.float.h" +#include "ops/MaxPool2D.h" +#include "ops/MaxPool2D.float.h" +#include "ops/Concatenation.h" +#include "ops/Concatenation.float.h" +#include "ops/Reshape.h" +#include "ops/Softmax.h" +#include "ops/Softmax.float.h" +#include "ops/FullyConnected.h" +#include "ops/FullyConnected.float.h" + +#include "Logging.h" +#include "Assert.h" + +enum PaddingScheme +{ + kPaddingUnknown = 0, + kPaddingSame = 1, + kPaddingValid = 2, +}; + +inline void calculateExplicitPadding(int32_t in_size, int32_t stride, int32_t filter_size, + int32_t padding_implicit, int32_t *padding_head, + int32_t *padding_tail) +{ + *padding_head = 0; + *padding_tail = 0; + + if (padding_implicit == kPaddingSame) + { + int32_t out_size = (in_size + stride - 1) / stride; + int32_t tmp = (out_size - 1) * stride + filter_size; + if (tmp > in_size) + { + *padding_head = (tmp - in_size) / 2; + *padding_tail = (tmp - in_size) - *padding_head; + } + } +} + +template static inline T getScalarData(const RunTimeOperandInfo &info) +{ + // TODO: Check buffer is at least as long as size of data. + T *data = reinterpret_cast(info.buffer); + return data[0]; +} + +// Updates the RunTimeOperandInfo with the newly calculated shape. +// Allocate the buffer if we need to. +static bool setInfoAndAllocateIfNeeded(RunTimeOperandInfo *info, const Shape &shape) +{ + // For user-provided model output operands, the parameters must match the Shape + // calculated from the preparation step. + if (info->lifetime == OperandLifeTime::MODEL_OUTPUT) + { + if (info->type != shape.type || info->dimensions != shape.dimensions) + { + LOG(ERROR) << "Invalid type or dimensions for model output"; + return false; + } + if (info->type == OperandType::TENSOR_QUANT8_ASYMM && + (info->scale != shape.scale || info->zeroPoint != shape.offset)) + { + LOG(ERROR) << "Invalid scale or zeroPoint for model output"; + return false; + } + } + info->type = shape.type; + info->dimensions = shape.dimensions; + info->scale = shape.scale; + info->zeroPoint = shape.offset; + if (info->lifetime == OperandLifeTime::TEMPORARY_VARIABLE && info->buffer == nullptr) + { + uint32_t length = sizeOfData(info->type, info->dimensions); + info->buffer = new uint8_t[length]; + if (info->buffer == nullptr) + { + return false; + } + } + return true; +} + +// Ignore the .pools entry in model and request. This will have been taken care of +// by the caller. +int Executor::run(const Model &model, const Request &request, + const std::vector &modelPoolInfos, + const std::vector &requestPoolInfos) +{ + VLOG(CPUEXE) << "Executor::run()"; + + mModel = &model; + mRequest = &request; // TODO check if mRequest is needed + initializeRunTimeInfo(modelPoolInfos, requestPoolInfos); + // The model has serialized the operation in execution order. + for (const auto &operation : model.operations) + { + int n = executeOperation(operation); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + } + mModel = nullptr; + mRequest = nullptr; + VLOG(CPUEXE) << "Completed run normally"; + return ANEURALNETWORKS_NO_ERROR; +} + +bool Executor::initializeRunTimeInfo(const std::vector &modelPoolInfos, + const std::vector &requestPoolInfos) +{ + VLOG(CPUEXE) << "Executor::initializeRunTimeInfo"; + const size_t count = mModel->operands.size(); + mOperands.resize(count); + + // Start by setting the runtime info to what's in the model. + for (size_t i = 0; i < count; i++) + { + const Operand &from = mModel->operands[i]; + RunTimeOperandInfo &to = mOperands[i]; + to.type = from.type; + to.dimensions = from.dimensions; + to.scale = from.scale; + to.zeroPoint = from.zeroPoint; + to.length = from.location.length; + to.lifetime = from.lifetime; + switch (from.lifetime) + { + case OperandLifeTime::TEMPORARY_VARIABLE: + to.buffer = nullptr; + to.numberOfUsesLeft = from.numberOfConsumers; + break; + case OperandLifeTime::CONSTANT_COPY: + to.buffer = const_cast(&mModel->operandValues[from.location.offset]); + to.numberOfUsesLeft = 0; + break; + case OperandLifeTime::CONSTANT_REFERENCE: + { + auto poolIndex = from.location.poolIndex; + ASSERT(poolIndex < modelPoolInfos.size()); + auto &r = modelPoolInfos[poolIndex]; + to.buffer = r.buffer + from.location.offset; + to.numberOfUsesLeft = 0; + break; + } + case OperandLifeTime::MODEL_INPUT: + case OperandLifeTime::MODEL_OUTPUT: + case OperandLifeTime::NO_VALUE: + to.buffer = nullptr; + to.numberOfUsesLeft = 0; + break; + default: + ASSERT(false); + break; + } + } + + // Adjust the runtime info for the arguments passed to the model, + // modifying the buffer location, and possibly the dimensions. + auto updateForArguments = [this, &requestPoolInfos](const std::vector &indexes, + const std::vector &arguments) { + ASSERT(indexes.size() == arguments.size()); + for (size_t i = 0; i < indexes.size(); i++) + { + const uint32_t operandIndex = indexes[i]; + const RequestArgument &from = arguments[i]; + RunTimeOperandInfo &to = mOperands[operandIndex]; + if (from.dimensions.size() > 0) + { + // It's the responsibility of the caller to validate that + // from.dimensions only modifies the dimensions that were + // unspecified in the model. That's the case in SampleDriver.cpp + // with the call to validateRequest(). + // TODO make sure that's the case for the default CPU path. + to.dimensions = from.dimensions; + } + if (from.hasNoValue) + { + to.lifetime = OperandLifeTime::NO_VALUE; + ASSERT(to.buffer == nullptr); + } + else + { + auto poolIndex = from.location.poolIndex; + ASSERT(poolIndex < requestPoolInfos.size()); + auto &r = requestPoolInfos[poolIndex]; + to.buffer = r.buffer + from.location.offset; + } + } + }; + updateForArguments(mModel->inputIndexes, mRequest->inputs); + updateForArguments(mModel->outputIndexes, mRequest->outputs); + + return true; +} + +void Executor::freeNoLongerUsedOperands(const std::vector &inputs) +{ + for (uint32_t i : inputs) + { + auto &info = mOperands[i]; + // Check if it's a static or model input/output. + if (info.numberOfUsesLeft == 0) + { + continue; + } + info.numberOfUsesLeft--; + if (info.numberOfUsesLeft == 0) + { + ASSERT(info.buffer != nullptr); + delete[] info.buffer; + info.buffer = nullptr; + } + } +} + +int Executor::executeOperation(const Operation &operation) +{ + const std::vector &ins = operation.inputs; + const std::vector &outs = operation.outputs; + bool success = false; + + // Function to verify that the number of input and output parameters + // matches what is expected. Also checks that all the parameters have + // values. This function is to be used only for operations that do not + // accept optional arguments. + // TODO Have a version that works for optional arguments. + auto allParametersPresent = [&operation, &ins, &outs, this](size_t requiredIns, + size_t requiredOuts) -> bool { + auto verify = [&operation, this](size_t requiredCount, const std::vector &indexes, + const char *type) -> bool { + size_t actualCount = indexes.size(); + if (actualCount != requiredCount) + { + LOG(ERROR) << getOperationName(operation.type) << ": Invalid number of " << type + << " operands. Got " << actualCount << " of " << requiredCount; + return false; + } + for (size_t i = 0; i < actualCount; i++) + { + if (mOperands[indexes[i]].lifetime == OperandLifeTime::NO_VALUE) + { + LOG(ERROR) << getOperationName(operation.type) << " " << type << " operand " << i + << " is required but missing."; + return false; + } + } + return true; + }; + return verify(requiredIns, ins, "in") && verify(requiredOuts, outs, "out"); + }; + + switch (operation.type) + { + case OperationType::ADD: + { + if (!allParametersPresent(3, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + const RunTimeOperandInfo &in1 = mOperands[ins[0]]; + const RunTimeOperandInfo &in2 = mOperands[ins[1]]; + int32_t activation = getScalarData(mOperands[ins[2]]); + + RunTimeOperandInfo &out = mOperands[outs[0]]; + Shape outShape = out.shape(); + + ASSERT(in1.type == OperandType::TENSOR_FLOAT32); + { + success = addPrepare(in1.shape(), in2.shape(), &outShape) && + setInfoAndAllocateIfNeeded(&out, outShape) && + addFloat32(reinterpret_cast(in1.buffer), in1.shape(), + reinterpret_cast(in2.buffer), in2.shape(), activation, + reinterpret_cast(out.buffer), outShape); + } + } + case OperationType::DEPTHWISE_CONV_2D: + { + const size_t inCount = ins.size(); + if ((inCount != 11 && inCount != 8) || !allParametersPresent(inCount, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + const RunTimeOperandInfo &input = mOperands[ins[0]]; + const RunTimeOperandInfo &filter = mOperands[ins[1]]; + const RunTimeOperandInfo &bias = mOperands[ins[2]]; + + int32_t padding_left, padding_right; + int32_t padding_top, padding_bottom; + int32_t stride_width, stride_height; + int32_t depth_multiplier; + int32_t activation; + + if (inCount == 11) + { + padding_left = getScalarData(mOperands[ins[3]]); + padding_right = getScalarData(mOperands[ins[4]]); + padding_top = getScalarData(mOperands[ins[5]]); + padding_bottom = getScalarData(mOperands[ins[6]]); + stride_width = getScalarData(mOperands[ins[7]]); + stride_height = getScalarData(mOperands[ins[8]]); + depth_multiplier = getScalarData(mOperands[ins[9]]); + activation = getScalarData(mOperands[ins[10]]); + } + else + { + int32_t padding_implicit = getScalarData(mOperands[ins[3]]); + stride_width = getScalarData(mOperands[ins[4]]); + stride_height = getScalarData(mOperands[ins[5]]); + depth_multiplier = getScalarData(mOperands[ins[6]]); + activation = getScalarData(mOperands[ins[7]]); + + Shape inputShape = input.shape(); + Shape filterShape = filter.shape(); + int32_t input_width = getSizeOfDimension(inputShape, 2); + int32_t input_height = getSizeOfDimension(inputShape, 1); + int32_t filter_width = getSizeOfDimension(filterShape, 2); + int32_t filter_height = getSizeOfDimension(filterShape, 1); + calculateExplicitPadding(input_width, stride_width, filter_width, padding_implicit, + &padding_left, &padding_right); + calculateExplicitPadding(input_height, stride_height, filter_height, padding_implicit, + &padding_top, &padding_bottom); + } + + RunTimeOperandInfo &output = mOperands[outs[0]]; + Shape outShape = output.shape(); + + ASSERT(input.type == OperandType::TENSOR_FLOAT32); + { + success = + depthwiseConvPrepare(input.shape(), filter.shape(), bias.shape(), padding_left, + padding_right, padding_top, padding_bottom, stride_width, + stride_height, &outShape) && + setInfoAndAllocateIfNeeded(&output, outShape) && + depthwiseConvFloat32(reinterpret_cast(input.buffer), input.shape(), + reinterpret_cast(filter.buffer), filter.shape(), + reinterpret_cast(bias.buffer), bias.shape(), padding_left, + padding_right, padding_top, padding_bottom, stride_width, stride_height, + depth_multiplier, activation, reinterpret_cast(output.buffer), outShape); + } + } + break; + case OperationType::CONV_2D: + { + const size_t inCount = ins.size(); + if ((inCount != 10 && inCount != 7) || !allParametersPresent(inCount, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + const RunTimeOperandInfo &input = mOperands[ins[0]]; + const RunTimeOperandInfo &filter = mOperands[ins[1]]; + const RunTimeOperandInfo &bias = mOperands[ins[2]]; + + int32_t padding_left, padding_right; + int32_t padding_top, padding_bottom; + int32_t stride_width, stride_height; + int32_t activation; + + if (inCount == 10) + { + padding_left = getScalarData(mOperands[ins[3]]); + padding_right = getScalarData(mOperands[ins[4]]); + padding_top = getScalarData(mOperands[ins[5]]); + padding_bottom = getScalarData(mOperands[ins[6]]); + stride_width = getScalarData(mOperands[ins[7]]); + stride_height = getScalarData(mOperands[ins[8]]); + activation = getScalarData(mOperands[ins[9]]); + } + else + { + int32_t padding_implicit = getScalarData(mOperands[ins[3]]); + stride_width = getScalarData(mOperands[ins[4]]); + stride_height = getScalarData(mOperands[ins[5]]); + activation = getScalarData(mOperands[ins[6]]); + + Shape inputShape = input.shape(); + Shape filterShape = filter.shape(); + int32_t input_width = getSizeOfDimension(inputShape, 2); + int32_t input_height = getSizeOfDimension(inputShape, 1); + int32_t filter_width = getSizeOfDimension(filterShape, 2); + int32_t filter_height = getSizeOfDimension(filterShape, 1); + calculateExplicitPadding(input_width, stride_width, filter_width, padding_implicit, + &padding_left, &padding_right); + calculateExplicitPadding(input_height, stride_height, filter_height, padding_implicit, + &padding_top, &padding_bottom); + } + + RunTimeOperandInfo &output = mOperands[outs[0]]; + Shape outShape = output.shape(); + + ASSERT(input.type == OperandType::TENSOR_FLOAT32); + { + success = + convPrepare(input.shape(), filter.shape(), bias.shape(), padding_left, padding_right, + padding_top, padding_bottom, stride_width, stride_height, &outShape) && + setInfoAndAllocateIfNeeded(&output, outShape) && + convFloat32(reinterpret_cast(input.buffer), input.shape(), + reinterpret_cast(filter.buffer), filter.shape(), + reinterpret_cast(bias.buffer), bias.shape(), padding_left, + padding_right, padding_top, padding_bottom, stride_width, stride_height, + activation, reinterpret_cast(output.buffer), outShape); + } + } + break; + case OperationType::AVERAGE_POOL_2D: + { + const size_t inCount = ins.size(); + if ((inCount != 10 && inCount != 7) || !allParametersPresent(inCount, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + const RunTimeOperandInfo &input = mOperands[ins[0]]; + + int32_t padding_left, padding_right; + int32_t padding_top, padding_bottom; + int32_t stride_width, stride_height; + int32_t filter_width, filter_height; + int32_t activation; + + if (inCount == 10) + { + padding_left = getScalarData(mOperands[ins[1]]); + padding_right = getScalarData(mOperands[ins[2]]); + padding_top = getScalarData(mOperands[ins[3]]); + padding_bottom = getScalarData(mOperands[ins[4]]); + stride_width = getScalarData(mOperands[ins[5]]); + stride_height = getScalarData(mOperands[ins[6]]); + filter_width = getScalarData(mOperands[ins[7]]); + filter_height = getScalarData(mOperands[ins[8]]); + activation = getScalarData(mOperands[ins[9]]); + } + else + { + int32_t padding_implicit = getScalarData(mOperands[ins[1]]); + stride_width = getScalarData(mOperands[ins[2]]); + stride_height = getScalarData(mOperands[ins[3]]); + filter_width = getScalarData(mOperands[ins[4]]); + filter_height = getScalarData(mOperands[ins[5]]); + activation = getScalarData(mOperands[ins[6]]); + + Shape inputShape = input.shape(); + int32_t input_width = getSizeOfDimension(inputShape, 2); + int32_t input_height = getSizeOfDimension(inputShape, 1); + calculateExplicitPadding(input_width, stride_width, filter_width, padding_implicit, + &padding_left, &padding_right); + calculateExplicitPadding(input_height, stride_height, filter_height, padding_implicit, + &padding_top, &padding_bottom); + } + + RunTimeOperandInfo &output = mOperands[outs[0]]; + Shape outShape = output.shape(); + + ASSERT(input.type == OperandType::TENSOR_FLOAT32); + { + success = averagePoolPrepare(input.shape(), padding_left, padding_right, padding_top, + padding_bottom, stride_width, stride_height, filter_width, + filter_height, &outShape) && + setInfoAndAllocateIfNeeded(&output, outShape) && + averagePoolFloat32(reinterpret_cast(input.buffer), input.shape(), padding_left, + padding_right, padding_top, padding_bottom, stride_width, stride_height, + filter_width, filter_height, activation, + reinterpret_cast(output.buffer), outShape); + } + } + break; + case OperationType::MAX_POOL_2D: + { + const size_t inCount = ins.size(); + if ((inCount != 10 && inCount != 7) || !allParametersPresent(inCount, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + const RunTimeOperandInfo &input = mOperands[ins[0]]; + + int32_t padding_left, padding_right; + int32_t padding_top, padding_bottom; + int32_t stride_width, stride_height; + int32_t filter_width, filter_height; + int32_t activation; + + if (inCount == 10) + { + padding_left = getScalarData(mOperands[ins[1]]); + padding_right = getScalarData(mOperands[ins[2]]); + padding_top = getScalarData(mOperands[ins[3]]); + padding_bottom = getScalarData(mOperands[ins[4]]); + stride_width = getScalarData(mOperands[ins[5]]); + stride_height = getScalarData(mOperands[ins[6]]); + filter_width = getScalarData(mOperands[ins[7]]); + filter_height = getScalarData(mOperands[ins[8]]); + activation = getScalarData(mOperands[ins[9]]); + } + else + { + int32_t padding_implicit = getScalarData(mOperands[ins[1]]); + stride_width = getScalarData(mOperands[ins[2]]); + stride_height = getScalarData(mOperands[ins[3]]); + filter_width = getScalarData(mOperands[ins[4]]); + filter_height = getScalarData(mOperands[ins[5]]); + activation = getScalarData(mOperands[ins[6]]); + + Shape inputShape = input.shape(); + int32_t input_width = getSizeOfDimension(inputShape, 2); + int32_t input_height = getSizeOfDimension(inputShape, 1); + calculateExplicitPadding(input_width, stride_width, filter_width, padding_implicit, + &padding_left, &padding_right); + calculateExplicitPadding(input_height, stride_height, filter_height, padding_implicit, + &padding_top, &padding_bottom); + } + + RunTimeOperandInfo &output = mOperands[outs[0]]; + Shape outShape = output.shape(); + + ASSERT(input.type == OperandType::TENSOR_FLOAT32); + { + success = maxPoolPrepare(input.shape(), padding_left, padding_right, padding_top, + padding_bottom, stride_width, stride_height, filter_width, + filter_height, &outShape) && + setInfoAndAllocateIfNeeded(&output, outShape) && + maxPoolFloat32(reinterpret_cast(input.buffer), input.shape(), padding_left, + padding_right, padding_top, padding_bottom, stride_width, stride_height, + filter_width, filter_height, activation, + reinterpret_cast(output.buffer), outShape); + } + } + break; + case OperationType::SOFTMAX: + { + if (!allParametersPresent(2, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + RunTimeOperandInfo &input = mOperands[ins[0]]; + float beta = getScalarData(mOperands[ins[1]]); + if (beta <= 0.0f) + { + LOG(ERROR) << "beta must be positive for softmax"; + return ANEURALNETWORKS_BAD_DATA; + } + + RunTimeOperandInfo &output = mOperands[outs[0]]; + Shape outShape = output.shape(); + + ASSERT(input.type == OperandType::TENSOR_FLOAT32); + { + success = softmaxPrepare(input.shape(), &outShape) && + setInfoAndAllocateIfNeeded(&output, outShape) && + softmaxFloat32(reinterpret_cast(input.buffer), input.shape(), beta, + reinterpret_cast(output.buffer), output.shape()); + } + } + break; + case OperationType::FULLY_CONNECTED: + { + if (!allParametersPresent(4, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + RunTimeOperandInfo &input = mOperands[ins[0]]; + RunTimeOperandInfo &weights = mOperands[ins[1]]; + RunTimeOperandInfo &bias = mOperands[ins[2]]; + + int32_t activation = getScalarData(mOperands[ins[3]]); + + RunTimeOperandInfo &output = mOperands[outs[0]]; + Shape outShape = output.shape(); + + ASSERT(input.type == OperandType::TENSOR_FLOAT32); + { + success = fullyConnectedPrepare(input.shape(), weights.shape(), bias.shape(), &outShape) && + setInfoAndAllocateIfNeeded(&output, outShape) && + fullyConnectedFloat32(reinterpret_cast(input.buffer), input.shape(), + reinterpret_cast(weights.buffer), weights.shape(), + reinterpret_cast(bias.buffer), bias.shape(), activation, + reinterpret_cast(output.buffer), outShape); + } + } + break; + case OperationType::CONCATENATION: + { + if (outs.size() != 1 || ins.size() < 2) + { + return ANEURALNETWORKS_BAD_DATA; + } + int numInputTensors = ins.size() - 1; + int32_t axis = getScalarData(mOperands[ins[numInputTensors]]); + + RunTimeOperandInfo &output = mOperands[outs[0]]; + Shape outShape = output.shape(); + + const RunTimeOperandInfo &firstInput = mOperands[ins[0]]; + ASSERT(firstInput.type == OperandType::TENSOR_FLOAT32); + { + std::vector inputShapes(numInputTensors); + std::vector inputDataPtrs(numInputTensors); + + for (int i = 0; i < numInputTensors; i++) + { + RunTimeOperandInfo &input = mOperands[ins[i]]; + inputShapes[i] = input.shape(); + inputDataPtrs[i] = reinterpret_cast(input.buffer); + } + success = concatenationPrepare(inputShapes, axis, &outShape) && + setInfoAndAllocateIfNeeded(&output, outShape) && + concatenationFloat32(inputDataPtrs, inputShapes, axis, reinterpret_cast(output.buffer), + outShape); + } + } + break; + case OperationType::RESHAPE: + { + if (!allParametersPresent(2, 1)) + { + return ANEURALNETWORKS_BAD_DATA; + } + const RunTimeOperandInfo &input = mOperands[ins[0]]; + const RunTimeOperandInfo &targetShape = mOperands[ins[1]]; + + RunTimeOperandInfo &output = mOperands[outs[0]]; + Shape outShape = output.shape(); + + success = reshapePrepare(input.shape(), reinterpret_cast(targetShape.buffer), + getNumberOfElements(targetShape.shape()), &outShape) && + setInfoAndAllocateIfNeeded(&output, outShape) && + reshapeGeneric(reinterpret_cast(input.buffer), input.shape(), + reinterpret_cast(output.buffer), outShape); + } + break; + default: + NYI(getOperationName(operation.type)); + break; + } + if (!success) + { + LOG(ERROR) << getOperationName(operation.type) << " failed."; + return ANEURALNETWORKS_OP_FAILED; + } + + freeNoLongerUsedOperands(ins); + return ANEURALNETWORKS_NO_ERROR; +} diff --git a/contrib/ann/runtimes/ref/src/Executor.h b/contrib/ann/runtimes/ref/src/Executor.h new file mode 100644 index 0000000..66dcca1 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/Executor.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __EXECUTOR_H__ +#define __EXECUTOR_H__ + +#include "Model.h" + +#include "Shape.h" +#include "Request.h" + +#include + +// Information we maintain about each operand during execution that +// may change during execution. +struct RunTimeOperandInfo +{ + // TODO Storing the type here is redundant, as it won't change during execution. + OperandType type; + + // The type and dimensions of the operand. The dimensions can + // change at runtime. We include the type because it's useful + // to pass together with the dimension to the functions implementing + // the operators. + // + // Q: Is it possible?? + std::vector dimensions; + float scale; + int32_t zeroPoint; + + // Where the operand's data is stored. Check the corresponding + // location information in the model to figure out if this points + // to memory we have allocated for an temporary operand. + uint8_t *buffer; + // The length of the buffer. + uint32_t length; + + // Whether this is a temporary variable, a model input, a constant, etc. + OperandLifeTime lifetime; + + // Keeps track of how many operations have yet to make use + // of this temporary variable. When the count is decremented to 0, + // we free the buffer. For non-temporary variables, this count is + // always 0. + uint32_t numberOfUsesLeft; + + Shape shape() const + { + return Shape{.type = type, .dimensions = dimensions, .scale = scale, .offset = zeroPoint}; + } +}; + +// Used to keep a pointer to each of the memory pools +struct RunTimePoolInfo +{ + uint8_t *buffer; + + bool set(uint8_t *m) + { + buffer = m; + return true; + } +}; + +// This class is used to execute a model on the CPU. +class Executor +{ +public: + // Executes the model. The results will be stored at the locations + // specified in the constructor. + // The model must outlive the executor. We prevent it from being modified + // while this is executing. + int run(const Model &model, const Request &request, + const std::vector &modelPoolInfos, + const std::vector &requestPoolInfos); + +private: + bool initializeRunTimeInfo(const std::vector &modelPoolInfos, + const std::vector &requestPoolInfos); + // Runs one operation of the graph. + int executeOperation(const Operation &entry); + // Decrement the usage count for the operands listed. Frees the memory + // allocated for any temporary variable with a count of zero. + void freeNoLongerUsedOperands(const std::vector &inputs); + + // The model and the request that we'll execute. Only valid while run() + // is being executed. + const Model *mModel = nullptr; + const Request *mRequest = nullptr; + + // We're copying the list of all the dimensions from the model, as + // these may be modified when we run the operatins. Since we're + // making a full copy, the indexes used in the operand description + // stay valid. + // std::vector mDimensions; + // Runtime information about all the operands. + std::vector mOperands; +}; + +#endif // __CPU_EXECUTOR_H__ diff --git a/contrib/ann/runtimes/ref/src/Logging.cpp b/contrib/ann/runtimes/ref/src/Logging.cpp new file mode 100644 index 0000000..4f849ef --- /dev/null +++ b/contrib/ann/runtimes/ref/src/Logging.cpp @@ -0,0 +1,30 @@ +/* + * 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 "Logging.h" + +VLogging::VLogging() +{ + _enabled = false; +} + +VLogging &VLogging::access() +{ + static VLogging instance; + return instance; +} + +std::ostream &VLogging::stream() { return std::cout; } diff --git a/contrib/ann/runtimes/ref/src/Logging.h b/contrib/ann/runtimes/ref/src/Logging.h new file mode 100644 index 0000000..1f81ad6 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/Logging.h @@ -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. + */ + +#ifndef __LOGGING_H__ +#define __LOGGING_H__ + +#include + +class VLogging +{ +public: + static VLogging &access(void); + bool enabled() const { return _enabled; } + std::ostream &stream(void); + +private: + VLogging(); + +private: + bool _enabled; +}; + +#define LOG(...) std::cout << std::endl +#define VLOG(...) \ + if (VLogging::access().enabled()) \ + (VLogging::access().stream() << std::endl) +#define NYI(module) std::cout << "NYI : '" << module << "' is not supported now." << std::endl; + +#endif // __LOGGING_H__ diff --git a/contrib/ann/runtimes/ref/src/Macro.h b/contrib/ann/runtimes/ref/src/Macro.h new file mode 100644 index 0000000..829c154 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/Macro.h @@ -0,0 +1,22 @@ +/* + * 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 __MACRO_H__ +#define __MACRO_H__ + +#define COUNT(X) (sizeof(X) / sizeof(X[0])) + +#endif // __MACRO_H__ diff --git a/contrib/ann/runtimes/ref/src/Memory.cpp b/contrib/ann/runtimes/ref/src/Memory.cpp new file mode 100644 index 0000000..fd70f8d --- /dev/null +++ b/contrib/ann/runtimes/ref/src/Memory.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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. + */ + +#define LOG_TAG "Memory" + +#include "Memory.h" +#include "NeuralNetworks.h" // ANEURALNETWORKS_XXX + +#include + +MappedMemory::~MappedMemory() +{ + if (_base) + { + munmap(_base, _size); + } +} + +int MappedMemory::set(size_t size, int prot, int fd, size_t offset) +{ +#if 0 + if (fd < 0) + { + LOG(ERROR) << "ANeuralNetworksMemory_createFromFd invalid fd " << fd; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + if (size == 0 || fd < 0) + { + LOG(ERROR) << "Invalid size or fd"; + return ANEURALNETWORKS_BAD_DATA; + } + int dupfd = dup(fd); + if (dupfd == -1) + { + LOG(ERROR) << "Failed to dup the fd"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } +#endif + void * const base = mmap(nullptr, size, prot, MAP_PRIVATE, fd, offset); + + if (base == MAP_FAILED) + { + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + + _base = static_cast(base); + _size = size; + + return ANEURALNETWORKS_NO_ERROR; +} + +int MappedMemory::getPointer(uint8_t **buffer) const +{ + *buffer = _base; + return ANEURALNETWORKS_NO_ERROR; +} + +bool MappedMemory::validateSize(uint32_t offset, uint32_t length) const +{ + return true; +} + +PrivateMemory::~PrivateMemory() +{ + if (_base) + { + delete[] _base; + } +} + +int PrivateMemory::create(uint32_t size) +{ + auto base = new uint8_t[size]; + + // TODO Check allocation failure + _base = base; + _size = size; + + return ANEURALNETWORKS_NO_ERROR; +} + +int PrivateMemory::getPointer(uint8_t **buffer) const +{ + *buffer = _base; + return ANEURALNETWORKS_NO_ERROR; +} + +bool PrivateMemory::validateSize(uint32_t offset, uint32_t length) const +{ + return true; +} diff --git a/contrib/ann/runtimes/ref/src/Memory.h b/contrib/ann/runtimes/ref/src/Memory.h new file mode 100644 index 0000000..648b5c7 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/Memory.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 +#include + +// Represents a memory region. +struct Memory +{ + Memory() = default; + virtual ~Memory() = default; + + // Disallow copy semantics to ensure the runtime object can only be freed + // once. Copy semantics could be enabled if some sort of reference counting + // or deep-copy system for runtime objects is added later. + Memory(const Memory &) = delete; + Memory &operator=(const Memory &) = delete; + + // Returns a pointer to the underlying memory of this memory object. + virtual int getPointer(uint8_t **buffer) const = 0; + virtual bool validateSize(uint32_t offset, uint32_t length) const = 0; +}; + +class MappedMemory final : public Memory +{ +public: + MappedMemory() = default; + +public: + ~MappedMemory(); + +public: + // Create the native_handle based on input size, prot, and fd. + // Existing native_handle will be deleted, and mHidlMemory will wrap + // the newly created native_handle. + int set(size_t size, int prot, int fd, size_t offset); + +public: + int getPointer(uint8_t **buffer) const override; + bool validateSize(uint32_t offset, uint32_t length) const override; + +private: + uint8_t *_base = nullptr; + size_t _size = 0; +}; + +// Represents a memory region. +class AllocatedMemory : public Memory +{ +public: + AllocatedMemory() = default; + virtual ~AllocatedMemory() = default; + +public: + virtual int create(uint32_t size) = 0; + +public: + // Returns a pointer to the underlying memory of this memory object. + virtual int getPointer(uint8_t **buffer) const = 0; + virtual bool validateSize(uint32_t offset, uint32_t length) const = 0; +}; + +class PrivateMemory final : public AllocatedMemory +{ +public: + PrivateMemory() = default; + ~PrivateMemory(); + +public: + // Disallow copy semantics to ensure the runtime object can only be freed + // once. Copy semantics could be enabled if some sort of reference counting + // or deep-copy system for runtime objects is added later. + PrivateMemory(const PrivateMemory &) = delete; + PrivateMemory &operator=(const PrivateMemory &) = delete; + +public: + virtual int create(uint32_t size); + +public: + // Returns a pointer to the underlying memory of this memory object. + virtual int getPointer(uint8_t **buffer) const; + virtual bool validateSize(uint32_t offset, uint32_t length) const; + +private: + uint8_t *_base = nullptr; + size_t _size = 0; +}; + +#endif // __MEMORY_H__ diff --git a/contrib/ann/runtimes/ref/src/MemoryTracker.cpp b/contrib/ann/runtimes/ref/src/MemoryTracker.cpp new file mode 100644 index 0000000..3c65149 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/MemoryTracker.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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. + */ + +#define LOG_TAG "Memory" + +#include "NeuralNetworks.h" // For ANEURALNETWORKS_... +#include "MemoryTracker.h" + +#include "Logging.h" + +#include // It's for 'close' and 'dup' + // TODO-NNRT : Remove this if included another header including this. + +uint32_t MemoryTracker::add(const Memory *memory) +{ + VLOG(MODEL) << __func__ << " for " << memory; + // See if we already have this memory. If so, + // return its index. + auto i = mKnown.find(memory); + if (i != mKnown.end()) + { + return i->second; + } + VLOG(MODEL) << "It's new"; + // It's a new one. Save it an assign an index to it. + size_t next = mKnown.size(); + if (next > 0xFFFFFFFF) + { + LOG(ERROR) << "ANeuralNetworks more than 2^32 memories."; + return ANEURALNETWORKS_BAD_DATA; + } + uint32_t idx = static_cast(next); + mKnown[memory] = idx; + mMemories.push_back(memory); + return idx; +} diff --git a/contrib/ann/runtimes/ref/src/MemoryTracker.h b/contrib/ann/runtimes/ref/src/MemoryTracker.h new file mode 100644 index 0000000..af687d1 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/MemoryTracker.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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_TRACKER_H__ +#define __MEMORY_TRACKER_H__ + +#include "Memory.h" + +#include +#include + +// A utility class to accumulate mulitple Memory objects and assign each +// a distinct index number, starting with 0. +// +// The user of this class is responsible for avoiding concurrent calls +// to this class from multiple threads. +class MemoryTracker +{ +public: + // Adds the memory, if it does not already exists. Returns its index. + // The memories should survive the tracker. + uint32_t add(const Memory *memory); + // Returns the number of memories contained. + uint32_t size() const { return static_cast(mKnown.size()); } + // Returns the ith memory. + const Memory *operator[](size_t i) const { return mMemories[i]; } + +private: + // The vector of Memory pointers we are building. + std::vector mMemories; + // A faster way to see if we already have a memory than doing find(). + std::unordered_map mKnown; +}; + +#endif // __MEMORY_TRACKER_H__ diff --git a/contrib/ann/runtimes/ref/src/Model.h b/contrib/ann/runtimes/ref/src/Model.h new file mode 100644 index 0000000..dc6a0d3 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/Model.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "Operand.h" +#include "Operation.h" + +#include +#include + +struct Model final { + std::vector operands; + std::vector operations; + + std::vector inputIndexes; + std::vector outputIndexes; + + std::vector operandValues; + + std::vector pools; +}; + +#endif // __MODEL_H__ diff --git a/contrib/ann/runtimes/ref/src/ModelArgumentInfo.cpp b/contrib/ann/runtimes/ref/src/ModelArgumentInfo.cpp new file mode 100644 index 0000000..3c10cd0 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ModelArgumentInfo.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "ModelArgumentInfo.h" +#include "NeuralNetworks.h" // For ANEURALNETWORKS_XXX +#include "Logging.h" +#include "Assert.h" + +// TODO-NNRT: Consider removing ModelArgumentInfo completely if it's not necessary +int ModelArgumentInfo::setFromPointer(const Operand &operand, + const ANeuralNetworksOperandType *type, void *data, + uint32_t length) +{ + if ((data == nullptr) != (length == 0)) + { + LOG(ERROR) << "Data pointer must be nullptr if and only if length is zero (data = " << data + << ", length = " << length << ")"; + return ANEURALNETWORKS_BAD_DATA; + } + if (data == nullptr) + { + state = ModelArgumentInfo::HAS_NO_VALUE; + } + else + { + int n = updateDimensionInfo(operand, type); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + uint32_t neededLength = sizeOfData(operand.type, dimensions); + if (neededLength != length) + { + LOG(ERROR) << "Setting argument with invalid length: " << length + << ", expected length: " << neededLength; + return ANEURALNETWORKS_BAD_DATA; + } + state = ModelArgumentInfo::POINTER; + } + buffer = data; + locationAndLength = {.poolIndex = 0, .offset = 0, .length = length}; + return ANEURALNETWORKS_NO_ERROR; +} + +int ModelArgumentInfo::setFromMemory(const Operand &operand, const ANeuralNetworksOperandType *type, + uint32_t poolIndex, uint32_t offset, uint32_t length) +{ + int n = updateDimensionInfo(operand, type); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + uint32_t neededLength = sizeOfData(operand.type, dimensions); + if (neededLength != length) + { + LOG(ERROR) << "Setting argument with invalid length: " << length + << ", expected length: " << neededLength; + return ANEURALNETWORKS_BAD_DATA; + } + + state = ModelArgumentInfo::MEMORY; + locationAndLength = {.poolIndex = poolIndex, .offset = offset, .length = length}; + buffer = nullptr; + return ANEURALNETWORKS_NO_ERROR; +} + +int ModelArgumentInfo::updateDimensionInfo(const Operand &operand, + const ANeuralNetworksOperandType *newType) +{ + ASSERT(dimensions.empty()); + if (newType == nullptr) + { + for (auto i : operand.dimensions) + { + if (i == 0) + { + LOG(ERROR) << "Setting input/output with unspecified dimensions"; + return ANEURALNETWORKS_BAD_DATA; + } + } + dimensions = operand.dimensions; + } + else + { + uint32_t count = newType->dimensionCount; + if (static_cast(newType->type) != operand.type || + count != operand.dimensions.size()) + { + LOG(ERROR) << "Setting input/output with incompatible types"; + return ANEURALNETWORKS_BAD_DATA; + } + dimensions = std::vector(count); + for (uint32_t i = 0; i < count; i++) + { + if (operand.dimensions[i] != 0 && operand.dimensions[i] != newType->dimensions[i]) + { + LOG(ERROR) << "Overriding a fully specified dimension is disallowed"; + return ANEURALNETWORKS_BAD_DATA; + } + else + { + dimensions[i] = newType->dimensions[i]; + } + } + } + return ANEURALNETWORKS_NO_ERROR; +} diff --git a/contrib/ann/runtimes/ref/src/ModelArgumentInfo.h b/contrib/ann/runtimes/ref/src/ModelArgumentInfo.h new file mode 100644 index 0000000..5773da7 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ModelArgumentInfo.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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_ARGUMENT_INFO_H__ +#define __MODEL_ARGUMENT_INFO_H__ + +#include "NeuralNetworks.h" + +#include "Operand.h" + +#include + +struct ModelArgumentInfo +{ + // Whether the argument was specified as being in a Memory, as a pointer, + // has no value, or has not been specified. + // If POINTER then: + // locationAndLength.length is valid. + // dimensions is valid. + // buffer is valid + // If MEMORY then: + // locationAndLength.location.{poolIndex, offset, length} is valid. + // dimensions is valid. + enum + { + POINTER, + MEMORY, + HAS_NO_VALUE, + UNSPECIFIED + } state = UNSPECIFIED; + + DataLocation locationAndLength; + + std::vector dimensions; + void *buffer; + + int setFromPointer(const Operand &operand, const ANeuralNetworksOperandType *type, void *buffer, + uint32_t length); + int setFromMemory(const Operand &operand, const ANeuralNetworksOperandType *type, + uint32_t poolIndex, uint32_t offset, uint32_t length); + int updateDimensionInfo(const Operand &operand, const ANeuralNetworksOperandType *newType); +}; + +#endif // __MODEL_ARGUMENT_INFO_H__ diff --git a/contrib/ann/runtimes/ref/src/ModelBuilder.cpp b/contrib/ann/runtimes/ref/src/ModelBuilder.cpp new file mode 100644 index 0000000..1f966bd --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ModelBuilder.cpp @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "ModelBuilder.h" + +#include "CompilationBuilder.h" +#include "Validation.h" +#include "Logging.h" +#include "Assert.h" + +#include +#include + +static inline void setFromIntList(std::vector *vec, uint32_t count, const uint32_t *data) +{ + vec->resize(count); + for (uint32_t i = 0; i < count; i++) + { + (*vec)[i] = data[i]; + } +} + +// Returns the number of padding bytes needed to align data of the +// specified length. It aligns object of length: +// 2, 3 on a 2 byte boundary, +// 4+ on a 4 byte boundary. +// We may want to have different alignments for tensors. +// TODO: This is arbitrary, more a proof of concept. We need +// to determine what this should be. +uint32_t alignBytesNeeded(uint32_t index, size_t length) +{ + uint32_t pattern; + if (length < 2) + { + pattern = 0; // No alignment necessary + } + else if (length < 4) + { + pattern = 1; // Align on 2-byte boundary + } + else + { + pattern = 3; // Align on 4-byte boundary + } + uint32_t extra = (~(index - 1)) & pattern; + return extra; +} + + +// The maximum number of operands and operations that a model may have. +const uint32_t MAX_NUMBER_OF_OPERANDS = 0xFFFFFFFE; +const uint32_t MAX_NUMBER_OF_OPERATIONS = 0xFFFFFFFE; + +bool ModelBuilder::badState(const char *name) +{ + if (mCompletedModel) + { + LOG(ERROR) << "ANeuralNetworksModel_" << name << " can't modify after model finished"; + return true; + } + if (mInvalidModel) + { + LOG(ERROR) << "ANeuralNetworksModel_" << name << " can't modify an invalid model"; + return true; + } + return false; +} + +int ModelBuilder::addOperand(const ANeuralNetworksOperandType &type) +{ + if (badState("addOperand")) + { + return ANEURALNETWORKS_BAD_STATE; + } + + int n = validateOperandType(type, "ANeuralNetworksModel_addOperand", true); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + size_t idx = mOperands.size(); + if (idx >= MAX_NUMBER_OF_OPERANDS) + { + LOG(ERROR) << "ANeuralNetworksModel_addOperand exceed max operands"; + return ANEURALNETWORKS_BAD_DATA; + } + mOperands.resize(idx + 1); + auto &operand = mOperands[idx]; + operand.type = static_cast(type.type); + setFromIntList(&operand.dimensions, type.dimensionCount, type.dimensions); + operand.numberOfConsumers = 0; + operand.scale = type.scale; + operand.zeroPoint = type.zeroPoint; + operand.lifetime = OperandLifeTime::TEMPORARY_VARIABLE; + operand.location = {.poolIndex = 0, .offset = 0, .length = 0}; + return ANEURALNETWORKS_NO_ERROR; +} + +int ModelBuilder::setOperandValue(uint32_t index, const void *buffer, size_t length) +{ + if (badState("setOperandValue")) + { + return ANEURALNETWORKS_BAD_STATE; + } + + VLOG(MODEL) << __func__ << " for operand " << index << " size " << length; + if (index >= operandCount()) + { + LOG(ERROR) << "ANeuralNetworksModel_setOperandValue setting operand " << index << " of " + << operandCount(); + return ANEURALNETWORKS_BAD_DATA; + } + Operand &operand = mOperands[index]; + if (buffer == nullptr) + { + if (length) + { + LOG(ERROR) << "ANeuralNetworksModel_setOperandValue buffer is nullptr but length is " + "not 0"; + return ANEURALNETWORKS_BAD_DATA; + } + operand.lifetime = OperandLifeTime::NO_VALUE; + // The location is unused and is set to zeros. + operand.location = {.poolIndex = 0, .offset = 0, .length = 0}; + } + else + { + if (length > 0xFFFFFFFF) + { + LOG(ERROR) << "ANeuralNetworksModel_setOperandValue value length of " << length + << " exceeds max size"; + return ANEURALNETWORKS_BAD_DATA; + } + uint32_t valueLength = static_cast(length); + uint32_t neededLength = sizeOfData(operand.type, operand.dimensions); + if (neededLength != valueLength) + { + LOG(ERROR) << "ANeuralNetworksModel_setOperandValue setting " << valueLength + << " bytes when needing " << neededLength; + return ANEURALNETWORKS_BAD_DATA; + } + if (valueLength <= ANEURALNETWORKS_MAX_SIZE_OF_IMMEDIATELY_COPIED_VALUES) + { + uint32_t existingSize = static_cast(mSmallOperandValues.size()); + uint32_t extraBytes = alignBytesNeeded(existingSize, valueLength); + mSmallOperandValues.resize(existingSize + extraBytes + valueLength); + operand.lifetime = OperandLifeTime::CONSTANT_COPY; + operand.location = { + .poolIndex = 0, .offset = existingSize + extraBytes, .length = neededLength}; + memcpy(&mSmallOperandValues[operand.location.offset], buffer, valueLength); + VLOG(MODEL) << "Copied small value to offset " << operand.location.offset; + } + else + { + VLOG(MODEL) << "Saving large value"; + operand.lifetime = OperandLifeTime::CONSTANT_REFERENCE; + // The values for poolIndex and offset will be set when the model is finished. + operand.location = {.poolIndex = 0, .offset = 0, .length = valueLength}; + // We keep track of the buffers. We'll allocate the shared memory only + // once we know the total size, to avoid needless copies. + mLargeOperandValues.push_back(LargeValue{.operandIndex = index, .buffer = buffer}); + } + } + return ANEURALNETWORKS_NO_ERROR; +} + +int ModelBuilder::setOperandValueFromMemory(uint32_t index, const Memory *memory, uint32_t offset, + size_t length) +{ + VLOG(MODEL) << __func__ << " for operand " << index << " offset " << offset << " size " << length; + if (badState("setOperandValueFromMemory")) + { + return ANEURALNETWORKS_BAD_STATE; + } + + if (index >= operandCount()) + { + LOG(ERROR) << "ANeuralNetworksModel_setOperandValueFromMemory setting operand " << index + << " of " << operandCount(); + return ANEURALNETWORKS_BAD_DATA; + } + Operand &operand = mOperands[index]; + uint32_t neededLength = sizeOfData(operand.type, operand.dimensions); + if (neededLength != length) + { + LOG(ERROR) << "ANeuralNetworksModel_setOperandValueFromMemory setting " << length + << " bytes when needing " << neededLength; + return ANEURALNETWORKS_BAD_DATA; + } + if (!memory->validateSize(offset, length)) + { + return ANEURALNETWORKS_BAD_DATA; + } + // TODO validate does not exceed length of memory + operand.lifetime = OperandLifeTime::CONSTANT_REFERENCE; + operand.location = {.poolIndex = mMemories.add(memory), .offset = offset, .length = neededLength}; + return ANEURALNETWORKS_NO_ERROR; +} + +int ModelBuilder::addOperation(OperationType type, uint32_t inputCount, const uint32_t *inputs, + uint32_t outputCount, const uint32_t *outputs) +{ + + if (badState("addOperation")) + { + return ANEURALNETWORKS_BAD_STATE; + } + + if (!validateOperationType(type)) + { + LOG(ERROR) << "ANeuralNetworksModel_addOperation invalid operations type " + << static_cast(type); + return ANEURALNETWORKS_BAD_DATA; + } + int n = validateOperandList(inputCount, inputs, operandCount(), + "ANeuralNetworksModel_addOperation inputs"); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + n = validateOperandList(outputCount, outputs, operandCount(), + "ANeuralNetworksModel_addOperation outputs"); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + + uint32_t operationIndex = operationCount(); + if (operationIndex >= MAX_NUMBER_OF_OPERATIONS) + { + LOG(ERROR) << "ANeuralNetworksModel_addOperation exceed max operations"; + return ANEURALNETWORKS_BAD_DATA; + } + mOperations.resize(operationIndex + 1); + auto &entry = mOperations[operationIndex]; + entry.type = type; + + setFromIntList(&entry.inputs, inputCount, inputs); + setFromIntList(&entry.outputs, outputCount, outputs); + for (uint32_t i : entry.inputs) + { + mOperands[i].numberOfConsumers++; + // TODO mOperands[i].consumers.push_back(operationIndex); + } + return ANEURALNETWORKS_NO_ERROR; +} + +int ModelBuilder::identifyInputsAndOutputs(uint32_t inputCount, const uint32_t *inputs, + uint32_t outputCount, const uint32_t *outputs) +{ + if (badState("identifyInputsAndOutputs")) + { + return ANEURALNETWORKS_BAD_STATE; + } + + int n = validateOperandList(inputCount, inputs, operandCount(), + "ANeuralNetworksModel_identifyInputsAndOutputs inputs"); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + n = validateOperandList(outputCount, outputs, operandCount(), + "ANeuralNetworksModel_identifyInputsAndOutputs outputs"); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + + // Makes a copy of the index list, validates the arguments, and changes + // the lifetime info of the corresponding operand. + auto setArguments = [&](std::vector *indexVector, uint32_t indexCount, + const uint32_t *indexList, OperandLifeTime lifetime) -> bool { + indexVector->resize(indexCount); + for (uint32_t i = 0; i < indexCount; i++) + { + const uint32_t operandIndex = indexList[i]; + if (operandIndex >= mOperands.size()) + { + LOG(ERROR) << "ANeuralNetworksModel_identifyInputsAndOutputs Can't set input or output " + "to be " + << operandIndex << " as this exceeds the numbe of operands " << mOperands.size(); + return false; + } + (*indexVector)[i] = operandIndex; + Operand &operand = mOperands[operandIndex]; + if (operand.lifetime != OperandLifeTime::TEMPORARY_VARIABLE) + { + LOG(ERROR) << "ANeuralNetworksModel_identifyInputsAndOutputs Can't set operand " + << operandIndex + << " to be an input or output. Check that it's not a constant or " + "already an input or output"; + return false; + } + operand.lifetime = lifetime; + } + return true; + }; + + if (!setArguments(&mInputIndexes, inputCount, inputs, OperandLifeTime::MODEL_INPUT) || + !setArguments(&mOutputIndexes, outputCount, outputs, OperandLifeTime::MODEL_OUTPUT)) + { + return ANEURALNETWORKS_BAD_DATA; + } + + return ANEURALNETWORKS_NO_ERROR; +} + +int ModelBuilder::createCompilation(CompilationBuilder **compilation) +{ + if (!mCompletedModel || mInvalidModel) + { + LOG(ERROR) << "ANeuralNetworksCompilation_create passed an unfinished model"; + *compilation = nullptr; + return ANEURALNETWORKS_BAD_STATE; + } + *compilation = new CompilationBuilder(this); + return (*compilation ? ANEURALNETWORKS_NO_ERROR : ANEURALNETWORKS_OUT_OF_MEMORY); +} + +int ModelBuilder::finish() +{ + if (mCompletedModel) + { + LOG(ERROR) << "ANeuralNetworksModel_finish called more than once"; + return ANEURALNETWORKS_BAD_STATE; + } + if (mInvalidModel) + { + LOG(ERROR) << "ANeuralNetworksModel_finish called on an invalid model"; + return ANEURALNETWORKS_BAD_STATE; + } + + int n = copyLargeValuesToMemory(); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + + Model modelForValidation; + publish(&modelForValidation); + if (!validateModel(modelForValidation)) + { + LOG(ERROR) << "ANeuralNetworksModel_finish called on invalid model"; + mInvalidModel = true; + return ANEURALNETWORKS_BAD_DATA; + } + + // We sort the operations so that they will be in the appropriate + // order for a single-threaded, op at a time execution. + // TODO: we don't need this if we always run the partitioner. + sortIntoRunOrder(); + mCompletedModel = true; + return ANEURALNETWORKS_NO_ERROR; +} + +void ModelBuilder::sortIntoRunOrder() +{ + // Tracks the operations that can be executed. + std::vector opsReadyToRun; + std::vector runOrder; + + // Tracks how many inputs are needed for each operation to be ready to run. + std::multimap operandToOperations; + std::vector unknownInputCount(operationCount()); + for (uint32_t operationIndex = 0; operationIndex < operationCount(); operationIndex++) + { + uint32_t &count = unknownInputCount[operationIndex]; + count = 0; + for (uint32_t operandIndex : mOperations[operationIndex].inputs) + { + auto lifetime = mOperands[operandIndex].lifetime; + if (lifetime == OperandLifeTime::TEMPORARY_VARIABLE || + lifetime == OperandLifeTime::MODEL_OUTPUT) + { + count++; + operandToOperations.insert(std::pair(operandIndex, operationIndex)); + } + } + if (count == 0) + { + opsReadyToRun.push_back(operationIndex); + } + } + + while (opsReadyToRun.size() > 0) + { + // Execute the next op + int opIndex = opsReadyToRun.back(); + opsReadyToRun.pop_back(); + const Operation &operation = mOperations[opIndex]; + + runOrder.push_back(mOperations[opIndex]); + + // Mark all its outputs as known. + for (uint32_t operandIndex : operation.outputs) + { + auto range = operandToOperations.equal_range(operandIndex); + for (auto i = range.first; i != range.second; i++) + { + uint32_t &count = unknownInputCount[i->second]; + if (--count == 0) + { + opsReadyToRun.push_back(i->second); + } + } + } + } + mOperations = runOrder; +} + +void ModelBuilder::publish(Model *model) const +{ + model->operands = mOperands; + model->operations = mOperations; + model->inputIndexes = mInputIndexes; + model->outputIndexes = mOutputIndexes; + model->operandValues = mSmallOperandValues; + + uint32_t count = mMemories.size(); + model->pools.resize(count); + for (uint32_t i = 0; i < count; i++) + { + uint8_t *buffer; + mMemories[i]->getPointer(&buffer); + model->pools[i] = buffer; + } +} + +int ModelBuilder::copyLargeValuesToMemory() +{ + if (!mLargeOperandValues.empty()) + { + // Calculate the size of the shared memory needed for all the large values. + // Also sets the offset for each value within the memory. + size_t poolSize = 0; + for (LargeValue &l : mLargeOperandValues) + { + Operand &operand = mOperands[l.operandIndex]; + ASSERT(operand.lifetime == OperandLifeTime::CONSTANT_REFERENCE); + poolSize += alignBytesNeeded(poolSize, operand.location.length); + operand.location.offset = poolSize; + poolSize += operand.location.length; + } + + // Allocated the shared memory. + int n = mLargeValueMemory.create(poolSize); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + uint8_t *memoryPointer = nullptr; + n = mLargeValueMemory.getPointer(&memoryPointer); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + uint32_t poolIndex = mMemories.add(&mLargeValueMemory); + VLOG(MODEL) << "Allocated large value pool of size " << poolSize << " at index " << poolIndex; + + // Copy the values to this memory. + for (LargeValue &l : mLargeOperandValues) + { + Operand &operand = mOperands[l.operandIndex]; + operand.location.poolIndex = poolIndex; + memcpy(memoryPointer + operand.location.offset, l.buffer, operand.location.length); + } + } + return ANEURALNETWORKS_NO_ERROR; +} diff --git a/contrib/ann/runtimes/ref/src/ModelBuilder.h b/contrib/ann/runtimes/ref/src/ModelBuilder.h new file mode 100644 index 0000000..ad50fad --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ModelBuilder.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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_BUILDER_H__ +#define __MODEL_BUILDER_H__ + +#include "NeuralNetworks.h" + +#include "Model.h" + +#include "Memory.h" +#include "MemoryTracker.h" + +#include +#include + +class CompilationBuilder; + +class ModelBuilder +{ +public: + virtual ~ModelBuilder() = default; + +public: + // Adds an operand to the model. + int addOperand(const ANeuralNetworksOperandType &type); + +public: + int setOperandValue(uint32_t index, const void *buffer, size_t length); + int setOperandValueFromMemory(uint32_t index, const Memory *memory, uint32_t offset, + size_t length); + +public: + int addOperation(OperationType type, uint32_t inputCount, const uint32_t *inputs, + uint32_t outputCount, const uint32_t *outputs); + +public: + int identifyInputsAndOutputs(uint32_t inputCount, const uint32_t *inputs, uint32_t outputCount, + const uint32_t *outputs); + +public: + int finish(); + bool isFinished() const { return mCompletedModel; } + +public: + int createCompilation(CompilationBuilder **compilation); + +public: + void publish(Model *model) const; + +public: + uint32_t operandCount() const + { + // We don't allow more than uint32_t worth of operands + return static_cast(mOperands.size()); + } + uint32_t operationCount() const + { + // We don't allow more than uint32_t worth of operations + return static_cast(mOperations.size()); + } + +public: + uint32_t inputCount() const { return static_cast(mInputIndexes.size()); } + uint32_t getInputOperandIndex(uint32_t i) const { return mInputIndexes[i]; } + const Operand &getInputOperand(uint32_t i) const { return mOperands[getInputOperandIndex(i)]; } + +public: + uint32_t outputCount() const { return static_cast(mOutputIndexes.size()); } + uint32_t getOutputOperandIndex(uint32_t i) const { return mOutputIndexes[i]; } + const Operand &getOutputOperand(uint32_t i) const { return mOperands[getOutputOperandIndex(i)]; } + +public: + const Operand &getOperand(uint32_t index) const { return mOperands[index]; } + const Operation &getOperation(uint32_t index) const { return mOperations[index]; } + +public: + const MemoryTracker &getMemories() const { return mMemories; } + const std::vector &getOperations() const { return mOperations; } + +private: + // Return true if either mCompleteModel or mInvalidModel is true. + bool badState(const char *name); + + // Sorts the operations to be in the correct order for single threaded + // node-at-a-time execution. + void sortIntoRunOrder(); + + // Copies the large values to a shared memory, if we have any. + int copyLargeValuesToMemory(); + +private: + // The operations of the graph. + std::vector mOperations; + // The description of the operands of the graph. + std::vector mOperands; + + // Specifies where to find the list of indexes identifying + // the inputs and outputs of the model. The offset is into + // the mOperandIndexes table. + std::vector mInputIndexes; + std::vector mOutputIndexes; + + MemoryTracker mMemories; + + // The value of the small operands that are defined at model + // creation time. + std::vector mSmallOperandValues; + + struct LargeValue + { + uint32_t operandIndex; + const void *buffer; + }; + // Operand index and buffer pointer for all the large operand values of this model. + std::vector mLargeOperandValues; + PrivateMemory mLargeValueMemory; + + // Once the model has been finished, we should not allow further + // modifications to the model. + mutable bool mCompletedModel = false; + + // Any invalid manipulation of the model will mark the model invalid. + // No further modifications are allowed to the model. + mutable bool mInvalidModel = false; +}; + +#endif // __MODEL_BUILDER_H__ diff --git a/contrib/ann/runtimes/ref/src/NeuralNetworks.cpp b/contrib/ann/runtimes/ref/src/NeuralNetworks.cpp new file mode 100644 index 0000000..186647b --- /dev/null +++ b/contrib/ann/runtimes/ref/src/NeuralNetworks.cpp @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "CompilationBuilder.h" +#include "ExecutionBuilder.h" +#include "ModelBuilder.h" +#include "Memory.h" + +#include "Logging.h" + +#include + +namespace +{ +template std::unique_ptr make_unique(Args &&... args) +{ + // NOTE std::make_unique is missing in C++11 standard + return std::unique_ptr(new T(std::forward(args)...)); +} +} // namespace + +int ANeuralNetworksMemory_createFromFd(size_t size, int prot, int fd, size_t offset, + ANeuralNetworksMemory **memory) +{ + *memory = nullptr; + auto m = make_unique(); + if (m == nullptr) + { + return ANEURALNETWORKS_OUT_OF_MEMORY; + } + int n = m->set(size, prot, fd, offset); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + *memory = reinterpret_cast(m.release()); + return ANEURALNETWORKS_NO_ERROR; +} + +void ANeuralNetworksMemory_free(ANeuralNetworksMemory *memory) +{ + // No validation. Free of nullptr is valid. + Memory *m = reinterpret_cast(memory); + delete m; +} + +int ANeuralNetworksModel_create(ANeuralNetworksModel **model) +{ + if (!model) + { + LOG(ERROR) << "ANeuralNetworksModel_create passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + ModelBuilder *m = new ModelBuilder(); + if (m == nullptr) + { + *model = nullptr; + return ANEURALNETWORKS_OUT_OF_MEMORY; + } + *model = reinterpret_cast(m); + return ANEURALNETWORKS_NO_ERROR; +} + +void ANeuralNetworksModel_free(ANeuralNetworksModel *model) +{ + // No validation. Free of nullptr is valid. + ModelBuilder *m = reinterpret_cast(model); + delete m; +} + +int ANeuralNetworksModel_finish(ANeuralNetworksModel *model) +{ + if (!model) + { + LOG(ERROR) << "ANeuralNetworksModel_finish passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + ModelBuilder *m = reinterpret_cast(model); + return m->finish(); +} + +int ANeuralNetworksModel_addOperand(ANeuralNetworksModel *model, + const ANeuralNetworksOperandType *type) +{ + if (!model || !type) + { + LOG(ERROR) << "ANeuralNetworksModel_addOperand passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + ModelBuilder *m = reinterpret_cast(model); + return m->addOperand(*type); +} + +int ANeuralNetworksModel_setOperandValue(ANeuralNetworksModel *model, int32_t index, + const void *buffer, size_t length) +{ + if (!model || !buffer) + { + LOG(ERROR) << "ANeuralNetworksModel_setOperandValue passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + ModelBuilder *m = reinterpret_cast(model); + return m->setOperandValue(index, buffer, length); +} + +int ANeuralNetworksModel_setOperandValueFromMemory(ANeuralNetworksModel *model, int32_t index, + const ANeuralNetworksMemory *memory, + size_t offset, size_t length) +{ + if (!model || !memory) + { + LOG(ERROR) << "ANeuralNetworksModel_setOperandValue passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + const Memory *mem = reinterpret_cast(memory); + ModelBuilder *m = reinterpret_cast(model); + return m->setOperandValueFromMemory(index, mem, offset, length); +} + +int ANeuralNetworksModel_addOperation(ANeuralNetworksModel *model, + ANeuralNetworksOperationType type, uint32_t inputCount, + const uint32_t *inputs, uint32_t outputCount, + const uint32_t *outputs) +{ + if (!model || !inputs || !outputs) + { + LOG(ERROR) << "ANeuralNetworksModel_addOperation passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + ModelBuilder *m = reinterpret_cast(model); + return m->addOperation(static_cast(type), inputCount, inputs, outputCount, + outputs); +} + +int ANeuralNetworksModel_identifyInputsAndOutputs(ANeuralNetworksModel *model, uint32_t inputCount, + const uint32_t *inputs, uint32_t outputCount, + const uint32_t *outputs) +{ + if (!model || !inputs || !outputs) + { + LOG(ERROR) << ("ANeuralNetworksModel_identifyInputsAndOutputs passed a nullptr"); + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + ModelBuilder *m = reinterpret_cast(model); + return m->identifyInputsAndOutputs(inputCount, inputs, outputCount, outputs); +} + +int ANeuralNetworksCompilation_create(ANeuralNetworksModel *model, + ANeuralNetworksCompilation **compilation) +{ + if (!model || !compilation) + { + LOG(ERROR) << "ANeuralNetworksCompilation_create passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + + ModelBuilder *m = reinterpret_cast(model); + CompilationBuilder *c = nullptr; + int result = m->createCompilation(&c); + *compilation = reinterpret_cast(c); + return result; +} + +void ANeuralNetworksCompilation_free(ANeuralNetworksCompilation *compilation) +{ + // No validation. Free of nullptr is valid. + // TODO specification says that a compilation-in-flight can be deleted + CompilationBuilder *c = reinterpret_cast(compilation); + delete c; +} + +int ANeuralNetworksCompilation_setPreference(ANeuralNetworksCompilation *compilation, + int32_t preference) +{ + if (!compilation) + { + LOG(ERROR) << "ANeuralNetworksCompilation_setPreference passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + // NOTE Ignore preference + return ANEURALNETWORKS_NO_ERROR; +} + +int ANeuralNetworksCompilation_finish(ANeuralNetworksCompilation *compilation) +{ + if (!compilation) + { + LOG(ERROR) << "ANeuralNetworksCompilation_finish passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + CompilationBuilder *c = reinterpret_cast(compilation); + return c->finish(); +} + +int ANeuralNetworksExecution_create(ANeuralNetworksCompilation *compilation, + ANeuralNetworksExecution **execution) +{ + if (!compilation || !execution) + { + LOG(ERROR) << "ANeuralNetworksExecution_create passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + + CompilationBuilder *c = reinterpret_cast(compilation); + ExecutionBuilder *r = nullptr; + int result = c->createExecution(&r); + *execution = reinterpret_cast(r); + return result; +} + +void ANeuralNetworksExecution_free(ANeuralNetworksExecution *execution) +{ + // TODO specification says that an execution-in-flight can be deleted + // No validation. Free of nullptr is valid. + ExecutionBuilder *r = reinterpret_cast(execution); + delete r; +} + +int ANeuralNetworksExecution_setInput(ANeuralNetworksExecution *execution, int32_t index, + const ANeuralNetworksOperandType *type, const void *buffer, + size_t length) +{ + if (!execution) + { + LOG(ERROR) << "ANeuralNetworksExecution_setInput passed execution with a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + if (!buffer && length != 0) + { + LOG(ERROR) << "ANeuralNetworksExecution_setInput passed buffer with a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + + ExecutionBuilder *r = reinterpret_cast(execution); + return r->setInput(index, type, buffer, length); +} + +int ANeuralNetworksExecution_setInputFromMemory(ANeuralNetworksExecution *execution, int32_t index, + const ANeuralNetworksOperandType *type, + const ANeuralNetworksMemory *memory, size_t offset, + size_t length) +{ + if (!execution || !memory) + { + LOG(ERROR) << "ANeuralNetworksExecution_setInputFromMemory passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + + const Memory *m = reinterpret_cast(memory); + ExecutionBuilder *r = reinterpret_cast(execution); + return r->setInputFromMemory(index, type, m, offset, length); +} + +int ANeuralNetworksExecution_setOutput(ANeuralNetworksExecution *execution, int32_t index, + const ANeuralNetworksOperandType *type, void *buffer, + size_t length) +{ + if (!execution || !buffer) + { + LOG(ERROR) << "ANeuralNetworksExecution_setOutput passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + ExecutionBuilder *r = reinterpret_cast(execution); + return r->setOutput(index, type, buffer, length); +} + +int ANeuralNetworksExecution_setOutputFromMemory(ANeuralNetworksExecution *execution, int32_t index, + const ANeuralNetworksOperandType *type, + const ANeuralNetworksMemory *memory, size_t offset, + size_t length) +{ + if (!execution || !memory) + { + LOG(ERROR) << "ANeuralNetworksExecution_setOutputFromMemory passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + + ExecutionBuilder *r = reinterpret_cast(execution); + const Memory *m = reinterpret_cast(memory); + return r->setOutputFromMemory(index, type, m, offset, length); +} + +int ANeuralNetworksExecution_startCompute(ANeuralNetworksExecution *execution, + ANeuralNetworksEvent **event) +{ + if (!execution || !event) + { + LOG(ERROR) << "ANeuralNetworksExecution_startCompute passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + // TODO validate the rest + + ExecutionBuilder *r = reinterpret_cast(execution); + + // Dynamically allocate an sp to wrap an ExecutionCallback, seen in the NN + // API as an abstract event object. The sp object is + // returned when the execution has been successfully launched, otherwise a + // nullptr is returned. The sp is used for ref-counting purposes. Without + // it, the HIDL service could attempt to communicate with a dead callback + // object. + *event = nullptr; + + int *e = new int; + int n = r->startCompute(); + if (n != ANEURALNETWORKS_NO_ERROR) + { + return n; + } + *event = reinterpret_cast(e); + return ANEURALNETWORKS_NO_ERROR; +} + +int ANeuralNetworksEvent_wait(ANeuralNetworksEvent *event) +{ + if (event == nullptr) + { + LOG(ERROR) << "ANeuralNetworksEvent_wait passed a nullptr"; + return ANEURALNETWORKS_UNEXPECTED_NULL; + } + + return ANEURALNETWORKS_NO_ERROR; +} + +void ANeuralNetworksEvent_free(ANeuralNetworksEvent *event) +{ + // No validation. Free of nullptr is valid. + if (event) + { + int *e = reinterpret_cast(event); + delete e; + } +} diff --git a/contrib/ann/runtimes/ref/src/Operand.h b/contrib/ann/runtimes/ref/src/Operand.h new file mode 100644 index 0000000..870a056 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/Operand.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __OPERAND_H__ +#define __OPERAND_H__ + +#include "OperandType.h" + +#include +#include + +enum class OperandLifeTime : int32_t { + TEMPORARY_VARIABLE = 0, + MODEL_INPUT = 1, + MODEL_OUTPUT = 2, + CONSTANT_COPY = 3, + CONSTANT_REFERENCE = 4, + NO_VALUE = 5, +}; + +struct DataLocation final { + uint32_t poolIndex; + uint32_t offset; + uint32_t length; +}; + +struct Operand final { + OperandType type; + float scale; + int32_t zeroPoint; + + std::vector dimensions; + + DataLocation location; + + uint32_t numberOfConsumers; + OperandLifeTime lifetime; +}; + +// Returns the amount of space needed to store a value of the dimensions and +// type of this operand. +inline uint32_t sizeOfData(const Operand &operand) +{ + return sizeOfData(operand.type, operand.dimensions); +} + +#endif // __OPERAND_H__ diff --git a/contrib/ann/runtimes/ref/src/OperandType.cpp b/contrib/ann/runtimes/ref/src/OperandType.cpp new file mode 100644 index 0000000..9f75fcc --- /dev/null +++ b/contrib/ann/runtimes/ref/src/OperandType.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "OperandType.h" +#include "Macro.h" + +const char *kTypeNames[] = { + "FLOAT32", "INT32", "UINT32", "TENSOR_FLOAT32", "TENSOR_INT32", "TENSOR_QUANT8_ASYMM", +}; + +static_assert(COUNT(kTypeNames) == kNumberOfDataTypes, "kTypeNames is incorrect"); + +const uint32_t kSizeOfDataType[]{ + 4, // ANEURALNETWORKS_FLOAT32 + 4, // ANEURALNETWORKS_INT32 + 4, // ANEURALNETWORKS_UINT32 + 4, // ANEURALNETWORKS_TENSOR_FLOAT32 + 4, // ANEURALNETWORKS_TENSOR_INT32 + 1 // ANEURALNETWORKS_TENSOR_SYMMETRICAL_QUANT8 +}; + +static_assert(COUNT(kSizeOfDataType) == kNumberOfDataTypes, "kSizeOfDataType is incorrect"); + +const char *getOperandTypeName(OperandType type) +{ + uint32_t n = static_cast(type); + return kTypeNames[n]; +} + +uint32_t sizeOfData(OperandType type, const std::vector &dimensions) +{ + int n = static_cast(type); + + uint32_t size = kSizeOfDataType[n]; + + for (auto d : dimensions) + { + size *= d; + } + return size; +} diff --git a/contrib/ann/runtimes/ref/src/OperandType.h b/contrib/ann/runtimes/ref/src/OperandType.h new file mode 100644 index 0000000..3dfd232 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/OperandType.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __OPERAND_TYPES_H__ +#define __OPERAND_TYPES_H__ + +#include +#include + +enum class OperandType : int32_t { + FLOAT32 = 0, + INT32 = 1, + UINT32 = 2, + TENSOR_FLOAT32 = 3, + TENSOR_INT32 = 4, + TENSOR_QUANT8_ASYMM = 5, +}; + +// The number of data types (OperandCode) defined in NeuralNetworks.h. +const int kNumberOfDataTypes = 6; + +// Returns the name of the operand type in ASCII. +const char *getOperandTypeName(OperandType type); + +// Returns the amount of space needed to store a value of the specified +// dimensions and type. +uint32_t sizeOfData(OperandType type, const std::vector &dimensions); + +#endif // __OPERAND_TYPES_H__ diff --git a/contrib/ann/runtimes/ref/src/OperandType.probe.cpp b/contrib/ann/runtimes/ref/src/OperandType.probe.cpp new file mode 100644 index 0000000..2caffde --- /dev/null +++ b/contrib/ann/runtimes/ref/src/OperandType.probe.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "OperandType.h" +#include "NeuralNetworks.h" + +static_assert(static_cast(OperandType::FLOAT32) == ANEURALNETWORKS_FLOAT32, + "FLOAT32 != ANEURALNETWORKS_FLOAT32"); +static_assert(static_cast(OperandType::INT32) == ANEURALNETWORKS_INT32, + "INT32 != ANEURALNETWORKS_INT32"); +static_assert(static_cast(OperandType::UINT32) == ANEURALNETWORKS_UINT32, + "UINT32 != ANEURALNETWORKS_UINT32"); + +static_assert(static_cast(OperandType::TENSOR_FLOAT32) == ANEURALNETWORKS_TENSOR_FLOAT32, + "TENSOR_FLOAT32 != ANEURALNETWORKS_TENSOR_FLOAT32"); +static_assert(static_cast(OperandType::TENSOR_QUANT8_ASYMM) == + ANEURALNETWORKS_TENSOR_QUANT8_ASYMM, + "TENSOR_QUANT8_ASYMM != ANEURALNETWORKS_TENSOR_QUANT8_ASYMM"); diff --git a/contrib/ann/runtimes/ref/src/Operation.h b/contrib/ann/runtimes/ref/src/Operation.h new file mode 100644 index 0000000..37f6a87 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/Operation.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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_H__ +#define __OPERATION_H__ + +#include "OperationType.h" + +#include +#include + +struct Operation final { + OperationType type; + std::vector inputs; + std::vector outputs; +}; + +#endif // __OPERATION_H__ diff --git a/contrib/ann/runtimes/ref/src/OperationType.cpp b/contrib/ann/runtimes/ref/src/OperationType.cpp new file mode 100644 index 0000000..f938b4d --- /dev/null +++ b/contrib/ann/runtimes/ref/src/OperationType.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "OperationType.h" +#include "Macro.h" + +const char *kOperationNames[kNumberOfOperationTypes] = { + "ADD", + "AVERAGE_POOL", + "CONCATENATION", + "CONV", + "DEPTHWISE_CONV", + "DEPTH_TO_SPACE", + "DEQUANTIZE", + "EMBEDDING_LOOKUP", + "FLOOR", + "FULLY_CONNECTED", + "HASHTABLE_LOOKUP", + "L2_NORMALIZATION", + "L2_POOL", + "LOCAL_RESPONSE_NORMALIZATION", + "LOGISTIC", + "LSH_PROJECTION", + "LSTM", + "MAX_POOL", + "MUL", + "RELU", + "RELU1", + "RELU6", + "RESHAPE", + "RESIZE_BILINEAR", + "RNN", + "SOFTMAX", + "SPACE_TO_DEPTH", + "SVDF", + "TANH", + "BATCH_TO_SPACE_ND", // V1_1, will not be merged till V1_1 is finalized + "DIV", + "MEAN", // V1_1, will not be merged till V1_1 is finalized + "PAD", // V1_1, will not be merged till V1_1 is finalized + "SPACE_TO_BATCH_ND", // V1_1, will not be merged till V1_1 is finalized + "SQUEEZE", // V1_1, will not be merged till V1_1 is finalized + "STRIDED_SLICE", + "SUB", +}; + +static_assert(COUNT(kOperationNames) == kNumberOfOperationTypes, "kOperationNames is incorrect"); + +const char *getOperationName(OperationType type) +{ + uint32_t n = static_cast(type); + return kOperationNames[n]; +} diff --git a/contrib/ann/runtimes/ref/src/OperationType.h b/contrib/ann/runtimes/ref/src/OperationType.h new file mode 100644 index 0000000..090308d --- /dev/null +++ b/contrib/ann/runtimes/ref/src/OperationType.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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_TYPE_H__ +#define __OPERATION_TYPE_H__ + +#include + +enum class OperationType : int32_t { + ADD = 0, + AVERAGE_POOL_2D = 1, + CONCATENATION = 2, + CONV_2D = 3, + DEPTHWISE_CONV_2D = 4, + DEPTH_TO_SPACE = 5, + DEQUANTIZE = 6, + EMBEDDING_LOOKUP = 7, + FLOOR = 8, + FULLY_CONNECTED = 9, + HASHTABLE_LOOKUP = 10, + L2_NORMALIZATION = 11, + L2_POOL_2D = 12, + LOCAL_RESPONSE_NORMALIZATION = 13, + LOGISTIC = 14, + LSH_PROJECTION = 15, + LSTM = 16, + MAX_POOL_2D = 17, + MUL = 18, + RELU = 19, + RELU1 = 20, + RELU6 = 21, + RESHAPE = 22, + RESIZE_BILINEAR = 23, + RNN = 24, + SOFTMAX = 25, + SPACE_TO_DEPTH = 26, + SVDF = 27, + TANH = 28, + DIV = 30, + STRIDED_SLICE = 35, + SUB = 36, + OEM_OPERATION = 10000, +}; + +// The number of operation types (OperationCode) defined in NeuralNetworks.h. +const int kNumberOfOperationTypes = 37; + +// Returns the name of the operation in ASCII. +const char *getOperationName(OperationType opCode); + +#endif // __OPERATION_TYPE_H__ diff --git a/contrib/ann/runtimes/ref/src/OperationType.probe.cpp b/contrib/ann/runtimes/ref/src/OperationType.probe.cpp new file mode 100644 index 0000000..c9886f3 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/OperationType.probe.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "OperationType.h" +#include "NeuralNetworks.h" + +static_assert(static_cast(OperationType::ADD) == ANEURALNETWORKS_ADD, + "OperationType::ADD != ANEURALNETWORKS_ADD"); +static_assert(static_cast(OperationType::AVERAGE_POOL_2D) == + ANEURALNETWORKS_AVERAGE_POOL_2D, + "OperationType::AVERAGE_POOL_2D != ANEURALNETWORKS_AVERAGE_POOL_2D"); +static_assert(static_cast(OperationType::CONV_2D) == ANEURALNETWORKS_CONV_2D, + "OperationType::CONV_2D != ANEURALNETWORKS_CONV_2D"); +static_assert(static_cast(OperationType::DEPTHWISE_CONV_2D) == + ANEURALNETWORKS_DEPTHWISE_CONV_2D, + "OperationType::DEPTHWISE_CONV_2D != ANEURALNETWORKS_DEPTHWISE_CONV_2D"); +static_assert(static_cast(OperationType::DEPTH_TO_SPACE) == ANEURALNETWORKS_DEPTH_TO_SPACE, + "OperationType::DEPTH_TO_SPACE != ANEURALNETWORKS_DEPTH_TO_SPACE"); +static_assert(static_cast(OperationType::DEQUANTIZE) == ANEURALNETWORKS_DEQUANTIZE, + "OperationType::DEQUANTIZE != ANEURALNETWORKS_DEQUANTIZE"); +static_assert(static_cast(OperationType::EMBEDDING_LOOKUP) == + ANEURALNETWORKS_EMBEDDING_LOOKUP, + "OperationType::EMBEDDING_LOOKUP != ANEURALNETWORKS_EMBEDDING_LOOKUP"); +static_assert(static_cast(OperationType::FLOOR) == ANEURALNETWORKS_FLOOR, + "OperationType::FLOOR != ANEURALNETWORKS_FLOOR"); +static_assert(static_cast(OperationType::FULLY_CONNECTED) == + ANEURALNETWORKS_FULLY_CONNECTED, + "OperationType::FULLY_CONNECTED != ANEURALNETWORKS_FULLY_CONNECTED"); +static_assert(static_cast(OperationType::HASHTABLE_LOOKUP) == + ANEURALNETWORKS_HASHTABLE_LOOKUP, + "OperationType::HASHTABLE_LOOKUP != ANEURALNETWORKS_HASHTABLE_LOOKUP"); +static_assert(static_cast(OperationType::L2_NORMALIZATION) == + ANEURALNETWORKS_L2_NORMALIZATION, + "OperationType::L2_NORMALIZATION != ANEURALNETWORKS_L2_NORMALIZATION"); +static_assert(static_cast(OperationType::L2_POOL_2D) == ANEURALNETWORKS_L2_POOL_2D, + "OperationType::L2_POOL_2D != ANEURALNETWORKS_L2_POOL_2D"); +static_assert(static_cast(OperationType::LOCAL_RESPONSE_NORMALIZATION) == + ANEURALNETWORKS_LOCAL_RESPONSE_NORMALIZATION, + "OperationType::LOCAL_RESPONSE_NORMALIZATION != " + "ANEURALNETWORKS_LOCAL_RESPONSE_NORMALIZATION"); +static_assert(static_cast(OperationType::LOGISTIC) == ANEURALNETWORKS_LOGISTIC, + "OperationType::LOGISTIC != ANEURALNETWORKS_LOGISTIC"); +static_assert(static_cast(OperationType::LSH_PROJECTION) == ANEURALNETWORKS_LSH_PROJECTION, + "OperationType::LSH_PROJECTION != ANEURALNETWORKS_LSH_PROJECTION"); +static_assert(static_cast(OperationType::LSTM) == ANEURALNETWORKS_LSTM, + "OperationType::LSTM != ANEURALNETWORKS_LSTM"); +static_assert(static_cast(OperationType::MAX_POOL_2D) == ANEURALNETWORKS_MAX_POOL_2D, + "OperationType::MAX_POOL_2D != ANEURALNETWORKS_MAX_POOL_2D"); +static_assert(static_cast(OperationType::MUL) == ANEURALNETWORKS_MUL, + "OperationType::MUL != ANEURALNETWORKS_MUL"); +static_assert(static_cast(OperationType::RELU) == ANEURALNETWORKS_RELU, + "OperationType::RELU != ANEURALNETWORKS_RELU"); +static_assert(static_cast(OperationType::RELU1) == ANEURALNETWORKS_RELU1, + "OperationType::RELU1 != ANEURALNETWORKS_RELU1"); +static_assert(static_cast(OperationType::RELU6) == ANEURALNETWORKS_RELU6, + "OperationType::RELU6 != ANEURALNETWORKS_RELU6"); +static_assert(static_cast(OperationType::RESHAPE) == ANEURALNETWORKS_RESHAPE, + "OperationType::RESHAPE != ANEURALNETWORKS_RESHAPE"); +static_assert(static_cast(OperationType::RESIZE_BILINEAR) == + ANEURALNETWORKS_RESIZE_BILINEAR, + "OperationType::RESIZE_BILINEAR != ANEURALNETWORKS_RESIZE_BILINEAR"); +static_assert(static_cast(OperationType::RNN) == ANEURALNETWORKS_RNN, + "OperationType::RNN != ANEURALNETWORKS_RNN"); +static_assert(static_cast(OperationType::SOFTMAX) == ANEURALNETWORKS_SOFTMAX, + "OperationType::SOFTMAX != ANEURALNETWORKS_SOFTMAX"); +static_assert(static_cast(OperationType::SPACE_TO_DEPTH) == ANEURALNETWORKS_SPACE_TO_DEPTH, + "OperationType::SPACE_TO_DEPTH != ANEURALNETWORKS_SPACE_TO_DEPTH"); +static_assert(static_cast(OperationType::SVDF) == ANEURALNETWORKS_SVDF, + "OperationType::SVDF != ANEURALNETWORKS_SVDF"); +static_assert(static_cast(OperationType::TANH) == ANEURALNETWORKS_TANH, + "OperationType::TANH != ANEURALNETWORKS_TANH"); diff --git a/contrib/ann/runtimes/ref/src/Probe.cpp b/contrib/ann/runtimes/ref/src/Probe.cpp new file mode 100644 index 0000000..3a085a1 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/Probe.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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" + +// Make sure the constants defined in the header files have not changed values. +// IMPORTANT: When adding new values, update kNumberOfDataTypes or kNumberOfDataTypesOEM +// in Utils.h. +static_assert(ANEURALNETWORKS_FLOAT32 == 0, "ANEURALNETWORKS_FLOAT32 has changed"); +static_assert(ANEURALNETWORKS_INT32 == 1, "ANEURALNETWORKS_INT32 has changed"); +static_assert(ANEURALNETWORKS_UINT32 == 2, "ANEURALNETWORKS_UINT32 has changed"); +static_assert(ANEURALNETWORKS_TENSOR_FLOAT32 == 3, "ANEURALNETWORKS_TENSOR_FLOAT32 has changed"); +static_assert(ANEURALNETWORKS_TENSOR_INT32 == 4, "ANEURALNETWORKS_TENSOR_INT32 has changed"); +static_assert(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM == 5, + "ANEURALNETWORKS_TENSOR_QUANT8_ASYMM has changed"); + +// IMPORTANT: When adding new values, update kNumberOfOperationTypes or +// kNumberOfOperationTypesOEM kNumberOfOperationTypesEx in Utils.h. +static_assert(ANEURALNETWORKS_ADD == 0, "ANEURALNETWORKS_ADD has changed"); +static_assert(ANEURALNETWORKS_AVERAGE_POOL_2D == 1, "ANEURALNETWORKS_AVERAGE_POOL_2D has changed"); +static_assert(ANEURALNETWORKS_CONCATENATION == 2, "ANEURALNETWORKS_CONCATENATION has changed"); +static_assert(ANEURALNETWORKS_CONV_2D == 3, "ANEURALNETWORKS_CONV_2D has changed"); +static_assert(ANEURALNETWORKS_DEPTHWISE_CONV_2D == 4, + "ANEURALNETWORKS_DEPTHWISE_CONV_2D has changed"); +static_assert(ANEURALNETWORKS_DEPTH_TO_SPACE == 5, "ANEURALNETWORKS_DEPTH_TO_SPACE has changed"); +static_assert(ANEURALNETWORKS_DEQUANTIZE == 6, "ANEURALNETWORKS_DEQUANTIZE has changed"); +static_assert(ANEURALNETWORKS_EMBEDDING_LOOKUP == 7, + "ANEURALNETWORKS_EMBEDDING_LOOKUP has changed"); +static_assert(ANEURALNETWORKS_FLOOR == 8, "ANEURALNETWORKS_FLOOR has changed"); +static_assert(ANEURALNETWORKS_FULLY_CONNECTED == 9, "ANEURALNETWORKS_FULLY_CONNECTED has changed"); +static_assert(ANEURALNETWORKS_HASHTABLE_LOOKUP == 10, + "ANEURALNETWORKS_HASHTABLE_LOOKUP has changed"); +static_assert(ANEURALNETWORKS_L2_NORMALIZATION == 11, + "ANEURALNETWORKS_L2_NORMALIZATION has changed"); +static_assert(ANEURALNETWORKS_L2_POOL_2D == 12, "ANEURALNETWORKS_L2_POOL has changed"); +static_assert(ANEURALNETWORKS_LOCAL_RESPONSE_NORMALIZATION == 13, + "ANEURALNETWORKS_LOCAL_RESPONSE_NORMALIZATION has changed"); +static_assert(ANEURALNETWORKS_LOGISTIC == 14, "ANEURALNETWORKS_LOGISTIC has changed"); +static_assert(ANEURALNETWORKS_LSH_PROJECTION == 15, "ANEURALNETWORKS_LSH_PROJECTION has changed"); +static_assert(ANEURALNETWORKS_LSTM == 16, "ANEURALNETWORKS_LSTM has changed"); +static_assert(ANEURALNETWORKS_MAX_POOL_2D == 17, "ANEURALNETWORKS_MAX_POOL has changed"); +static_assert(ANEURALNETWORKS_MUL == 18, "ANEURALNETWORKS_MUL has changed"); +static_assert(ANEURALNETWORKS_RELU == 19, "ANEURALNETWORKS_RELU has changed"); +static_assert(ANEURALNETWORKS_RELU1 == 20, "ANEURALNETWORKS_RELU1 has changed"); +static_assert(ANEURALNETWORKS_RELU6 == 21, "ANEURALNETWORKS_RELU6 has changed"); +static_assert(ANEURALNETWORKS_RESHAPE == 22, "ANEURALNETWORKS_RESHAPE has changed"); +static_assert(ANEURALNETWORKS_RESIZE_BILINEAR == 23, "ANEURALNETWORKS_RESIZE_BILINEAR has changed"); +static_assert(ANEURALNETWORKS_RNN == 24, "ANEURALNETWORKS_RNN has changed"); +static_assert(ANEURALNETWORKS_SOFTMAX == 25, "ANEURALNETWORKS_SOFTMAX has changed"); +static_assert(ANEURALNETWORKS_SPACE_TO_DEPTH == 26, "ANEURALNETWORKS_SPACE_TO_DEPTH has changed"); +static_assert(ANEURALNETWORKS_SVDF == 27, "ANEURALNETWORKS_SVDF has changed"); +static_assert(ANEURALNETWORKS_TANH == 28, "ANEURALNETWORKS_TANH has changed"); + +static_assert(ANEURALNETWORKS_FUSED_NONE == 0, "ANEURALNETWORKS_FUSED_NONE has changed"); +static_assert(ANEURALNETWORKS_FUSED_RELU == 1, "ANEURALNETWORKS_FUSED_RELU has changed"); +static_assert(ANEURALNETWORKS_FUSED_RELU1 == 2, "ANEURALNETWORKS_FUSED_RELU1 has changed"); +static_assert(ANEURALNETWORKS_FUSED_RELU6 == 3, "ANEURALNETWORKS_FUSED_RELU6 has changed"); + +static_assert(ANEURALNETWORKS_PREFER_LOW_POWER == 0, + "ANEURALNETWORKS_PREFER_LOW_POWER has changed"); +static_assert(ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER == 1, + "ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER has changed"); +static_assert(ANEURALNETWORKS_PREFER_SUSTAINED_SPEED == 2, + "ANEURALNETWORKS_PREFER_SUSTAINED_SPEED has changed"); + +static_assert(ANEURALNETWORKS_NO_ERROR == 0, "ANEURALNETWORKS_NO_ERROR has changed"); +static_assert(ANEURALNETWORKS_OUT_OF_MEMORY == 1, "ANEURALNETWORKS_OUT_OF_MEMORY has changed"); +static_assert(ANEURALNETWORKS_INCOMPLETE == 2, "ANEURALNETWORKS_INCOMPLETE has changed"); +static_assert(ANEURALNETWORKS_UNEXPECTED_NULL == 3, "ANEURALNETWORKS_UNEXPECTED_NULL has changed"); +static_assert(ANEURALNETWORKS_BAD_DATA == 4, "ANEURALNETWORKS_BAD_DATA has changed"); +static_assert(ANEURALNETWORKS_OP_FAILED == 5, "ANEURALNETWORKS_OP_FAILED has changed"); +static_assert(ANEURALNETWORKS_BAD_STATE == 6, "ANEURALNETWORKS_BAD_STATE has changed"); + +static_assert(ANEURALNETWORKS_MAX_SIZE_OF_IMMEDIATELY_COPIED_VALUES == 128, + "ANEURALNETWORKS_MAX_SIZE_OF_IMMEDIATELY_COPIED_VALUES has changed"); diff --git a/contrib/ann/runtimes/ref/src/Request.h b/contrib/ann/runtimes/ref/src/Request.h new file mode 100644 index 0000000..49f74fd --- /dev/null +++ b/contrib/ann/runtimes/ref/src/Request.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __REQUEST_H__ +#define __REQUEST_H__ + +#include +#include + +struct RequestArgument final { + bool hasNoValue; + DataLocation location; + std::vector dimensions; +}; + +struct Request final { + std::vector inputs; + std::vector outputs; +}; + +#endif // __REQUEST_H__ diff --git a/contrib/ann/runtimes/ref/src/Shape.cpp b/contrib/ann/runtimes/ref/src/Shape.cpp new file mode 100644 index 0000000..37a54c2 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/Shape.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "Shape.h" + +#include // For 'size_t' + +bool SameShape(const Shape &in1, const Shape &in2) +{ + if (in1.type != in2.type || in1.dimensions.size() != in2.dimensions.size()) + { + return false; + } + for (size_t i = 0; i < in1.dimensions.size(); i++) + { + if (in1.dimensions[i] != in2.dimensions[i]) + { + return false; + } + } + return true; +} + +bool SetShape(const Shape &in, Shape *out) +{ + if (in.type != out->type || in.dimensions.size() != out->dimensions.size()) + { + return false; + } + out->dimensions = in.dimensions; + return true; +} + +uint32_t getNumberOfElements(const Shape &shape) +{ + uint32_t count = 1; + for (size_t i = 0; i < shape.dimensions.size(); i++) + { + count *= shape.dimensions[i]; + } + return count; +} + +uint32_t getNumberOfDimensions(const Shape &shape) { return shape.dimensions.size(); } + +uint32_t getSizeOfDimension(const Shape &shape, uint32_t dimensionIdx) +{ + if (dimensionIdx >= shape.dimensions.size()) + { + // TODO, log the error + return 0; + } + return shape.dimensions[dimensionIdx]; +} diff --git a/contrib/ann/runtimes/ref/src/Shape.h b/contrib/ann/runtimes/ref/src/Shape.h new file mode 100644 index 0000000..2e3d92e --- /dev/null +++ b/contrib/ann/runtimes/ref/src/Shape.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __SHAPE_H__ +#define __SHAPE_H__ + +#include "OperandType.h" + +#include +#include + +// The type and dimensions of an operand. +struct Shape +{ + OperandType type; + std::vector dimensions; + float scale; + int32_t offset; +}; + +// Verifies that the two shapes are the same. +bool SameShape(const Shape &in1, const Shape &in2); + +// Sets out to the same shape as in. +bool SetShape(const Shape &in, Shape *out); + +// Return the total number of elements, i.e. all the dimensions multiplied +// together. For a scalar, returns one. +uint32_t getNumberOfElements(const Shape &shape); +uint32_t getNumberOfDimensions(const Shape &shape); +uint32_t getSizeOfDimension(const Shape &shape, uint32_t dimensionIdx); + +#endif // __SHAPE_H__ diff --git a/contrib/ann/runtimes/ref/src/Validation.cpp b/contrib/ann/runtimes/ref/src/Validation.cpp new file mode 100644 index 0000000..679b14a --- /dev/null +++ b/contrib/ann/runtimes/ref/src/Validation.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "Validation.h" +#include "Macro.h" +#include "Assert.h" + +static inline bool validCode(uint32_t codeCount, uint32_t code) +{ + return (code < codeCount); +} + +int validateOperationType(const OperationType &type) +{ + return validCode(kNumberOfOperationTypes, static_cast(type)); +} + +// Validates the type. The used dimensions can be underspecified. +int validateOperandType(const ANeuralNetworksOperandType &type, const char *tag, bool allowPartial) +{ + if (!allowPartial) + { + for (uint32_t i = 0; i < type.dimensionCount; i++) + { + if (type.dimensions[i] == 0) + { + LOG(ERROR) << tag << " OperandType invalid dimensions[" << i + << "] = " << type.dimensions[i]; + return ANEURALNETWORKS_BAD_DATA; + } + } + } + if (!validCode(kNumberOfDataTypes, type.type)) + { + LOG(ERROR) << tag << " OperandType invalid type " << type.type; + return ANEURALNETWORKS_BAD_DATA; + } + if (type.type == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) + { + if (type.zeroPoint < 0 || type.zeroPoint > 255) + { + LOG(ERROR) << tag << " OperandType invalid zeroPoint " << type.zeroPoint; + return ANEURALNETWORKS_BAD_DATA; + } + if (type.scale < 0.f) + { + LOG(ERROR) << tag << " OperandType invalid scale " << type.scale; + return ANEURALNETWORKS_BAD_DATA; + } + } + + // TODO-NNRT : add 'type.type == ANEURALNETWORKS_OEM_SCALAR' later. + // OEM operaters are not supported now. + if (type.type == ANEURALNETWORKS_FLOAT32 || type.type == ANEURALNETWORKS_INT32 || + type.type == ANEURALNETWORKS_UINT32) + { + if (type.dimensionCount != 0 || type.dimensions != nullptr) + { + LOG(ERROR) << tag << " Invalid dimensions for scalar type"; + return ANEURALNETWORKS_BAD_DATA; + } + } + + return ANEURALNETWORKS_NO_ERROR; +} + +int validateOperandList(uint32_t count, const uint32_t *list, uint32_t operandCount, + const char *tag) +{ + for (uint32_t i = 0; i < count; i++) + { + if (list[i] >= operandCount) + { + LOG(ERROR) << tag << " invalid operand index at " << i << " = " << list[i] + << ", operandCount " << operandCount; + return ANEURALNETWORKS_BAD_DATA; + } + } + return ANEURALNETWORKS_NO_ERROR; +} + +static bool validOperandIndexes(const std::vector indexes, size_t operandCount) +{ + for (uint32_t i : indexes) + { + if (i >= operandCount) + { + LOG(ERROR) << "Index out of range " << i << "/" << operandCount; + return false; + } + } + return true; +} + +static bool validOperands(const std::vector &operands, const std::vector &operandValues) +{ + for (auto &operand : operands) + { + if (!validCode(kNumberOfDataTypes, static_cast(operand.type))) + { + LOG(ERROR) << "Invalid operand type "; + return false; + } + /* TODO validate dim with type + if (!validOperandIndexes(operand.dimensions, mDimensions)) { + return false; + } + */ + switch (operand.lifetime) + { + case OperandLifeTime::CONSTANT_COPY: + if (operand.location.offset + operand.location.length > operandValues.size()) + { + LOG(ERROR) << "OperandValue location out of range. Starts at " << operand.location.offset + << ", length " << operand.location.length << ", max " << operandValues.size(); + return false; + } + break; + case OperandLifeTime::TEMPORARY_VARIABLE: + case OperandLifeTime::MODEL_INPUT: + case OperandLifeTime::MODEL_OUTPUT: + case OperandLifeTime::NO_VALUE: + if (operand.location.offset != 0 || operand.location.length != 0) + { + LOG(ERROR) << "Unexpected offset " << operand.location.offset << " or length " + << operand.location.length << " for runtime location."; + return false; + } + break; + case OperandLifeTime::CONSTANT_REFERENCE: +#if 0 + if (operand.location.poolIndex >= poolCount) + { + LOG(ERROR) << "Invalid poolIndex " << operand.location.poolIndex << "/" << poolCount; + return false; + } +#endif + break; + // TODO: Validate that we are within the pool. + default: + LOG(ERROR) << "Invalid lifetime"; + return false; + } + } + return true; +} + +static bool validOperations(const std::vector &operations, size_t operandCount) +{ + for (auto &op : operations) + { + if (!validCode(kNumberOfOperationTypes, static_cast(op.type))) + { + LOG(ERROR) << "Invalid operation type "; + return false; + } + if (!validOperandIndexes(op.inputs, operandCount) || + !validOperandIndexes(op.outputs, operandCount)) + { + return false; + } + } + return true; +} + +// TODO doublecheck +bool validateModel(const Model &model) +{ + const size_t operandCount = model.operands.size(); + return (validOperands(model.operands, model.operandValues) && + validOperations(model.operations, operandCount) && + validOperandIndexes(model.inputIndexes, operandCount) && + validOperandIndexes(model.outputIndexes, operandCount)); +} + +bool validRequestArguments(const std::vector &arguments, + const std::vector &operandIndexes, + const std::vector &operands, size_t poolCount, const char *type) +{ + const size_t argumentCount = arguments.size(); + if (argumentCount != operandIndexes.size()) + { + LOG(ERROR) << "Request specifies " << argumentCount << " " << type << "s but the model has " + << operandIndexes.size(); + return false; + } + for (size_t argumentIndex = 0; argumentIndex < argumentCount; argumentIndex++) + { + const RequestArgument &argument = arguments[argumentIndex]; + const uint32_t operandIndex = operandIndexes[argumentIndex]; + const Operand &operand = operands[operandIndex]; + if (argument.hasNoValue) + { + if (argument.location.poolIndex != 0 || argument.location.offset != 0 || + argument.location.length != 0 || argument.dimensions.size() != 0) + { + LOG(ERROR) << "Request " << type << " " << argumentIndex + << " has no value yet has details."; + return false; + } + } + if (argument.location.poolIndex >= poolCount) + { + LOG(ERROR) << "Request " << type << " " << argumentIndex << " has an invalid poolIndex " + << argument.location.poolIndex << "/" << poolCount; + return false; + } + // TODO: Validate that we are within the pool. + uint32_t rank = argument.dimensions.size(); + if (rank > 0) + { + if (rank != operand.dimensions.size()) + { + LOG(ERROR) << "Request " << type << " " << argumentIndex << " has number of dimensions (" + << rank << ") different than the model's (" << operand.dimensions.size() << ")"; + return false; + } + for (size_t i = 0; i < rank; i++) + { + if (argument.dimensions[i] != operand.dimensions[i] && operand.dimensions[i] != 0) + { + LOG(ERROR) << "Request " << type << " " << argumentIndex << " has dimension " << i + << " of " << operand.dimensions[i] << " different than the model's " + << operand.dimensions[i]; + return false; + } + if (argument.dimensions[i] == 0) + { + LOG(ERROR) << "Request " << type << " " << argumentIndex << " has dimension " << i + << " of zero"; + return false; + } + } + } + } + return true; +} + +// TODO doublecheck +bool validateRequest(const Request &request, const Model &model) +{ + //const size_t poolCount = request.pools.size(); + const size_t poolCount = 0; + return (validRequestArguments(request.inputs, model.inputIndexes, model.operands, poolCount, + "input") && + validRequestArguments(request.outputs, model.outputIndexes, model.operands, poolCount, + "output")); +} + diff --git a/contrib/ann/runtimes/ref/src/Validation.h b/contrib/ann/runtimes/ref/src/Validation.h new file mode 100644 index 0000000..dab426a --- /dev/null +++ b/contrib/ann/runtimes/ref/src/Validation.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __VALIDATION_H__ +#define __VALIDATION_H__ + +#include "OperationType.h" +#include "Model.h" +#include "Request.h" +#include "NeuralNetworks.h" + +int validateOperationType(const OperationType &); +int validateOperandType(const ANeuralNetworksOperandType &type, const char *tag, bool allowPartial); +int validateOperandList(uint32_t count, const uint32_t *list, uint32_t operandCount, + const char *tag); + +bool validateModel(const Model &model); +bool validateRequest(const Request &request, const Model &model); + +#endif // __VALIDATION_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/Add.cpp b/contrib/ann/runtimes/ref/src/ops/Add.cpp new file mode 100644 index 0000000..0b826f0 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/Add.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "Add.h" +#include "Assert.h" + +bool addPrepare(const Shape &in1, const Shape &in2, Shape *out) +{ + ASSERT(getNumberOfDimensions(in1) <= 4 && getNumberOfDimensions(in2) <= 4); + ASSERT(in1.type == in2.type); + if (SameShape(in1, in2)) + { + return SetShape(in1, out); + } + else + { + // BroadcastAdd needed + uint32_t numberOfDims1 = getNumberOfDimensions(in1); + uint32_t numberOfDims2 = getNumberOfDimensions(in2); + uint32_t maxDims = std::max(numberOfDims1, numberOfDims2); + out->dimensions = std::vector(maxDims); + for (uint32_t i = 1; i <= maxDims; i++) + { + uint32_t dim1 = 1; + if (i <= numberOfDims1) + { + dim1 = getSizeOfDimension(in1, numberOfDims1 - i); + } + uint32_t dim2 = 1; + if (i <= numberOfDims2) + { + dim2 = getSizeOfDimension(in2, numberOfDims2 - i); + } + if (dim1 != dim2 && dim1 != 1 && dim2 != 1) + { + LOG(ERROR) << "Dimensions mismatch for BroadcastAdd"; + return false; + } + out->dimensions[maxDims - i] = std::max(dim1, dim2); + } + } + return true; +} diff --git a/contrib/ann/runtimes/ref/src/ops/Add.float.cpp b/contrib/ann/runtimes/ref/src/ops/Add.float.cpp new file mode 100644 index 0000000..c24c258 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/Add.float.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "Add.float.h" + +#include "internal/Array.h" +#include "internal/NDArray.h" +#include "internal/Matrix.h" +#include "internal/Fused.h" +#include "internal/ActivationUtils.h" + +template +void Add(const float *input1_data, const Dims<4> &input1_dims, const float *input2_data, + const Dims<4> &input2_dims, float *output_data, const Dims<4> &output_dims) +{ + /* const int batches = */ MatchingArraySize(input1_dims, 3, input2_dims, 3, output_dims, 3); + /* const int height = */ MatchingArraySize(input1_dims, 2, input2_dims, 2, output_dims, 2); + /* const int width = */ MatchingArraySize(input1_dims, 1, input2_dims, 1, output_dims, 1); + /* const int depth = */ MatchingArraySize(input1_dims, 0, input2_dims, 0, output_dims, 0); + DCHECK(IsPackedWithoutStrides(input1_dims)); + DCHECK(IsPackedWithoutStrides(input2_dims)); + DCHECK(IsPackedWithoutStrides(output_dims)); + + int i = 0; + const int size = input1_dims.sizes[3] * input1_dims.strides[3]; + + for (; i < size; i++) + { + auto x = input1_data[i] + input2_data[i]; + output_data[i] = ActivationFunction(x); + } +} + +// TODO: We can implement BroadcastAdd on buffers of arbitrary +// dimensionality if the runtime code does a single loop over one dimension +// that handles broadcasting as the base case. The code generator would then +// generate max(D1, D2) nested for loops. +// TODO: BroadcastAdd is intentionally duplicated from +// reference_ops.h. Once an optimized version is implemented and NdArrayDesc +// is no longer referenced in this file, move NdArrayDesc from types.h to +// reference_ops.h. +template +void BroadcastAdd(const float *input1_data, const Dims<4> &input1_dims, const float *input2_data, + const Dims<4> &input2_dims, float *output_data, const Dims<4> &output_dims) +{ + NdArrayDesc<4> desc1; + NdArrayDesc<4> desc2; + NdArrayDescsForElementwiseBroadcast(input1_dims, input2_dims, &desc1, &desc2); + + // In Tensorflow, the dimensions are canonically named (batch_number, row, + // col, channel), with extents (batches, height, width, depth), with the + // trailing dimension changing most rapidly (channels has the smallest stride, + // typically 1 element). + // + // In generated C code, we store arrays with the dimensions reversed. The + // first dimension has smallest stride. + // + // We name our variables by their Tensorflow convention, but generate C code + // nesting loops such that the innermost loop has the smallest stride for the + // best cache behavior. + for (int b = 0; b < ArraySize(output_dims, 3); ++b) + { + for (int y = 0; y < ArraySize(output_dims, 2); ++y) + { + for (int x = 0; x < ArraySize(output_dims, 1); ++x) + { + for (int c = 0; c < ArraySize(output_dims, 0); ++c) + { + output_data[Offset(output_dims, c, x, y, b)] = + ActivationFunction(input1_data[SubscriptToIndex(desc1, c, x, y, b)] + + input2_data[SubscriptToIndex(desc2, c, x, y, b)]); + } + } + } + } +} + +bool addFloat32(const float *in1, const Shape &shape1, const float *in2, const Shape &shape2, + int32_t activation, float *out, const Shape &shapeOut) +{ + bool needBroadcast = !SameShape(shape1, shape2); + +#define ANDROID_NN_NORMAL_ADD(activation) \ + Add(in1, convertShapeToDims(shape1), \ + in2, convertShapeToDims(shape2), \ + out, convertShapeToDims(shapeOut)) + +#define ANDROID_NN_BROADCAST_ADD(activation) \ + BroadcastAdd( \ + in1, convertShapeToDims(shape1), in2, convertShapeToDims(shape2), out, \ + convertShapeToDims(shapeOut)) + + if (needBroadcast) + { + ANDROID_NN_MACRO_DISPATCH(ANDROID_NN_BROADCAST_ADD) + } + else + { + ANDROID_NN_MACRO_DISPATCH(ANDROID_NN_NORMAL_ADD) + } + +#undef ANDROID_NN_NORMAL_ADD +#undef ANDROID_NN_BROADCAST_ADD + return true; +} diff --git a/contrib/ann/runtimes/ref/src/ops/Add.float.h b/contrib/ann/runtimes/ref/src/ops/Add.float.h new file mode 100644 index 0000000..3657a04 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/Add.float.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __OP_ADD_FLOAT_H__ +#define __OP_ADD_FLOAT_H__ + +#include "Shape.h" + +#include + +bool addFloat32(const float *in1, const Shape &shape1, const float *in2, const Shape &shape2, + int32_t activation, float *out, const Shape &shapeOut); + +#endif // __OP_ADD_FLOAT_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/Add.h b/contrib/ann/runtimes/ref/src/ops/Add.h new file mode 100644 index 0000000..c6751fc --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/Add.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __OP_ADD_H__ +#define __OP_ADD_H__ + +#include "Shape.h" + +bool addPrepare(const Shape &in1, const Shape &in2, Shape *out1); + +#endif // __OP_ADD_FLOAT_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/AvgPool2D.cpp b/contrib/ann/runtimes/ref/src/ops/AvgPool2D.cpp new file mode 100644 index 0000000..cd9fcff --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/AvgPool2D.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "AvgPool2D.h" + +#include "internal/Pooling.h" + +bool averagePoolPrepare(const Shape &input, int32_t padding_left, int32_t padding_right, + int32_t padding_top, int32_t padding_bottom, int32_t stride_width, + int32_t stride_height, int32_t filter_width, int32_t filter_height, + Shape *output) +{ + return genericPoolingPrepare(input, padding_left, padding_right, padding_top, padding_bottom, + stride_width, stride_height, filter_width, filter_height, + output); +} diff --git a/contrib/ann/runtimes/ref/src/ops/AvgPool2D.float.cpp b/contrib/ann/runtimes/ref/src/ops/AvgPool2D.float.cpp new file mode 100644 index 0000000..c5ef63a --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/AvgPool2D.float.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "AvgPool2D.float.h" + +#include "internal/Array.h" +#include "internal/Matrix.h" +#include "internal/FeatureMap.h" +#include "internal/Fused.h" +#include "internal/ActivationUtils.h" + +template +void AveragePool(const float *input_data, const Dims<4> &input_dims, int stride_width, + int stride_height, int pad_width, int pad_height, int kwidth, int kheight, + float *output_data, const Dims<4> &output_dims) +{ + const int batches = MatchingArraySize(input_dims, 3, output_dims, 3); + const int input_height = ArraySize(input_dims, 2); + const int input_width = ArraySize(input_dims, 1); + const int output_height = ArraySize(output_dims, 2); + const int output_width = ArraySize(output_dims, 1); + const int depth = MatchingArraySize(input_dims, 0, output_dims, 0); + + const auto in_mat = MapAsMatrixWithFirstDimAsRows(input_data, input_dims); + auto out_mat = MapAsMatrixWithFirstDimAsRows(output_data, output_dims); + // TODO: get rid of the dynamic memory allocation here! + Eigen::VectorXf out_count(out_mat.cols()); + out_count.setZero(); + // Prefill the output to 0. + out_mat.setZero(); + for (int b = 0; b < batches; ++b) + { + for (int h = 0; h < input_height; ++h) + { + for (int w = 0; w < input_width; ++w) + { + // (h_start, h_end) * (w_start, w_end) is the range that the input + // vector projects to. + int hpad = h + pad_height; + int wpad = w + pad_width; + int h_start = (hpad < kheight) ? 0 : (hpad - kheight) / stride_height + 1; + int h_end = std::min(hpad / stride_height + 1, output_height); + int w_start = (wpad < kwidth) ? 0 : (wpad - kwidth) / stride_width + 1; + int w_end = std::min(wpad / stride_width + 1, output_width); + // compute elementwise sum + for (int ph = h_start; ph < h_end; ++ph) + { + for (int pw = w_start; pw < w_end; ++pw) + { + int out_offset = NodeOffset(b, ph, pw, output_height, output_width); + out_mat.col(out_offset) += in_mat.col(NodeOffset(b, h, w, input_height, input_width)); + out_count(out_offset)++; + } + } + } + } + } + // Divide the output by the actual number of elements being averaged over + DCHECK_GT(out_count.minCoeff(), 0); + out_mat.array().rowwise() /= out_count.transpose().array(); + + for (int b = 0; b < batches; ++b) + { + for (int y = 0; y < output_height; ++y) + { + for (int x = 0; x < output_width; ++x) + { + for (int c = 0; c < depth; ++c) + { + output_data[Offset(output_dims, c, x, y, b)] = + ActivationFunction(output_data[Offset(output_dims, c, x, y, b)]); + } + } + } + } +} + +#define ANDROID_NN_POOLING_PARAMETERS \ + uint32_t height = getSizeOfDimension(inputShape, 1); \ + uint32_t width = getSizeOfDimension(inputShape, 2); \ + uint32_t outHeight = getSizeOfDimension(outputShape, 1); \ + uint32_t outWidth = getSizeOfDimension(outputShape, 2); \ + \ + uint32_t paddingHeight = (uint32_t)padding_top; \ + uint32_t paddingWidth = (uint32_t)padding_left; + +bool averagePoolFloat32(const float *inputData, const Shape &inputShape, int32_t padding_left, + int32_t padding_right, int32_t padding_top, int32_t padding_bottom, + int32_t stride_width, int32_t stride_height, int32_t filter_width, + int32_t filter_height, int32_t activation, float *outputData, + const Shape &outputShape) +{ + + ANDROID_NN_POOLING_PARAMETERS + +#define ANDROID_NN_AVERAGE_POOL(activation) \ + AveragePool( \ + inputData, convertShapeToDims(inputShape), stride_width, stride_height, paddingWidth, \ + paddingHeight, filter_width, filter_height, outputData, convertShapeToDims(outputShape)) + + ANDROID_NN_MACRO_DISPATCH(ANDROID_NN_AVERAGE_POOL) +#undef ANDROID_NN_AVERAGE_POOL + + return true; +} + +#undef ANDROID_NN_POOLING_PARAMETERS diff --git a/contrib/ann/runtimes/ref/src/ops/AvgPool2D.float.h b/contrib/ann/runtimes/ref/src/ops/AvgPool2D.float.h new file mode 100644 index 0000000..b980e00 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/AvgPool2D.float.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __OP_AVG_POOL_2D_FLOAT_H__ +#define __OP_AVG_POOL_2D_FLOAT_H__ + +#include "Shape.h" + +#include + +bool averagePoolFloat32(const float *inputData, const Shape &inputShape, int32_t padding_left, + int32_t padding_right, int32_t padding_top, int32_t padding_bottom, + int32_t stride_width, int32_t stride_height, int32_t filter_width, + int32_t filter_height, int32_t activation, float *outputData, + const Shape &outputShape); + +#endif // __OP_AVG_POOL_2D_FLOAT_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/AvgPool2D.h b/contrib/ann/runtimes/ref/src/ops/AvgPool2D.h new file mode 100644 index 0000000..c863855 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/AvgPool2D.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __OP_AVG_POOL_2D_H__ +#define __OP_AVG_POOL_2D_H__ + +#include "Shape.h" + +#include + +bool averagePoolPrepare(const Shape &input, int32_t padding_left, int32_t padding_right, + int32_t padding_top, int32_t padding_bottom, int32_t stride_width, + int32_t stride_height, int32_t filter_width, int32_t filter_height, + Shape *output); + +#endif // __OP_AVG_POOL_2D_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/Concatenation.cpp b/contrib/ann/runtimes/ref/src/ops/Concatenation.cpp new file mode 100644 index 0000000..6bfe640 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/Concatenation.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "Concatenation.h" +#include "Assert.h" + +bool concatenationPrepare(const std::vector &inputShapes, int32_t axis, Shape *output) +{ + + int num_inputs = inputShapes.size(); + OperandType input_type = inputShapes[0].type; + uint32_t num_dimensions = getNumberOfDimensions(inputShapes[0]); + + ASSERT(axis >= 0); + ASSERT(axis < (int32_t)num_dimensions); + + int sum_axis = getSizeOfDimension(inputShapes[0], axis); + for (int i = 1; i < num_inputs; ++i) + { + ASSERT(getNumberOfDimensions(inputShapes[i]) == num_dimensions); + ASSERT(inputShapes[i].type == inputShapes[0].type); + if (input_type == OperandType::TENSOR_QUANT8_ASYMM) + { + ASSERT(inputShapes[0].offset == inputShapes[i].offset); + ASSERT(inputShapes[0].scale == inputShapes[i].scale); + } + for (int d = 0; d < (int32_t)num_dimensions; ++d) + { + if (d == axis) + { + sum_axis += getSizeOfDimension(inputShapes[i], axis); + } + else + { + ASSERT(getSizeOfDimension(inputShapes[0], d) == + getSizeOfDimension(inputShapes[i], d)); + } + } + } + + output->type = input_type; + output->dimensions = inputShapes[0].dimensions; + output->dimensions[axis] = sum_axis; + + if (input_type == OperandType::TENSOR_QUANT8_ASYMM) + { + ASSERT(inputShapes[0].offset == output->offset); + ASSERT(inputShapes[0].scale == output->scale); + } + + return true; +} diff --git a/contrib/ann/runtimes/ref/src/ops/Concatenation.float.cpp b/contrib/ann/runtimes/ref/src/ops/Concatenation.float.cpp new file mode 100644 index 0000000..a45bc1a --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/Concatenation.float.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "Concatenation.float.h" + +#include "internal/Array.h" +#include "internal/Matrix.h" +#include "internal/Fused.h" + +template +void Concatenation(int concat_dim, const Scalar *const *input_data, + const Dims<4> *const *input_dims, int inputs_count, Scalar *output_data, + const Dims<4> &output_dims) +{ + DCHECK_GT(inputs_count, 1); + int concat_size = 0; + for (int i = 0; i < inputs_count; i++) + { + for (int j = 0; j < 4; j++) + { + if (j != concat_dim) + { + MatchingArraySize(*input_dims[i], j, output_dims, j); + } + } + concat_size += ArraySize(*input_dims[i], concat_dim); + } + DCHECK_EQ(concat_size, ArraySize(output_dims, concat_dim)); + DCHECK(IsPackedWithoutStrides(output_dims)); + // for now we dont have a model with a Concatenation + // with fused activation function. + DCHECK(Ac == FusedActivationFunctionType::kNone); + int outer_size = 1; + for (int i = concat_dim + 1; i < 4; i++) + { + outer_size *= output_dims.sizes[i]; + } + Scalar *output_ptr = output_data; + for (int k = 0; k < outer_size; k++) + { + for (int i = 0; i < inputs_count; ++i) + { + const int copy_size = input_dims[i]->sizes[concat_dim] * input_dims[i]->strides[concat_dim]; + memcpy(output_ptr, input_data[i] + k * copy_size, copy_size * sizeof(Scalar)); + output_ptr += copy_size; + } + } +} + +bool concatenationFloat32(const std::vector &inputDataPtrs, + const std::vector &inputShapes, int32_t axis, float *outputData, + const Shape &outputShape) +{ + int num_inputs = inputShapes.size(); + std::vector *> inputDimsPtr(num_inputs); + std::vector> inputDims(num_inputs); + for (int i = 0; i < num_inputs; i++) + { + inputDims[i] = convertShapeToDims(inputShapes[i]); + inputDimsPtr[i] = &inputDims[i]; + } + + Concatenation( + getNumberOfDimensions(outputShape) - axis - 1, inputDataPtrs.data(), inputDimsPtr.data(), + num_inputs, outputData, convertShapeToDims(outputShape)); + + return true; +} diff --git a/contrib/ann/runtimes/ref/src/ops/Concatenation.float.h b/contrib/ann/runtimes/ref/src/ops/Concatenation.float.h new file mode 100644 index 0000000..65bca18 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/Concatenation.float.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __OP_CONCATENATION_FLOAT_H__ +#define __OP_CONCATENATION_FLOAT_H__ + +#include "Shape.h" + +#include +#include + +bool concatenationFloat32(const std::vector &inputDataPtrs, + const std::vector &inputShapes, int32_t axis, float *outputData, + const Shape &outputShape); + +#endif // __OP_CONCATENATION_FLOAT_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/Concatenation.h b/contrib/ann/runtimes/ref/src/ops/Concatenation.h new file mode 100644 index 0000000..b92071e --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/Concatenation.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __OP_CONCATENATION_H__ +#define __OP_CONCATENATION_H__ + +#include "Shape.h" + +#include +#include + +bool concatenationPrepare(const std::vector &inputShapes, int32_t axis, Shape *output); + +#endif // __OP_CONCATENATION_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/Conv2D.cpp b/contrib/ann/runtimes/ref/src/ops/Conv2D.cpp new file mode 100644 index 0000000..ef4407e --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/Conv2D.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "Conv2D.h" +#include "Assert.h" + +#include "internal/Spatial.h" + +bool convPrepare(const Shape &input, const Shape &filter, const Shape &bias, int32_t padding_left, + int32_t padding_right, int32_t padding_top, int32_t padding_bottom, + int32_t stride_width, int32_t stride_height, Shape *output) +{ + ASSERT(input.type == filter.type); + if (input.type == OperandType::TENSOR_QUANT8_ASYMM) + { + ASSERT(bias.type == OperandType::TENSOR_INT32); + } + else + { + ASSERT(input.type == bias.type); + } + ASSERT(getNumberOfDimensions(input) == 4); + ASSERT(getNumberOfDimensions(filter) == 4); + ASSERT(getNumberOfDimensions(bias) == 1); + + ASSERT(getSizeOfDimension(filter, 0) == getSizeOfDimension(bias, 0)); + ASSERT(getSizeOfDimension(filter, 3) == getSizeOfDimension(input, 3)); + + uint32_t channels_out = getSizeOfDimension(filter, 0); + uint32_t width = getSizeOfDimension(input, 2); + uint32_t height = getSizeOfDimension(input, 1); + uint32_t filterWidth = getSizeOfDimension(filter, 2); + uint32_t filterHeight = getSizeOfDimension(filter, 1); + uint32_t batches = getSizeOfDimension(input, 0); + + uint32_t outWidth = computeOutSize(width, filterWidth, stride_width, padding_left, padding_right); + uint32_t outHeight = + computeOutSize(height, filterHeight, stride_height, padding_top, padding_bottom); + + output->type = input.type; + output->dimensions = {batches, outHeight, outWidth, channels_out}; + return true; +} diff --git a/contrib/ann/runtimes/ref/src/ops/Conv2D.float.cpp b/contrib/ann/runtimes/ref/src/ops/Conv2D.float.cpp new file mode 100644 index 0000000..572f648 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/Conv2D.float.cpp @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "Conv2D.float.h" + +#include "internal/Spatial.h" +#include "internal/Array.h" +#include "internal/Matrix.h" +#include "internal/Fused.h" +#include "internal/GEMM.h" +#include "internal/ActivationUtils.h" + +template +inline void ExtractPatchIntoBufferColumn(const Dims<4> &input_dims, int w, int h, int b, + int kheight, int kwidth, int stride_width, + int stride_height, int pad_width, int pad_height, + int in_width, int in_height, int in_depth, + int single_buffer_length, int buffer_id, const T *in_data, + T *conv_buffer_data, uint8 byte_zero) +{ + // This chunk of code reshapes all the inputs corresponding to + // output (b, h, w) to a column vector in conv_buffer(:, buffer_id). + const int kwidth_times_indepth = kwidth * in_depth; + const int inwidth_times_indepth = in_width * in_depth; + const int ih_ungated_start = h * stride_height - pad_height; + const int ih_ungated_end = (ih_ungated_start + kheight); + const int ih_end = std::min(ih_ungated_end, in_height); + const int iw_ungated_start = w * stride_width - pad_width; + const int iw_ungated_end = (iw_ungated_start + kwidth); + const int iw_end = std::min(iw_ungated_end, in_width); + // If the patch is off the edge of the input image, skip writing those rows + // and columns from the patch into the output array. + const int h_offset = std::max(0, -ih_ungated_start); + const int w_offset = std::max(0, -iw_ungated_start); + const int ih_start = std::max(0, ih_ungated_start); + const int iw_start = std::max(0, iw_ungated_start); + const int single_row_num = std::min(kwidth - w_offset, in_width - iw_start) * in_depth; + const int output_row_offset = (buffer_id * single_buffer_length); + int out_offset = output_row_offset + (h_offset * kwidth + w_offset) * in_depth; + int in_offset = Offset(input_dims, 0, iw_start, ih_start, b); + + // Express all of the calculations as padding around the input patch. + const int top_padding = h_offset; + const int bottom_padding = (ih_ungated_end - ih_end); + const int left_padding = w_offset; + const int right_padding = (iw_ungated_end - iw_end); + assert(single_row_num == ((kwidth - (left_padding + right_padding)) * in_depth)); + + // Write out zeroes to the elements representing the top rows of the input + // patch that are off the edge of the input image. + if (top_padding > 0) + { + const int top_row_elements = (top_padding * kwidth * in_depth); + memset(conv_buffer_data + output_row_offset, byte_zero, (top_row_elements * sizeof(T))); + } + + // If the patch is on the interior of the input image horizontally, just copy + // over the rows sequentially, otherwise add zero padding at the start or end. + if ((left_padding == 0) && (right_padding == 0)) + { + for (int ih = ih_start; ih < ih_end; ++ih) + { + memcpy(conv_buffer_data + out_offset, in_data + in_offset, single_row_num * sizeof(T)); + out_offset += kwidth_times_indepth; + in_offset += inwidth_times_indepth; + } + } + else + { + for (int ih = ih_start; ih < ih_end; ++ih) + { + if (left_padding > 0) + { + const int left_start = (out_offset - (left_padding * in_depth)); + memset(conv_buffer_data + left_start, byte_zero, (left_padding * in_depth * sizeof(T))); + } + memcpy(conv_buffer_data + out_offset, in_data + in_offset, single_row_num * sizeof(T)); + if (right_padding > 0) + { + const int right_start = (out_offset + single_row_num); + memset(conv_buffer_data + right_start, byte_zero, (right_padding * in_depth * sizeof(T))); + } + out_offset += kwidth_times_indepth; + in_offset += inwidth_times_indepth; + } + } + + // If the bottom of the patch falls off the input image, pad the values + // representing those input rows with zeroes. + if (bottom_padding > 0) + { + const int bottom_row_elements = (bottom_padding * kwidth * in_depth); + const int bottom_start = + output_row_offset + ((top_padding + (ih_end - ih_start)) * kwidth * in_depth); + memset(conv_buffer_data + bottom_start, byte_zero, (bottom_row_elements * sizeof(T))); + } +} + +template +void Im2col(const T *input_data, const Dims<4> &input_dims, int stride_width, int stride_height, + int pad_width, int pad_height, int kheight, int kwidth, uint8 byte_zero, T *output_data, + const Dims<4> &output_dims) +{ + DCHECK(IsPackedWithoutStrides(input_dims)); + DCHECK(IsPackedWithoutStrides(output_dims)); + const int batches = MatchingArraySize(input_dims, 3, output_dims, 3); + const int input_depth = ArraySize(input_dims, 0); + const int input_width = ArraySize(input_dims, 1); + const int input_height = ArraySize(input_dims, 2); + const int output_depth = ArraySize(output_dims, 0); + const int output_width = ArraySize(output_dims, 1); + const int output_height = ArraySize(output_dims, 2); + + int buffer_id = 0; + // Loop over the output nodes. + for (int b = 0; b < batches; ++b) + { + for (int h = 0; h < output_height; ++h) + { + for (int w = 0; w < output_width; ++w) + { + ExtractPatchIntoBufferColumn(input_dims, w, h, b, kheight, kwidth, stride_width, + stride_height, pad_width, pad_height, input_width, + input_height, input_depth, output_depth, buffer_id, input_data, + output_data, byte_zero); + ++buffer_id; + } + } + } +} + +template +void Conv(const float *input_data, const Dims<4> &input_dims, const float *filter_data, + const Dims<4> &filter_dims, const float *bias_data, const Dims<4> &bias_dims, + int stride_width, int stride_height, int pad_width, int pad_height, float *output_data, + const Dims<4> &output_dims, float *im2col_data, const Dims<4> &im2col_dims) +{ + (void)im2col_data; + (void)im2col_dims; + + const float *gemm_input_data = nullptr; + const Dims<4> *gemm_input_dims = nullptr; + const int filter_width = ArraySize(filter_dims, 1); + const int filter_height = ArraySize(filter_dims, 2); + const bool need_im2col = + stride_width != 1 || stride_height != 1 || filter_width != 1 || filter_height != 1; + if (need_im2col) + { + DCHECK(im2col_data); + Im2col(input_data, input_dims, stride_width, stride_height, pad_width, pad_height, + filter_height, filter_width, 0, im2col_data, im2col_dims); + gemm_input_data = im2col_data; + gemm_input_dims = &im2col_dims; + } + else + { +#if 0 // TODO-NNRT : Check if it needs, 'im2col_data' seems to be always not null. + DCHECK(!im2col_data); +#endif + gemm_input_data = input_data; + gemm_input_dims = &input_dims; + } + + const auto im2col_matrix_map = MapAsMatrixWithFirstDimAsRows(gemm_input_data, *gemm_input_dims); + const auto filter_matrix_map = MapAsMatrixWithLastDimAsCols(filter_data, filter_dims); + auto output_matrix_map = MapAsMatrixWithFirstDimAsRows(output_data, output_dims); + + Gemm(filter_matrix_map.transpose(), im2col_matrix_map, &output_matrix_map); + + AddBiasAndEvalActivationFunction(bias_data, bias_dims, output_data, output_dims); +} + +// If possible we will use this static buffer for the tensor. +static constexpr int kStaticBufferSize = 1605632; +static char static_scratch_buffer[kStaticBufferSize]; + +#define ANDROID_NN_CONV_PARAMETERS(Type) \ + uint32_t height = getSizeOfDimension(inputShape, 1); \ + uint32_t width = getSizeOfDimension(inputShape, 2); \ + uint32_t filterHeight = getSizeOfDimension(filterShape, 1); \ + uint32_t filterWidth = getSizeOfDimension(filterShape, 2); \ + uint32_t outHeight = getSizeOfDimension(outputShape, 1); \ + uint32_t outWidth = getSizeOfDimension(outputShape, 2); \ + uint32_t inDepth = getSizeOfDimension(inputShape, 3); \ + \ + uint32_t paddingHeight = (uint32_t)padding_top; \ + uint32_t paddingWidth = (uint32_t)padding_left; \ + \ + Dims<4> im2colDim; \ + im2colDim.sizes[3] = (int)getSizeOfDimension(outputShape, 0); \ + im2colDim.sizes[2] = (int)getSizeOfDimension(outputShape, 1); \ + im2colDim.sizes[1] = (int)getSizeOfDimension(outputShape, 2); \ + im2colDim.sizes[0] = (int)inDepth * filterHeight * filterWidth; \ + \ + im2colDim.strides[0] = 1; \ + for (int i = 1; i < 4; i++) \ + { \ + im2colDim.strides[i] = im2colDim.strides[i - 1] * im2colDim.sizes[i - 1]; \ + } \ + \ + Type *im2colData = nullptr; \ + int im2colByteSize = sizeof(Type); \ + for (int i = 0; i < 4; i++) \ + { \ + im2colByteSize *= im2colDim.sizes[i]; \ + } \ + if (im2colByteSize <= kStaticBufferSize) \ + { \ + im2colData = reinterpret_cast(static_scratch_buffer); \ + } \ + else \ + { \ + im2colData = new (std::nothrow) Type[im2colByteSize / sizeof(Type)]; \ + } + +bool convFloat32(const float *inputData, const Shape &inputShape, const float *filterData, + const Shape &filterShape, const float *biasData, const Shape &biasShape, + int32_t padding_left, int32_t padding_right, int32_t padding_top, + int32_t padding_bottom, int32_t stride_width, int32_t stride_height, + int32_t activation, float *outputData, const Shape &outputShape) +{ + + ANDROID_NN_CONV_PARAMETERS(float) + +#define ANDROID_NN_CONV(activation) \ + Conv( \ + inputData, convertShapeToDims(inputShape), filterData, convertShapeToDims(filterShape), \ + biasData, convertShapeToDims(biasShape), stride_width, stride_height, paddingWidth, \ + paddingHeight, outputData, convertShapeToDims(outputShape), im2colData, im2colDim) + + ANDROID_NN_MACRO_DISPATCH_WITH_DELETE(ANDROID_NN_CONV) +#undef ANDROID_NN_CONV + + if (im2colByteSize > kStaticBufferSize) + { + delete[] im2colData; + } + return true; +} diff --git a/contrib/ann/runtimes/ref/src/ops/Conv2D.float.h b/contrib/ann/runtimes/ref/src/ops/Conv2D.float.h new file mode 100644 index 0000000..620263f --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/Conv2D.float.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __OP_CONV_2D_FLOAT_H__ +#define __OP_CONV_2D_FLOAT_H__ + +#include "Shape.h" + +#include + +bool convFloat32(const float *inputData, const Shape &inputShape, const float *filterData, + const Shape &filterShape, const float *biasData, const Shape &biasShape, + int32_t padding_left, int32_t padding_right, int32_t padding_top, + int32_t padding_bottom, int32_t stride_width, int32_t stride_height, + int32_t activation, float *outputData, const Shape &outputShape); + +#endif // __OP_CONV_2D_FLOAT_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/Conv2D.h b/contrib/ann/runtimes/ref/src/ops/Conv2D.h new file mode 100644 index 0000000..7dc1e34 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/Conv2D.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __OP_CONV_2D_H__ +#define __OP_CONV_2D_H__ + +#include "Shape.h" + +#include + +bool convPrepare(const Shape &input, const Shape &filter, const Shape &bias, int32_t padding_left, + int32_t padding_right, int32_t padding_top, int32_t padding_bottom, + int32_t stride_width, int32_t stride_height, Shape *output); + +#endif // __OP_CONV_2D_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/DepthwiseConv2D.cpp b/contrib/ann/runtimes/ref/src/ops/DepthwiseConv2D.cpp new file mode 100644 index 0000000..4692564 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/DepthwiseConv2D.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "DepthwiseConv2D.h" +#include "Assert.h" + +#include "internal/Spatial.h" + +bool depthwiseConvPrepare(const Shape &input, const Shape &filter, const Shape &bias, + int32_t padding_left, int32_t padding_right, int32_t padding_top, + int32_t padding_bottom, int32_t stride_width, int32_t stride_height, + Shape *output) +{ + ASSERT(input.type == filter.type); + if (input.type == OperandType::TENSOR_QUANT8_ASYMM) + { + ASSERT(bias.type == OperandType::TENSOR_INT32); + } + else + { + ASSERT(input.type == bias.type); + } + ASSERT(getNumberOfDimensions(input) == 4); + ASSERT(getNumberOfDimensions(filter) == 4); + ASSERT(getNumberOfDimensions(bias) == 1); + + ASSERT(getSizeOfDimension(filter, 3) == getSizeOfDimension(bias, 0)); + + uint32_t channels_out = getSizeOfDimension(filter, 3); + uint32_t width = getSizeOfDimension(input, 2); + uint32_t height = getSizeOfDimension(input, 1); + uint32_t filterWidth = getSizeOfDimension(filter, 2); + uint32_t filterHeight = getSizeOfDimension(filter, 1); + uint32_t batches = getSizeOfDimension(input, 0); + + uint32_t outWidth = computeOutSize(width, filterWidth, stride_width, padding_left, padding_right); + uint32_t outHeight = + computeOutSize(height, filterHeight, stride_height, padding_top, padding_bottom); + + output->type = input.type; + output->dimensions = {batches, outHeight, outWidth, channels_out}; + return true; +} diff --git a/contrib/ann/runtimes/ref/src/ops/DepthwiseConv2D.float.cpp b/contrib/ann/runtimes/ref/src/ops/DepthwiseConv2D.float.cpp new file mode 100644 index 0000000..0e2a517 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/DepthwiseConv2D.float.cpp @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "DepthwiseConv2D.float.h" +#include "Assert.h" + +#include "internal/Spatial.h" +#include "internal/Array.h" +#include "internal/Fused.h" +#include "internal/ActivationUtils.h" + +#include // 'memcpy' + +namespace optimized_ops +{ + +// Implementation of float DepthwiseConv + +template +struct FloatDepthwiseConvKernel +{ +}; + +// Accumulates the effect of one row of the filter, on a segment of one row +// of the output, accessing the corresponding one row of the input. +template +void FloatDepthwiseConvAccumRow(int stride, int input_depth, int input_width, + const float *input_data, int pad_width, int depth_multiplier, + int filter_width, const float *filter_data, int out_x_buffer_start, + int out_x_buffer_end, int output_depth, float *acc_buffer) +{ + // Sanity check parameters. This is important in particular to ensure + // that we keep the number of template instantiations minimal, so we don't + // increase binary size unnecessarily. + static_assert(kFixedDepthMultiplier || !kFixedInputDepth, ""); + static_assert(kFixedInputDepth || kAllowStrided, ""); + DCHECK(stride == 1 || kAllowStrided); + if (kFixedInputDepth) + { + DCHECK_EQ(input_depth, kFixedInputDepth); + } + if (kFixedDepthMultiplier) + { + DCHECK_EQ(depth_multiplier, kFixedDepthMultiplier); + } + DCHECK_EQ(output_depth, input_depth * depth_multiplier); + const int input_ptr_increment = stride * input_depth; + const float *filter_base_ptr = filter_data; + for (int filter_x = 0; filter_x < filter_width; ++filter_x) + { + // For the current (filter_x, filter_y) point in the filter, + // compute the boundaries of the corresponding output row segment. + int out_x_loop_start_unclampled = 0; + int out_x_loop_end_unclampled = 0; + if (kAllowStrided) + { + if (stride == 2) + { + out_x_loop_start_unclampled = (pad_width - filter_x + 1) / 2; + out_x_loop_end_unclampled = (pad_width + input_width - filter_x + 1) / 2; + } + else if (stride == 4) + { + out_x_loop_start_unclampled = (pad_width - filter_x + 3) / 4; + out_x_loop_end_unclampled = (pad_width + input_width - filter_x + 3) / 4; + } + else + { + out_x_loop_start_unclampled = (pad_width - filter_x + stride - 1) / stride; + out_x_loop_end_unclampled = (pad_width + input_width - filter_x + stride - 1) / stride; + } + } + else + { + out_x_loop_start_unclampled = pad_width - filter_x; + out_x_loop_end_unclampled = pad_width + input_width - filter_x; + } + // The kernel will have to iterate on the segment of the + // output row that starts at out_x_loop_start and out_x_loop_end. + const int out_x_loop_start = std::max(out_x_buffer_start, out_x_loop_start_unclampled); + const int out_x_loop_end = std::min(out_x_buffer_end, out_x_loop_end_unclampled); + + float *acc_buffer_ptr = acc_buffer + (out_x_loop_start - out_x_buffer_start) * output_depth; + const int in_x_origin = (out_x_loop_start * stride) - pad_width + filter_x; + const float *input_ptr = input_data + in_x_origin * input_depth; + const int num_output_pixels = out_x_loop_end - out_x_loop_start; + FloatDepthwiseConvKernel::Run( + num_output_pixels, input_depth, depth_multiplier, input_ptr, input_ptr_increment, + filter_base_ptr, acc_buffer_ptr); + filter_base_ptr += output_depth; + } +} + +// generic fallback of FloatDepthwiseConvAccumRow, portable, non-templatized. +inline void FloatDepthwiseConvAccumRowGeneric(int stride, int input_depth, int input_width, + const float *input_data, int pad_width, + int depth_multiplier, int filter_width, + const float *filter_data, int out_x_buffer_start, + int out_x_buffer_end, int output_depth, + float *acc_buffer) +{ + const float *filter_base_ptr = filter_data; + for (int filter_x = 0; filter_x < filter_width; ++filter_x) + { + const int out_x_loop_start = + std::max(out_x_buffer_start, (pad_width - filter_x + stride - 1) / stride); + const int out_x_loop_end = + std::min(out_x_buffer_end, (pad_width + input_width - filter_x + stride - 1) / stride); + + float *acc_buffer_ptr = acc_buffer + (out_x_loop_start - out_x_buffer_start) * output_depth; + const int in_x_origin = (out_x_loop_start * stride) - pad_width + filter_x; + const float *input_ptr = input_data + in_x_origin * input_depth; + const int input_ptr_increment = (stride - 1) * input_depth; + for (int out_x = out_x_loop_start; out_x < out_x_loop_end; out_x++) + { + const float *filter_ptr = filter_base_ptr; + for (int ic = 0; ic < input_depth; ++ic) + { + const float input_val = *input_ptr++; + for (int m = 0; m < depth_multiplier; m++) + { + const float filter_val = *filter_ptr++; + *acc_buffer_ptr++ += filter_val * input_val; + } + } + input_ptr += input_ptr_increment; + } + filter_base_ptr += output_depth; + } +} + +// Initializes the accumulator buffer with bias values. +inline void DepthwiseConvInitAccBuffer(int num_output_pixels, int output_depth, + const float *bias_data, float *acc_buffer) +{ + for (int i = 0; i < num_output_pixels; i++) + { + memcpy(acc_buffer + i * output_depth, bias_data, sizeof(acc_buffer[0]) * output_depth); + } +} + +template +void DepthwiseConv(const float *input_data, const Dims<4> &input_dims, const float *filter_data, + const Dims<4> &filter_dims, const float *bias_data, const Dims<4> &bias_dims, + int stride_width, int stride_height, int pad_width, int pad_height, + int depth_multiplier, float *output_data, const Dims<4> &output_dims) +{ + static_assert( + Ac == FusedActivationFunctionType::kNone || Ac == FusedActivationFunctionType::kRelu || + Ac == FusedActivationFunctionType::kRelu6 || Ac == FusedActivationFunctionType::kRelu1, + ""); + const int batches = MatchingArraySize(input_dims, 3, output_dims, 3); + const int output_depth = MatchingArraySize(filter_dims, 0, output_dims, 0); + const int input_height = ArraySize(input_dims, 2); + const int input_width = ArraySize(input_dims, 1); + const int input_depth = ArraySize(input_dims, 0); + const int filter_height = ArraySize(filter_dims, 2); + const int filter_width = ArraySize(filter_dims, 1); + const int output_height = ArraySize(output_dims, 2); + const int output_width = ArraySize(output_dims, 1); +#if 0 // TODO-NNRT : Check if assertion is needed, output depth some times not equal to input * + // depthmultiplier + DCHECK(output_depth == input_depth * depth_multiplier); +#endif + + static const int kAccBufferMaxSize = 1024; + float acc_buffer[kAccBufferMaxSize]; + DCHECK_GE(kAccBufferMaxSize, output_depth); + const int kOutputPixelsInAccBuffer = kAccBufferMaxSize / output_depth; + const int kAccBufferActualSize = kOutputPixelsInAccBuffer * output_depth; + DCHECK_LE(kOutputPixelsInAccBuffer * output_depth, kAccBufferActualSize); + DCHECK_LE(kAccBufferActualSize, kAccBufferMaxSize); + DCHECK_GE(kOutputPixelsInAccBuffer, 1); + + // row_accum_func will point to the core accumulation function to be used + // for this DepthwiseConv op. + auto *row_accum_func = FloatDepthwiseConvAccumRowGeneric; + + const int kMaxFixedDepthMultiplier = 16; + int fixed_depth_multiplier = 0; + if (depth_multiplier <= kMaxFixedDepthMultiplier) + { + fixed_depth_multiplier = depth_multiplier; + } + // kMaxUnrolling is the max number of output values that we aim to handle + // in one unrolled iteration of the inner loop. For practical performance + // reasons, it is limited by the number of available registers. We could + // fine-tune it depending on the architecture, but that's not worth doing + // since this whole code is not very optimized to begin with. The + // present value reflects what's realistic on ARM 32bit NEON with 16 128-bit + // vector registers. + const int kMaxUnrolling = 8; + int fixed_input_depth = 0; + if (fixed_depth_multiplier && input_depth * fixed_depth_multiplier <= kMaxUnrolling) + { + fixed_input_depth = input_depth; + } + + // Now that we have determined row_accum_func, we can start work. + float *output_ptr = output_data; + for (int b = 0; b < batches; ++b) + { + for (int out_y = 0; out_y < output_height; ++out_y) + { + const int in_y_origin = (out_y * stride_height) - pad_height; + const int filter_y_start = std::max(0, -in_y_origin); + const int filter_y_end = std::min(filter_height, input_height - in_y_origin); + for (int out_x_buffer_start = 0; out_x_buffer_start < output_width; + out_x_buffer_start += kOutputPixelsInAccBuffer) + { + const int out_x_buffer_end = + std::min(output_width, out_x_buffer_start + kOutputPixelsInAccBuffer); + // We call a 'pixel' a group of activation that share all but the + // 'depth'/'channel' coordinate. num_output_pixels is the number of + // output pixels that we will accumulate in this loop iteration. + const int num_output_pixels = out_x_buffer_end - out_x_buffer_start; + // Initialize our local accumulator with the bias values, so we don't + // have to add them later. + DepthwiseConvInitAccBuffer(num_output_pixels, output_depth, bias_data, acc_buffer); + // Accumulation loop. Most of the time should be spent in here. + for (int filter_y = filter_y_start; filter_y < filter_y_end; ++filter_y) + { + const int in_y = in_y_origin + filter_y; + row_accum_func(stride_width, input_depth, input_width, + input_data + in_y * input_dims.strides[2] + b * input_dims.strides[3], + pad_width, depth_multiplier, filter_width, + filter_data + filter_y * filter_dims.strides[2], out_x_buffer_start, + out_x_buffer_end, output_depth, acc_buffer); + } + // Finished accumulating. Now store to destination. + const int num_output_values = output_depth * num_output_pixels; + int i = 0; + // Handle leftover values, one by one. This is very slow. + for (; i < num_output_values; i++) + { + float acc = acc_buffer[i]; + if (Ac == FusedActivationFunctionType::kRelu) + { + acc = std::max(0.f, acc); + } + else if (Ac == FusedActivationFunctionType::kRelu6) + { + acc = std::max(0.f, std::min(6.f, acc)); + } + else if (Ac == FusedActivationFunctionType::kRelu1) + { + acc = std::max(-1.f, std::min(1.f, acc)); + } + *output_ptr++ = acc; + } + } + } + } +} + +} // namespace optimized_ops + +#define ANDROID_NN_DEPTHWISE_CONV_PARAMETERS \ + uint32_t height = getSizeOfDimension(inputShape, 1); \ + uint32_t width = getSizeOfDimension(inputShape, 2); \ + uint32_t filterHeight = getSizeOfDimension(filterShape, 1); \ + uint32_t filterWidth = getSizeOfDimension(filterShape, 2); \ + uint32_t outHeight = getSizeOfDimension(outputShape, 1); \ + uint32_t outWidth = getSizeOfDimension(outputShape, 2); \ + \ + uint32_t paddingHeight = (uint32_t)padding_top; \ + uint32_t paddingWidth = (uint32_t)padding_left; + +bool depthwiseConvFloat32(const float *inputData, const Shape &inputShape, const float *filterData, + const Shape &filterShape, const float *biasData, const Shape &biasShape, + int32_t padding_left, int32_t padding_right, int32_t padding_top, + int32_t padding_bottom, int32_t stride_width, int32_t stride_height, + int32_t depth_multiplier, int32_t activation, float *outputData, + const Shape &outputShape) +{ + + ANDROID_NN_DEPTHWISE_CONV_PARAMETERS + +#define ANDROID_NN_DEPTHWISE_CONV(activation) \ + optimized_ops::DepthwiseConv( \ + inputData, convertShapeToDims(inputShape), filterData, convertShapeToDims(filterShape), \ + biasData, convertShapeToDims(biasShape), stride_width, stride_height, paddingWidth, \ + paddingHeight, depth_multiplier, outputData, convertShapeToDims(outputShape)) + + ANDROID_NN_MACRO_DISPATCH(ANDROID_NN_DEPTHWISE_CONV) +#undef ANDROID_NN_DEPTHWISE_CONV + + return true; +} diff --git a/contrib/ann/runtimes/ref/src/ops/DepthwiseConv2D.float.h b/contrib/ann/runtimes/ref/src/ops/DepthwiseConv2D.float.h new file mode 100644 index 0000000..3fbfeae --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/DepthwiseConv2D.float.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __OP_DEPTHWISE_CONV_2D_FLOAT_H__ +#define __OP_DEPTHWISE_CONV_2D_FLOAT_H__ + +#include "Shape.h" + +#include + +bool depthwiseConvFloat32(const float *inputData, const Shape &inputShape, const float *filterData, + const Shape &filterShape, const float *biasData, const Shape &biasShape, + int32_t padding_left, int32_t padding_right, int32_t padding_top, + int32_t padding_bottom, int32_t stride_width, int32_t stride_height, + int32_t depth_multiplier, int32_t activation, float *outputData, + const Shape &outputShape); + +#endif // __OP_DEPTHWISE_CONV_2D_FLOAT_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/DepthwiseConv2D.h b/contrib/ann/runtimes/ref/src/ops/DepthwiseConv2D.h new file mode 100644 index 0000000..13f5202 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/DepthwiseConv2D.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __OP_DEPTHWISE_CONV_2D_H__ +#define __OP_DEPTHWISE_CONV_2D_H__ + +#include "Shape.h" + +#include + +bool depthwiseConvPrepare(const Shape &input, const Shape &filter, const Shape &bias, + int32_t padding_left, int32_t padding_right, int32_t padding_top, + int32_t padding_bottom, int32_t stride_width, int32_t stride_height, + Shape *output); + +#endif // __OP_DEPTHWISE_CONV_2D_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/FullyConnected.cpp b/contrib/ann/runtimes/ref/src/ops/FullyConnected.cpp new file mode 100644 index 0000000..d21389e --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/FullyConnected.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "FullyConnected.h" +#include "Assert.h" + +#if 0 +#include "internal/Matrix.h" +#include "internal/Fused.h" +#include "internal/GEMM.h" +#include "internal/ActivationUtils.h" +#endif + +bool fullyConnectedPrepare(const Shape &input, const Shape &weights, const Shape &bias, + Shape *output) +{ + // Check all the parameters of tensor match within themselves and match the + // input configuration. + ASSERT(input.type == weights.type); + if (input.type == OperandType::TENSOR_QUANT8_ASYMM) + { + ASSERT(bias.type == OperandType::TENSOR_INT32); + } + else + { + ASSERT(input.type == bias.type); + } + ASSERT(getNumberOfDimensions(input) >= 2); + uint32_t input_size = getNumberOfElements(input); + uint32_t num_units = getSizeOfDimension(weights, 0); + + // modified to resolve Coverity 118949 (Apr 25, 2018) by hyunsik.yoon + // Original Code: + // uint32_t batch_size = input_size / getSizeOfDimension(weights, 1); + // + // Coverity Detection: Division by zero + // + // Code below is modified code + + uint32_t shape_size = getSizeOfDimension(weights, 1); + if (shape_size == 0) + { + return false; + } + + uint32_t batch_size = input_size / shape_size; + + ASSERT(getSizeOfDimension(bias, 0) == num_units); + ASSERT(getSizeOfDimension(weights, 1) * batch_size == input_size); + ASSERT(getNumberOfDimensions(weights) == 2); + + output->type = input.type; + output->dimensions = {batch_size, num_units}; + + return true; +} diff --git a/contrib/ann/runtimes/ref/src/ops/FullyConnected.float.cpp b/contrib/ann/runtimes/ref/src/ops/FullyConnected.float.cpp new file mode 100644 index 0000000..adce125 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/FullyConnected.float.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "FullyConnected.float.h" +#include "Assert.h" + +#include "internal/Matrix.h" +#include "internal/Fused.h" +#include "internal/GEMM.h" +#include "internal/ActivationUtils.h" + +template +void FullyConnected(const float *input_data, const Dims<4> &input_dims, const float *weights_data, + const Dims<4> &weights_dims, const float *bias_data, const Dims<4> &bias_dims, + float *output_data, const Dims<4> &output_dims) +{ + // TODO(b/62193649): this convoluted shape computation (determining + // input_rows from the weights_dims, then MapAsMatrixWithGivenNumberOfRows) + // is because the current --variable_batch hack consists in overwriting the + // 3rd dimension with the runtime batch size, as we don't keep track for each + // array of which dimension is the batch dimension in it. + // When that is fixed, this should become: + // const auto input_matrix_map = + // MapAsMatrixWithFirstDimAsRows(input_data, input_dims); + const int input_rows = ArraySize(weights_dims, 0); + const auto input_matrix_map = + MapAsMatrixWithGivenNumberOfRows(input_data, input_dims, input_rows); + const auto filter_matrix_map = MapAsMatrixWithFirstDimAsRows(weights_data, weights_dims); + auto output_matrix_map = MapAsMatrixWithFirstDimAsRows(output_data, output_dims); + + Gemm(filter_matrix_map.transpose(), input_matrix_map, &output_matrix_map); + AddBiasAndEvalActivationFunction(bias_data, bias_dims, output_data, output_dims); +} + +bool fullyConnectedFloat32(const float *inputData, const Shape &inputShape, + const float *weightsData, const Shape &weightsShape, + const float *biasData, const Shape &biasShape, int32_t activation, + float *outputData, const Shape &outputShape) +{ + +#define ANDROID_NN_FULLY_CONNECTED(activation) \ + FullyConnected( \ + inputData, convertShapeToDims(inputShape), weightsData, convertShapeToDims(weightsShape), \ + biasData, convertShapeToDims(biasShape), outputData, convertShapeToDims(outputShape)) + + ANDROID_NN_MACRO_DISPATCH(ANDROID_NN_FULLY_CONNECTED) +#undef ANDROID_NN_FULLY_CONNECTED + return true; +} diff --git a/contrib/ann/runtimes/ref/src/ops/FullyConnected.float.h b/contrib/ann/runtimes/ref/src/ops/FullyConnected.float.h new file mode 100644 index 0000000..3412fdb --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/FullyConnected.float.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __OP_FULLY_CONNECTED_FLOAT_H__ +#define __OP_FULLY_CONNECTED_FLOAT_H__ + +#include "Shape.h" + +#include + +bool fullyConnectedFloat32(const float *inputData, const Shape &inputShape, const float *weights, + const Shape &weightsShape, const float *biasData, const Shape &biasShape, + int32_t activation, float *outputData, const Shape &outputShape); + +#endif // __OP_FULLY_CONNECTED_FLOAT_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/FullyConnected.h b/contrib/ann/runtimes/ref/src/ops/FullyConnected.h new file mode 100644 index 0000000..985fd7e --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/FullyConnected.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __OP_FULLY_CONNECTED_H__ +#define __OP_FULLY_CONNECTED_H__ + +#include "Shape.h" + +#include + +bool fullyConnectedPrepare(const Shape &input, const Shape &weights, const Shape &bias, + Shape *output); + +#endif // __OP_FULLY_CONNECTED_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/MaxPool2D.cpp b/contrib/ann/runtimes/ref/src/ops/MaxPool2D.cpp new file mode 100644 index 0000000..405afbb --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/MaxPool2D.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "MaxPool2D.h" + +#include "internal/Pooling.h" + +bool maxPoolPrepare(const Shape &input, int32_t padding_left, int32_t padding_right, + int32_t padding_top, int32_t padding_bottom, int32_t stride_width, + int32_t stride_height, int32_t filter_width, int32_t filter_height, + Shape *output) +{ + return genericPoolingPrepare(input, padding_left, padding_right, padding_top, padding_bottom, + stride_width, stride_height, filter_width, filter_height, + output); +} diff --git a/contrib/ann/runtimes/ref/src/ops/MaxPool2D.float.cpp b/contrib/ann/runtimes/ref/src/ops/MaxPool2D.float.cpp new file mode 100644 index 0000000..f6c05fc --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/MaxPool2D.float.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "MaxPool2D.float.h" + +#include "internal/Array.h" +#include "internal/Matrix.h" +#include "internal/FeatureMap.h" +#include "internal/Fused.h" +#include "internal/ActivationUtils.h" + +template +void MaxPool(const float *input_data, const Dims<4> &input_dims, int stride_width, + int stride_height, int pad_width, int pad_height, int kwidth, int kheight, + float *output_data, const Dims<4> &output_dims) +{ + const int batches = MatchingArraySize(input_dims, 3, output_dims, 3); + const int input_height = ArraySize(input_dims, 2); + const int input_width = ArraySize(input_dims, 1); + const int output_height = ArraySize(output_dims, 2); + const int output_width = ArraySize(output_dims, 1); + const int depth = MatchingArraySize(input_dims, 0, output_dims, 0); + + const auto in_mat = MapAsMatrixWithFirstDimAsRows(input_data, input_dims); + auto out_mat = MapAsMatrixWithFirstDimAsRows(output_data, output_dims); + // Prefill the output to minimum representable float value + out_mat.setConstant(std::numeric_limits::lowest()); + for (int b = 0; b < batches; ++b) + { + for (int h = 0; h < input_height; ++h) + { + for (int w = 0; w < input_width; ++w) + { + // (h_start, h_end) * (w_start, w_end) is the range that the input + // vector projects to. + int hpad = h + pad_height; + int wpad = w + pad_width; + int h_start = (hpad < kheight) ? 0 : (hpad - kheight) / stride_height + 1; + int h_end = std::min(hpad / stride_height + 1, output_height); + int w_start = (wpad < kwidth) ? 0 : (wpad - kwidth) / stride_width + 1; + int w_end = std::min(wpad / stride_width + 1, output_width); + // compute elementwise sum + for (int ph = h_start; ph < h_end; ++ph) + { + for (int pw = w_start; pw < w_end; ++pw) + { + int out_offset = NodeOffset(b, ph, pw, output_height, output_width); + out_mat.col(out_offset) = + out_mat.col(out_offset) + .cwiseMax(in_mat.col(NodeOffset(b, h, w, input_height, input_width))); + } + } + } + } + } + + for (int b = 0; b < batches; ++b) + { + for (int y = 0; y < output_height; ++y) + { + for (int x = 0; x < output_width; ++x) + { + for (int c = 0; c < depth; ++c) + { + output_data[Offset(output_dims, c, x, y, b)] = + ActivationFunction(output_data[Offset(output_dims, c, x, y, b)]); + } + } + } + } +} + +#define ANDROID_NN_POOLING_PARAMETERS \ + uint32_t height = getSizeOfDimension(inputShape, 1); \ + uint32_t width = getSizeOfDimension(inputShape, 2); \ + uint32_t outHeight = getSizeOfDimension(outputShape, 1); \ + uint32_t outWidth = getSizeOfDimension(outputShape, 2); \ + \ + uint32_t paddingHeight = (uint32_t)padding_top; \ + uint32_t paddingWidth = (uint32_t)padding_left; + +bool maxPoolFloat32(const float *inputData, const Shape &inputShape, int32_t padding_left, + int32_t padding_right, int32_t padding_top, int32_t padding_bottom, + int32_t stride_width, int32_t stride_height, int32_t filter_width, + int32_t filter_height, int32_t activation, float *outputData, + const Shape &outputShape) +{ + + ANDROID_NN_POOLING_PARAMETERS + +#define ANDROID_NN_MAX_POOL(activation) \ + MaxPool( \ + inputData, convertShapeToDims(inputShape), stride_width, stride_height, paddingWidth, \ + paddingHeight, filter_width, filter_height, outputData, convertShapeToDims(outputShape)) + + ANDROID_NN_MACRO_DISPATCH(ANDROID_NN_MAX_POOL) +#undef ANDROID_NN_MAX_POOL + + return true; +} + +#undef ANDROID_NN_POOLING_PARAMETERS diff --git a/contrib/ann/runtimes/ref/src/ops/MaxPool2D.float.h b/contrib/ann/runtimes/ref/src/ops/MaxPool2D.float.h new file mode 100644 index 0000000..fd320f3 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/MaxPool2D.float.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __OP_MAX_POOL_2D_FLOAT_H__ +#define __OP_MAX_POOL_2D_FLOAT_H__ + +#include "Shape.h" + +#include + +bool maxPoolFloat32(const float *inputData, const Shape &inputShape, int32_t padding_left, + int32_t padding_right, int32_t padding_top, int32_t padding_bottom, + int32_t stride_width, int32_t stride_height, int32_t filter_width, + int32_t filter_height, int32_t activation, float *outputData, + const Shape &outputShape); + +#endif // __OP_MAX_POOL_2D_FLOAT_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/MaxPool2D.h b/contrib/ann/runtimes/ref/src/ops/MaxPool2D.h new file mode 100644 index 0000000..e15a030 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/MaxPool2D.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __OP_MAX_POOL_2D_H__ +#define __OP_MAX_POOL_2D_H__ + +#include "Shape.h" + +#include + +bool maxPoolPrepare(const Shape &input, int32_t padding_left, int32_t padding_right, + int32_t padding_top, int32_t padding_bottom, int32_t stride_width, + int32_t stride_height, int32_t filter_width, int32_t filter_height, + Shape *output); + +#endif // __OP_MAX_POOL_2D_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/Reshape.cpp b/contrib/ann/runtimes/ref/src/ops/Reshape.cpp new file mode 100644 index 0000000..a88e81a --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/Reshape.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "Reshape.h" +#include "Operand.h" +#include "Assert.h" + +#include + +bool reshapePrepare(const Shape &input, const int32_t *targetDims, const int32_t targetDimsSize, + Shape *output) +{ + // Reshape allows one of the targetDims components to have the + // special -1 value, meaning it will be calculated automatically based on the + // input. Here we calculate what that dimension should be so that the number + // of output elements in the same as the number of input elements. + int32_t numInputElements = (int32_t)getNumberOfElements(input); + + std::vector outDims(targetDimsSize); + int32_t numOutputElements = 1; + int32_t strechDim = -1; + for (int32_t i = 0; i < targetDimsSize; ++i) + { + int32_t value = targetDims[i]; + if (value == -1) + { + ASSERT(strechDim == -1); + strechDim = i; + } + else + { + numOutputElements *= value; + outDims[i] = (uint32_t)value; + } + } + if (strechDim != -1) + { + int32_t strechValue = numInputElements / numOutputElements; + outDims[strechDim] = (uint32_t)strechValue; + numOutputElements *= strechValue; + } + + ASSERT(numInputElements == numOutputElements); + + output->type = input.type; + output->dimensions = outDims; + output->offset = input.offset; + output->scale = input.scale; + + return true; +} + +bool reshapeGeneric(const void *inputData, const Shape &inputShape, void *outputData, + const Shape &outputShape) +{ + size_t count = sizeOfData(inputShape.type, inputShape.dimensions); + memcpy(outputData, inputData, count); + return true; +} diff --git a/contrib/ann/runtimes/ref/src/ops/Reshape.h b/contrib/ann/runtimes/ref/src/ops/Reshape.h new file mode 100644 index 0000000..47609ff --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/Reshape.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __OP_RESHAPE_H__ +#define __OP_RESHAPE_H__ + +#include "Shape.h" + +#include + +bool reshapePrepare(const Shape &input, const int32_t *targetDims, const int32_t targetDimsSize, + Shape *output); + +bool reshapeGeneric(const void *inputData, const Shape &inputShape, void *outputData, + const Shape &outputShape); + +#endif // __OP_RESHAPE_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/Softmax.cpp b/contrib/ann/runtimes/ref/src/ops/Softmax.cpp new file mode 100644 index 0000000..9e90446 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/Softmax.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "Softmax.h" + +#include "internal/Elementwise.h" + +bool softmaxPrepare(const Shape &input, Shape *output) +{ + return genericActivationPrepare(input, output); +} diff --git a/contrib/ann/runtimes/ref/src/ops/Softmax.float.cpp b/contrib/ann/runtimes/ref/src/ops/Softmax.float.cpp new file mode 100644 index 0000000..e6c5af4 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/Softmax.float.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "Softmax.float.h" +#include "Logging.h" + +#include "internal/Array.h" +#include "internal/Matrix.h" + +inline void Softmax(const float *input_data, const Dims<4> &input_dims, float beta, + float *output_data, const Dims<4> &output_dims) +{ + /* const int batches = */ MatchingArraySize(input_dims, 3, output_dims, 3); + /* const int height = */ MatchingArraySize(input_dims, 2, output_dims, 2); + /* const int width = */ MatchingArraySize(input_dims, 1, output_dims, 1); + /* const int depth = */ MatchingArraySize(input_dims, 0, output_dims, 0); + + const auto in_mat = MapAsMatrixWithFirstDimAsRows(input_data, input_dims); + auto out_mat = MapAsMatrixWithFirstDimAsRows(output_data, output_dims); + // Compute the exponential first, removing the max coefficient for numerical + // stability. + out_mat = (in_mat.rowwise() - in_mat.colwise().maxCoeff()).array() * beta; + // We are separating out the exp function so that exp can be vectorized. + out_mat = out_mat.array().exp(); + // Normalize to get the activations. + Eigen::Array scale = out_mat.array().colwise().sum().inverse(); + out_mat.array().rowwise() *= scale; +} + +bool softmaxFloat32(const float *inputData, const Shape &inputShape, const float beta, + float *outputData, const Shape &outputShape) +{ + Dims<4> dim; + if (getNumberOfDimensions(inputShape) == 2) + { + uint32_t batch_size = getSizeOfDimension(inputShape, 0); + uint32_t input_size = getNumberOfElements(inputShape) / batch_size; + + Shape shapeIn4D; + shapeIn4D.dimensions = {batch_size, 1, 1, input_size}; + dim = convertShapeToDims(shapeIn4D); + } + else if (getNumberOfDimensions(inputShape) == 4) + { + dim = convertShapeToDims(inputShape); + } + else + { + LOG(ERROR) << "only 2D and 4D tensors supported"; + return false; + } + + Softmax(inputData, dim, beta, outputData, dim); + return true; +} diff --git a/contrib/ann/runtimes/ref/src/ops/Softmax.float.h b/contrib/ann/runtimes/ref/src/ops/Softmax.float.h new file mode 100644 index 0000000..227b658 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/Softmax.float.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __OP_SOFTMAX_FLOAT_H__ +#define __OP_SOFTMAX_FLOAT_H__ + +#include "Shape.h" + +#include + +bool softmaxFloat32(const float *inputData, const Shape &inputShape, const float beta, + float *outputData, const Shape &outputShape); + +#endif // __OP_SOFTMAX_FLOAT_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/Softmax.h b/contrib/ann/runtimes/ref/src/ops/Softmax.h new file mode 100644 index 0000000..a1e2e9c --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/Softmax.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __OP_SOFTMAX_H__ +#define __OP_SOFTMAX_H__ + +#include "Shape.h" + +#include + +bool softmaxPrepare(const Shape &input, Shape *output); + +#endif // __OP_SOFTMAX_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/internal/ActivationUtils.h b/contrib/ann/runtimes/ref/src/ops/internal/ActivationUtils.h new file mode 100644 index 0000000..9d413c6 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/internal/ActivationUtils.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __ACTIVATION_UTILS_H__ +#define __ACTIVATION_UTILS_H__ + +#include "Logging.h" + +#define ANDROID_NN_MACRO_DISPATCH_INTERNAL(macro) \ + case (int32_t)FusedActivationFunc::NONE: \ + macro(kNone); \ + break; \ + case (int32_t)FusedActivationFunc::RELU: \ + macro(kRelu); \ + break; \ + case (int32_t)FusedActivationFunc::RELU1: \ + macro(kRelu1); \ + break; \ + case (int32_t)FusedActivationFunc::RELU6: \ + macro(kRelu6); \ + break; + +#define ANDROID_NN_MACRO_DISPATCH(macro) \ + switch (activation) \ + { \ + ANDROID_NN_MACRO_DISPATCH_INTERNAL(macro) \ + default: \ + LOG(ERROR) << "Unsupported fused activation function type"; \ + return false; \ + } + +#define ANDROID_NN_MACRO_DISPATCH_WITH_DELETE(macro) \ + switch (activation) \ + { \ + ANDROID_NN_MACRO_DISPATCH_INTERNAL(macro) \ + default: \ + LOG(ERROR) << "Unsupported fused activation function type"; \ + if (im2colByteSize > kStaticBufferSize) \ + { \ + delete[] im2colData; \ + } \ + return false; \ + } + +#endif // __ACTIVATION_UTILS_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/internal/Array.h b/contrib/ann/runtimes/ref/src/ops/internal/Array.h new file mode 100644 index 0000000..4c34e4e --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/internal/Array.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __ARRAY_H__ +#define __ARRAY_H__ + +#include "Shape.h" +#include "Dims.h" + +#include "Macro.h" + +// Get common array size, DCHECKing that they all agree. +template +int MatchingArraySize(const ArrayType1 &array1, int index1, const ArrayType2 &array2, int index2) +{ + DCHECK_EQ(ArraySize(array1, index1), ArraySize(array2, index2)); + return ArraySize(array1, index1); +} + +template +int MatchingArraySize(const ArrayType1 &array1, int index1, const ArrayType2 &array2, int index2, + Args... args) +{ + DCHECK_EQ(ArraySize(array1, index1), ArraySize(array2, index2)); + return MatchingArraySize(array1, index1, args...); +} + +#endif // __ARRAY_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/internal/Dims.h b/contrib/ann/runtimes/ref/src/ops/internal/Dims.h new file mode 100644 index 0000000..c961cf1 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/internal/Dims.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __DIMS_H__ +#define __DIMS_H__ + +#include "Shape.h" +#include "Macro.h" + +template struct Dims +{ + int sizes[N]; + int strides[N]; +}; + +inline Dims<4> convertShapeToDims(const Shape &shape) +{ + Dims<4> dims; + for (int i = 0; i < 4; i++) + { + dims.sizes[i] = 1; + } + + if (shape.dimensions.size() == 1) + { + dims.sizes[0] = (int)getSizeOfDimension(shape, 0); + } + else + { + for (int i = 0; i < 4; i++) + { + int src = (int)shape.dimensions.size() - i - 1; + if (src >= 0) + { + dims.sizes[i] = (int)getSizeOfDimension(shape, src); + } + } + } + + dims.strides[0] = 1; + for (int i = 1; i < 4; i++) + { + dims.strides[i] = dims.strides[i - 1] * dims.sizes[i - 1]; + } + return dims; +} + +inline int Offset(const Dims<4> &dims, int i0, int i1, int i2, int i3) +{ + DCHECK(i0 >= 0 && i0 < dims.sizes[0]); + DCHECK(i1 >= 0 && i1 < dims.sizes[1]); + DCHECK(i2 >= 0 && i2 < dims.sizes[2]); + DCHECK(i3 >= 0 && i3 < dims.sizes[3]); + return i0 * dims.strides[0] + i1 * dims.strides[1] + i2 * dims.strides[2] + i3 * dims.strides[3]; +} + +// Get array size, DCHECKing that the dim index is in range. +template int ArraySize(const Dims &array, int index) +{ + DCHECK(index >= 0 && index < N); + return array.sizes[index]; +} + +template inline int FlatSize(const Dims &dims) +{ + int flat_size = 1; + for (int i = 0; i < N; ++i) + { + flat_size *= dims.sizes[i]; + } + return flat_size; +} + +inline int RequiredBufferSizeForDims(const Dims<4> &dims) +{ + int max_offset = 0; + for (int i = 0; i < 4; i++) + { + max_offset += (dims.sizes[i] - 1) * dims.strides[i]; + } + return max_offset + 1; +} + +// Flat size calculation, checking that dimensions match with one or more other +// arrays. +template inline int MatchingFlatSize(const Dims &dims, const Dims &check_dims_0) +{ + for (int i = 0; i < N; ++i) + { + DCHECK_EQ(ArraySize(dims, i), ArraySize(check_dims_0, i)); + } + return FlatSize(dims); +} + +template +inline int MatchingFlatSize(const Dims &dims, const Dims &check_dims_0, + const Dims &check_dims_1) +{ + for (int i = 0; i < N; ++i) + { + DCHECK_EQ(ArraySize(dims, i), ArraySize(check_dims_0, i)); + } + return MatchingFlatSize(dims, check_dims_1); +} + +template +inline int MatchingFlatSize(const Dims &dims, const Dims &check_dims_0, + const Dims &check_dims_1, const Dims &check_dims_2) +{ + for (int i = 0; i < N; ++i) + { + DCHECK_EQ(ArraySize(dims, i), ArraySize(check_dims_0, i)); + } + return FlatSize(dims, check_dims_1, check_dims_2); +} + +template +inline int MatchingFlatSize(const Dims &dims, const Dims &check_dims_0, + const Dims &check_dims_1, const Dims &check_dims_2, + const Dims &check_dims_3) +{ + for (int i = 0; i < N; ++i) + { + DCHECK_EQ(ArraySize(dims, i), ArraySize(check_dims_0, i)); + } + return FlatSize(dims, check_dims_1, check_dims_2, check_dims_3); +} + +template bool IsPackedWithoutStrides(const Dims &dims) +{ + int expected_stride = 1; + for (int d = 0; d < N; d++) + { + if (dims.strides[d] != expected_stride) + return false; + expected_stride *= dims.sizes[d]; + } + return true; +} + +#endif // __DIMS_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/internal/Elementwise.cpp b/contrib/ann/runtimes/ref/src/ops/internal/Elementwise.cpp new file mode 100644 index 0000000..5615e30 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/internal/Elementwise.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "Elementwise.h" +#include "Assert.h" + +bool genericActivationPrepare(const Shape &input, Shape *output) +{ + ASSERT(getNumberOfDimensions(input) <= 4); + return SetShape(input, output); +} diff --git a/contrib/ann/runtimes/ref/src/ops/internal/Elementwise.h b/contrib/ann/runtimes/ref/src/ops/internal/Elementwise.h new file mode 100644 index 0000000..732f9b8 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/internal/Elementwise.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __ELEMENTWISE_H__ +#define __ELEMENTWISE_H__ + +#include "Shape.h" + +bool genericActivationPrepare(const Shape &input, Shape *output); + +#endif // __ELEMENTWISE_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/internal/FeatureMap.h b/contrib/ann/runtimes/ref/src/ops/internal/FeatureMap.h new file mode 100644 index 0000000..e4d323f --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/internal/FeatureMap.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __FEATURE_MAP_H__ +#define __FEATURE_MAP_H__ + +inline int NodeOffset(int b, int h, int w, int height, int width) +{ + return (b * height + h) * width + w; +} + +#endif // __FEATURE_MAP_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/internal/Fused.cpp b/contrib/ann/runtimes/ref/src/ops/internal/Fused.cpp new file mode 100644 index 0000000..c50b9de --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/internal/Fused.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "Fused.h" +#include "NeuralNetworks.h" + +static_assert(static_cast(FusedActivationFunc::NONE) == ANEURALNETWORKS_FUSED_NONE, + "FusedActivationFunc::NONE != ANEURALNETWORKS_FUSED_NONE"); +static_assert(static_cast(FusedActivationFunc::RELU) == ANEURALNETWORKS_FUSED_RELU, + "FusedActivationFunc::RELU != ANEURALNETWORKS_FUSED_RELU"); +static_assert(static_cast(FusedActivationFunc::RELU1) == ANEURALNETWORKS_FUSED_RELU1, + "FusedActivationFunc::RELU1 != ANEURALNETWORKS_FUSED_RELU1"); +static_assert(static_cast(FusedActivationFunc::RELU6) == ANEURALNETWORKS_FUSED_RELU6, + "FusedActivationFunc::RELU6 != ANEURALNETWORKS_FUSED_RELU6"); diff --git a/contrib/ann/runtimes/ref/src/ops/internal/Fused.h b/contrib/ann/runtimes/ref/src/ops/internal/Fused.h new file mode 100644 index 0000000..fccd72c --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/internal/Fused.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __FUSED_H__ +#define __FUSED_H__ + +#include "Dims.h" + +#include + +enum class FusedActivationFunc : int32_t { + NONE = 0, + RELU = 1, + RELU1 = 2, + RELU6 = 3, +}; + +enum class FusedActivationFunctionType +{ + kNone, + kRelu6, + kRelu1, + kRelu +}; + +template struct ActivationFunctionImpl; + +template <> struct ActivationFunctionImpl +{ + static float Eval(float x) { return x; } +}; + +template <> struct ActivationFunctionImpl +{ + static float Eval(float x) { return x < 0.f ? 0.f : x; } +}; + +template <> struct ActivationFunctionImpl +{ + static float Eval(float x) { return x > 1.f ? 1.f : x < -1.f ? -1.f : x; } +}; + +template <> struct ActivationFunctionImpl +{ + static float Eval(float x) { return x > 6.f ? 6.f : x < 0.f ? 0.f : x; } +}; + +template float ActivationFunction(float x) +{ + return ActivationFunctionImpl::Eval(x); +} + +template +void AddBiasAndEvalActivationFunction(const float *bias_data, const Dims<4> &bias_dims, + float *array_data, const Dims<4> &array_dims) +{ + const int bias_size = bias_dims.sizes[3] * bias_dims.strides[3]; + const int array_size = array_dims.sizes[3] * array_dims.strides[3]; + DCHECK_EQ((array_size % bias_size), 0); + for (int array_offset = 0; array_offset < array_size; array_offset += bias_size) + { + for (int i = 0; i < bias_size; i++) + { + array_data[array_offset + i] = + ActivationFunction(array_data[array_offset + i] + bias_data[i]); + } + } +} + +#endif // __FUSED_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/internal/GEMM.h b/contrib/ann/runtimes/ref/src/ops/internal/GEMM.h new file mode 100644 index 0000000..e94b358 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/internal/GEMM.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __GEMM_H__ +#define __GEMM_H__ + +#include "Eigen/Core" + +template +void Gemm(const Eigen::MatrixBase &lhs, const Eigen::MatrixBase &rhs, + Eigen::MatrixBase *result) +{ + if (rhs.cols() == 1) + { + result->col(0).noalias() = lhs * rhs.col(0); + } + else + { + result->noalias() = lhs * rhs; + } +} + + +#endif // __GEMM_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/internal/Macro.h b/contrib/ann/runtimes/ref/src/ops/internal/Macro.h new file mode 100644 index 0000000..d3130af --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/internal/Macro.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __COMPATIBILITY_H__ +#define __COMPATIBILITY_H__ + +#include +#include + +#ifndef DCHECK +#define DCHECK(condition) (condition) ? (void)0 : assert(false) +#endif + +#ifndef DCHECK_EQ +#define DCHECK_EQ(x, y) ((x) == (y)) ? (void)0 : assert(false) +#endif + +#ifndef DCHECK_GE +#define DCHECK_GE(x, y) ((x) >= (y)) ? (void)0 : assert(false) +#endif + +#ifndef DCHECK_GT +#define DCHECK_GT(x, y) ((x) > (y)) ? (void)0 : assert(false) +#endif + +#ifndef DCHECK_LE +#define DCHECK_LE(x, y) ((x) <= (y)) ? (void)0 : assert(false) +#endif + +#ifndef DCHECK_LT +#define DCHECK_LT(x, y) ((x) < (y)) ? (void)0 : assert(false) +#endif + +#ifndef CHECK_EQ +#define CHECK_EQ(x, y) ((x) == (y)) ? (void)0 : assert(false) +#endif + +using uint8 = std::uint8_t; +using int16 = std::int16_t; +using uint16 = std::uint16_t; +using int32 = std::int32_t; +using uint32 = std::uint32_t; + +#endif // __COMPATIBILITY_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/internal/Matrix.h b/contrib/ann/runtimes/ref/src/ops/internal/Matrix.h new file mode 100644 index 0000000..4ac0b3b --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/internal/Matrix.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __MATRIX_H__ +#define __MATRIX_H__ + +#include "Dims.h" +#include "Eigen/Core" + +// Make a local VectorMap typedef allowing to map a float array +// as a Eigen vector expression. The std::conditional here is to +// construct the suitable Eigen type for the constness of the +// data. Indeed, for const data, we need to produce +// Eigen::Map> +// and not the more straightforward +// Eigen::Map> +template +using VectorMap = typename std::conditional< + std::is_const::value, + Eigen::Map::type, Eigen::Dynamic, 1>>, + Eigen::Map>>::type; + +template VectorMap MapAsVector(Scalar *data, const Dims &dims) +{ + const int size = RequiredBufferSizeForDims(dims); + return VectorMap(data, size, 1); +} + +// Make a local VectorMap typedef allowing to map a float array +// as a Eigen matrix expression. The same explanation as for VectorMap +// above also applies here. +template +using MatrixMap = typename std::conditional< + std::is_const::value, + Eigen::Map::type, Eigen::Dynamic, + Eigen::Dynamic>>, + Eigen::Map>>::type; + +template +MatrixMap MapAsMatrixWithFirstDimAsRows(Scalar *data, const Dims &dims) +{ + const int rows = dims.sizes[0]; + int cols = 1; + for (int d = 1; d < N; d++) + { + cols *= dims.sizes[d]; + } + return MatrixMap(data, rows, cols); +} + +template +MatrixMap MapAsMatrixWithLastDimAsCols(Scalar *data, const Dims &dims) +{ + const int cols = dims.sizes[N - 1]; + int rows = 1; + for (int d = 0; d < N - 1; d++) + { + rows *= dims.sizes[d]; + } + return MatrixMap(data, rows, cols); +} + +template +using ArrayMap = typename std::conditional< + std::is_const::value, + Eigen::Map::type, Eigen::Dynamic, + Eigen::Dynamic>>, + Eigen::Map>>::type; + +template +ArrayMap MapAsArrayWithFirstDimAsRows(Scalar *data, const Dims &dims) +{ + const int rows = dims.sizes[0]; + int cols = 1; + for (int d = 1; d < N; d++) + { + cols *= dims.sizes[d]; + } + return ArrayMap(data, rows, cols); +} + +// TODO(b/62193649): this function is only needed as long +// as we have the --variable_batch hack. +template +MatrixMap MapAsMatrixWithGivenNumberOfRows(Scalar *data, const Dims &dims, int rows) +{ + int cols = 1; + bool matched_rows = false; + for (int d = 0; d < N; d++) + { + cols *= dims.sizes[d]; + if (cols == rows) + { + matched_rows = true; + cols = 1; + } + } + DCHECK(matched_rows); + return MatrixMap(data, rows, cols); +} + +#endif // __MATRIX_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/internal/NDArray.h b/contrib/ann/runtimes/ref/src/ops/internal/NDArray.h new file mode 100644 index 0000000..24f6417 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/internal/NDArray.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __ND_ARRAY_H__ +#define __ND_ARRAY_H__ + +#include "Dims.h" +#include "Macro.h" + +// DO NOT USE THIS STRUCT FOR NEW FUNCTIONALITY BEYOND IMPLEMENTING ELEMENT-WISE +// BROADCASTING. +// +// NdArrayDesc describes the shape and memory layout of an N-dimensional +// rectangular array of numbers. +// +// NdArrayDesc is basically identical to Dims defined in types.h. +// However, as Dims is to be deprecated, this class exists as an adaptor +// to enable simple unoptimized implementations of element-wise broadcasting +// operations. +template struct NdArrayDesc +{ + // The "extent" of each dimension. Indices along dimension d must be in the + // half-open interval [0, extents[d]). + int extents[N]; + + // The number of *elements* (not bytes) between consecutive indices of each + // dimension. + int strides[N]; +}; + +// DO NOT USE THIS FUNCTION FOR NEW FUNCTIONALITY BEYOND IMPLEMENTING +// ELEMENT-WISE BROADCASTING. +// +// Same as Offset(), except takes as NdArrayDesc instead of Dims. +inline int SubscriptToIndex(const NdArrayDesc<4> &desc, int i0, int i1, int i2, int i3) +{ + DCHECK(i0 >= 0 && i0 < desc.extents[0]); + DCHECK(i1 >= 0 && i1 < desc.extents[1]); + DCHECK(i2 >= 0 && i2 < desc.extents[2]); + DCHECK(i3 >= 0 && i3 < desc.extents[3]); + return i0 * desc.strides[0] + i1 * desc.strides[1] + i2 * desc.strides[2] + i3 * desc.strides[3]; +} + +// Given the dimensions of the operands for an element-wise binary broadcast, +// adjusts them so that they can be directly iterated over with simple loops. +// Returns the adjusted dims as instances of NdArrayDesc in 'desc0_out' and +// 'desc1_out'. 'desc0_out' and 'desc1_out' cannot be nullptr. +// +// This function assumes that the two input shapes are compatible up to +// broadcasting and the shorter one has already been prepended with 1s to be the +// same length. E.g., if shape0 is (1, 16, 16, 64) and shape1 is (1, 64), +// shape1 must already have been prepended to be (1, 1, 1, 64). Recall that +// Dims refer to shapes in reverse order. In this case, input0_dims will be +// (64, 16, 16, 1) and input1_dims will be (64, 1, 1, 1). +// +// When two shapes are compatible up to broadcasting, for each dimension d, +// the input extents are either equal, or one of them is 1. +// +// This function performs the following for each dimension d: +// - If the extents are equal, then do nothing since the loop that walks over +// both of the input arrays is correct. +// - Otherwise, one (and only one) of the extents must be 1. Say extent0 is 1 +// and extent1 is e1. Then set extent0 to e1 and stride0 *to 0*. This allows +// array0 to be referenced *at any index* in dimension d and still access the +// same slice. +template +inline void +NdArrayDescsForElementwiseBroadcast(const Dims &input0_dims, const Dims &input1_dims, + NdArrayDesc *desc0_out, NdArrayDesc *desc1_out) +{ + DCHECK(desc0_out != nullptr); + DCHECK(desc1_out != nullptr); + + // Copy dims to desc. + for (int i = 0; i < N; ++i) + { + desc0_out->extents[i] = input0_dims.sizes[i]; + desc0_out->strides[i] = input0_dims.strides[i]; + desc1_out->extents[i] = input1_dims.sizes[i]; + desc1_out->strides[i] = input1_dims.strides[i]; + } + + // Walk over each dimension. If the extents are equal do nothing. + // Otherwise, set the desc with extent 1 to have extent equal to the other and + // stride 0. + for (int i = 0; i < N; ++i) + { + const int extent0 = ArraySize(input0_dims, i); + const int extent1 = ArraySize(input1_dims, i); + if (extent0 != extent1) + { + if (extent0 == 1) + { + desc0_out->strides[i] = 0; + desc0_out->extents[i] = extent1; + } + else + { + DCHECK_EQ(extent1, 1); + desc1_out->strides[i] = 0; + desc1_out->extents[i] = extent0; + } + } + } +} + +inline int NodeOffset(int b, int h, int w, int height, int width) +{ + return (b * height + h) * width + w; +} + +#endif // __ND_ARRAY_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/internal/Pooling.cpp b/contrib/ann/runtimes/ref/src/ops/internal/Pooling.cpp new file mode 100644 index 0000000..a3b8cf3 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/internal/Pooling.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 "Pooling.h" +#include "Spatial.h" + +#include "Assert.h" + +bool genericPoolingPrepare(const Shape &input, int32_t padding_left, int32_t padding_right, + int32_t padding_top, int32_t padding_bottom, int32_t stride_width, + int32_t stride_height, int32_t filter_width, int32_t filter_height, + Shape *output) +{ + ASSERT(getNumberOfDimensions(input) == 4); + + uint32_t batches = getSizeOfDimension(input, 0); + uint32_t width = getSizeOfDimension(input, 2); + uint32_t height = getSizeOfDimension(input, 1); + uint32_t channels_out = getSizeOfDimension(input, 3); + + uint32_t outWidth = + computeOutSize(width, filter_width, stride_width, padding_left, padding_right); + uint32_t outHeight = + computeOutSize(height, filter_height, stride_height, padding_top, padding_bottom); + + output->type = input.type; + output->dimensions = {batches, outHeight, outWidth, channels_out}; + return true; +} diff --git a/contrib/ann/runtimes/ref/src/ops/internal/Pooling.h b/contrib/ann/runtimes/ref/src/ops/internal/Pooling.h new file mode 100644 index 0000000..c55bc16 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/internal/Pooling.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __POOLING_H__ +#define __POOLING_H__ + +#include "Shape.h" + +#include + +bool genericPoolingPrepare(const Shape &input, int32_t padding_left, int32_t padding_right, + int32_t padding_top, int32_t padding_bottom, int32_t stride_width, + int32_t stride_height, int32_t filter_width, int32_t filter_height, + Shape *output); + + +#endif // __POOLING_H__ diff --git a/contrib/ann/runtimes/ref/src/ops/internal/Spatial.h b/contrib/ann/runtimes/ref/src/ops/internal/Spatial.h new file mode 100644 index 0000000..6b8f0c1 --- /dev/null +++ b/contrib/ann/runtimes/ref/src/ops/internal/Spatial.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * 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 __SPATIAL_H__ +#define __SPATIAL_H__ + +#include + +inline uint32_t computeOutSize(uint32_t imageSize, uint32_t filterSize, uint32_t stride, + uint32_t paddingHead, uint32_t paddingTail) +{ + return (imageSize - filterSize + stride + paddingHead + paddingTail) / stride; +} + +#endif // __SPATIAL_H__ -- 2.7.4