From 3475ebe6587b41ea3409ef97ea17ddfd108c3f0e Mon Sep 17 00:00:00 2001 From: =?utf8?q?=EC=9C=A4=ED=98=84=EC=8B=9D/=EB=8F=99=EC=9E=91=EC=A0=9C?= =?utf8?q?=EC=96=B4Lab=28SR=29/Principal=20Engineer/=EC=82=BC=EC=84=B1?= =?utf8?q?=EC=A0=84=EC=9E=90?= Date: Thu, 22 Nov 2018 14:08:03 +0900 Subject: [PATCH] [enco] Splitting Frontend.cpp into Op/*.cpp and more (#2362) * [enco] Splitting Frontend.cpp into Op/*.cpp and more Through this commit, Frontend.cpp will be refactored into several files. Signed-off-by: Hyun Sik Yoon * pr fix: remove useless "tflite::" * pr fix: Split h file into h and cpp file * pr fix: remove wrong test files --- contrib/enco/frontend/tflite/src/Context.cpp | 114 +++ contrib/enco/frontend/tflite/src/Context.h | 167 +++ contrib/enco/frontend/tflite/src/Convert.cpp | 57 ++ contrib/enco/frontend/tflite/src/Convert.h | 43 + contrib/enco/frontend/tflite/src/Frontend.cpp | 1079 +------------------- contrib/enco/frontend/tflite/src/GraphBuilder.h | 39 + .../frontend/tflite/src/GraphBuilderRegistry.h | 81 ++ contrib/enco/frontend/tflite/src/IRBuilder.h | 8 +- contrib/enco/frontend/tflite/src/Op/Activation.cpp | 81 ++ contrib/enco/frontend/tflite/src/Op/Activation.h | 37 + .../enco/frontend/tflite/src/Op/AveragePool2D.cpp | 112 ++ .../enco/frontend/tflite/src/Op/AveragePool2D.h | 38 + .../enco/frontend/tflite/src/Op/Concatenation.cpp | 169 +++ .../enco/frontend/tflite/src/Op/Concatenation.h | 38 + contrib/enco/frontend/tflite/src/Op/Conv2D.cpp | 190 ++++ contrib/enco/frontend/tflite/src/Op/Conv2D.h | 38 + contrib/enco/frontend/tflite/src/Op/MaxPool2D.cpp | 109 ++ contrib/enco/frontend/tflite/src/Op/MaxPool2D.h | 38 + contrib/enco/frontend/tflite/src/Op/Padding.cpp | 80 ++ contrib/enco/frontend/tflite/src/Op/Padding.h | 38 + contrib/enco/frontend/tflite/src/Op/ReLU.cpp | 87 ++ contrib/enco/frontend/tflite/src/Op/ReLU.h | 38 + contrib/enco/frontend/tflite/src/Op/ReLU6.cpp | 87 ++ contrib/enco/frontend/tflite/src/Op/ReLU6.h | 38 + contrib/enco/frontend/tflite/src/Op/Reshape.cpp | 87 ++ contrib/enco/frontend/tflite/src/Op/Reshape.h | 38 + contrib/enco/frontend/tflite/src/TensorBags.h | 60 ++ 27 files changed, 1912 insertions(+), 1079 deletions(-) create mode 100644 contrib/enco/frontend/tflite/src/Context.cpp create mode 100644 contrib/enco/frontend/tflite/src/Context.h create mode 100644 contrib/enco/frontend/tflite/src/Convert.cpp create mode 100644 contrib/enco/frontend/tflite/src/Convert.h create mode 100644 contrib/enco/frontend/tflite/src/GraphBuilder.h create mode 100644 contrib/enco/frontend/tflite/src/GraphBuilderRegistry.h create mode 100644 contrib/enco/frontend/tflite/src/Op/Activation.cpp create mode 100644 contrib/enco/frontend/tflite/src/Op/Activation.h create mode 100644 contrib/enco/frontend/tflite/src/Op/AveragePool2D.cpp create mode 100644 contrib/enco/frontend/tflite/src/Op/AveragePool2D.h create mode 100644 contrib/enco/frontend/tflite/src/Op/Concatenation.cpp create mode 100644 contrib/enco/frontend/tflite/src/Op/Concatenation.h create mode 100644 contrib/enco/frontend/tflite/src/Op/Conv2D.cpp create mode 100644 contrib/enco/frontend/tflite/src/Op/Conv2D.h create mode 100644 contrib/enco/frontend/tflite/src/Op/MaxPool2D.cpp create mode 100644 contrib/enco/frontend/tflite/src/Op/MaxPool2D.h create mode 100644 contrib/enco/frontend/tflite/src/Op/Padding.cpp create mode 100644 contrib/enco/frontend/tflite/src/Op/Padding.h create mode 100644 contrib/enco/frontend/tflite/src/Op/ReLU.cpp create mode 100644 contrib/enco/frontend/tflite/src/Op/ReLU.h create mode 100644 contrib/enco/frontend/tflite/src/Op/ReLU6.cpp create mode 100644 contrib/enco/frontend/tflite/src/Op/ReLU6.h create mode 100644 contrib/enco/frontend/tflite/src/Op/Reshape.cpp create mode 100644 contrib/enco/frontend/tflite/src/Op/Reshape.h create mode 100644 contrib/enco/frontend/tflite/src/TensorBags.h diff --git a/contrib/enco/frontend/tflite/src/Context.cpp b/contrib/enco/frontend/tflite/src/Context.cpp new file mode 100644 index 0000000..cfdbc06 --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Context.cpp @@ -0,0 +1,114 @@ +/* + * 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 "Context.h" + +#include "Convert.h" + +#include +#include + +#include +#include + +#include +#include + +using namespace nncc::core::ADT; + +namespace tflimport +{ + +void TensorContext::prepare(const tflite::SubGraph *graph) +{ + for (uint32_t tensor_id = 0; tensor_id < graph->tensors()->size(); ++tensor_id) + { + auto const tensor_info = graph->tensors()->Get(tensor_id); + auto const tensor_name = tensor_info->name()->str(); + auto const tensor_shape = as_tensor_shape(tensor_info->shape()); + + _name_ctx[tensor_id] = tensor_name; + _shape_ctx[tensor_id] = tensor_shape; + } +} + +TflOpCodeContext::TflOpCodeContext( + const flatbuffers::Vector> *opcodes) +{ + for (const tflite::OperatorCode *opcode : *opcodes) + { + _opcodes.push_back(opcode); + } +} + +tflite::BuiltinOperator TflOpCodeContext::builtin_code(const tflite::Operator *op) const +{ + uint32_t index = op->opcode_index(); + assert(index < _opcodes.size()); + const tflite::OperatorCode *opcode = _opcodes.at(index); + return opcode->builtin_code(); +} + +std::string TflOpCodeContext::opcode_name(const tflite::Operator *op) const +{ + uint32_t index = op->opcode_index(); + assert(index < _opcodes.size()); + const tflite::OperatorCode *opcode = _opcodes.at(index); + + if (!is_valid(opcode)) + { + std::ostringstream oss; + oss << "(invalid: " << index << ")"; + return oss.str(); + } + + if (is_custom(opcode)) + { + if (!opcode->custom_code()) + return "(invalid custom)"; + + return opcode->custom_code()->c_str(); + } + + tflite::BuiltinOperator code = opcode->builtin_code(); + return EnumNameBuiltinOperator(code); +} + +bool TflOpCodeContext::is_valid(const tflite::OperatorCode *opcode) +{ + tflite::BuiltinOperator code = opcode->builtin_code(); + return (tflite::BuiltinOperator_MIN <= code && code <= tflite::BuiltinOperator_MAX); +} + +bool TflOpCodeContext::is_custom(const tflite::OperatorCode *opcode) +{ + tflite::BuiltinOperator code = opcode->builtin_code(); + return (code == tflite::BuiltinOperator_CUSTOM); +} + +TflBufferContext::TflBufferContext(const tflite::Model *tfl_model) +{ + const flatbuffers::Vector> *tfl_buffers; + + tfl_buffers = tfl_model->buffers(); + + for (uint32_t buffer_id = 0; buffer_id < tfl_buffers->size(); ++buffer_id) + { + _buffer_ctx[buffer_id] = (*tfl_buffers)[buffer_id]; + } +} + +} // namespace tflimport diff --git a/contrib/enco/frontend/tflite/src/Context.h b/contrib/enco/frontend/tflite/src/Context.h new file mode 100644 index 0000000..d126a02 --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Context.h @@ -0,0 +1,167 @@ +/* + * 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 __CONTEXT_H__ +#define __CONTEXT_H__ + +#include "Convert.h" +#include "TensorBags.h" + +#include +#include + +#include + +#include + +using namespace nncc::core::ADT; + +namespace tflimport +{ + +/** + * @brief Extracts and holds operand(tensor) information such as name and shape + */ +class TensorContext +{ +public: + void prepare(const tflite::SubGraph *graph); + + const std::string &name(uint32_t tensor_id) { return _name_ctx[tensor_id]; } + const tensor::Shape &shape(uint32_t tensor_id) { return _shape_ctx[tensor_id]; } + +private: + std::map _name_ctx; + std::map _shape_ctx; +}; + +/** + * @brief Class that holds operator codes and related methods + */ +class TflOpCodeContext +{ +public: + TflOpCodeContext(const flatbuffers::Vector> *opcodes); + + /** + * @brief Returns BuiltinOperator value of the operator + */ + tflite::BuiltinOperator builtin_code(const tflite::Operator *op) const; + + /** + * @brief Returns human readable name of the operator code of the operator + * + * @note TF lite InterpreterBuilder sets an error state and returns error code + * for invalid opcode. Here we just return human readable message as + * this method returns a name for the operator code. + */ + std::string opcode_name(const tflite::Operator *op) const; + +public: + static bool is_valid(const tflite::OperatorCode *opcode); + static bool is_custom(const tflite::OperatorCode *opcode); + +private: + std::vector _opcodes; +}; + +/** + * @brief Class to read and provide buffer information of tflite + */ +class TflBufferContext +{ +public: + template struct TflBuffer + { + TflBuffer(const T *p, size_t s) : ptr{p}, len{s} {}; + const T *ptr; + size_t len; + }; + +public: + explicit TflBufferContext(const tflite::Model *tfl_model); + +public: + template + TflBuffer tensor_buffer(const tflite::SubGraph *graph, uint32_t tensor_idx) const + { + TflBufferContext::TflBuffer res{nullptr, 0}; + const auto *tensor = graph->tensors()->Get(tensor_idx); + uint32_t tfl_buf_id = tensor->buffer(); + + assert(_buffer_ctx.size() > tfl_buf_id); + + const tflite::Buffer *tfl_buffer = _buffer_ctx.at(tfl_buf_id); + + if (auto *array = tfl_buffer->data()) + { + if (size_t size = array->size()) + { + assert(size % sizeof(T) == 0); + + res.len = size / sizeof(T); + res.ptr = reinterpret_cast(array->data()); + } + } + + return res; + } + +private: + std::map _buffer_ctx; +}; + +/** + * @brief Class to store context to build IR from tflite + */ +class GraphBuilderContext +{ +public: + explicit GraphBuilderContext(coco::Module *m, coco::Data *d, coco::Block *block, + TensorBags &tensor_bags, TensorContext &tensor_context, + TflBufferContext &buffer_context, const tflite::SubGraph *graph) + : _m(m), _d(d), _block(block), _tensor_bags(tensor_bags), _tensor_context(tensor_context), + _buffer_context(buffer_context), _graph(graph) + { + // DO NOTHING + } + + GraphBuilderContext() = delete; + GraphBuilderContext(const GraphBuilderContext &) = delete; + GraphBuilderContext(GraphBuilderContext &&) = delete; + +public: + coco::Module *m() { return _m; } + coco::Data *d() { return _d; } + coco::Block *block() { return _block; } + TensorContext &tensor() { return _tensor_context; } + TensorBags &bags() { return _tensor_bags; } + TflBufferContext &buffer() { return _buffer_context; } + const tflite::SubGraph *graph() { return _graph; } + +private: + coco::Module *_m; + coco::Data *_d; + coco::Block *_block; + TensorContext &_tensor_context; + TensorBags &_tensor_bags; + TflBufferContext &_buffer_context; + const tflite::SubGraph *_graph; +}; + +} // namespace tflimport + +#endif // __CONTEXT_H__ diff --git a/contrib/enco/frontend/tflite/src/Convert.cpp b/contrib/enco/frontend/tflite/src/Convert.cpp new file mode 100644 index 0000000..ffae95d --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Convert.cpp @@ -0,0 +1,57 @@ +/* + * 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 "Convert.h" + +#include + +#include + +using namespace nncc::core::ADT; + +namespace tflimport +{ + +IndexVector as_index_vector(const flatbuffers::Vector *array) +{ + const uint32_t size = array->size(); + + std::vector res(size); + + for (uint32_t i = 0; i < size; i++) + { + res[i] = array->Get(i); + } + + return res; +} + +tensor::Shape as_tensor_shape(const flatbuffers::Vector *shape) +{ + const uint32_t rank = shape->size(); + + tensor::Shape res; + + res.resize(rank); + for (uint32_t axis = 0; axis < rank; ++axis) + { + res.dim(axis) = shape->Get(axis); + } + + return res; +} + +} // namespace tflimport diff --git a/contrib/enco/frontend/tflite/src/Convert.h b/contrib/enco/frontend/tflite/src/Convert.h new file mode 100644 index 0000000..fb4c248 --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Convert.h @@ -0,0 +1,43 @@ +/* + * 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 __CONVERT_H__ +#define __CONVERT_H__ + +#include + +#include + +using namespace nncc::core::ADT; + +namespace tflimport +{ + +using IndexVector = std::vector; + +/** + * @brief Converts flatbuffers::Vector to IndexVector + */ +IndexVector as_index_vector(const flatbuffers::Vector *array); + +/** + * @brief Converts flatbuffers::Vector to nncc::core::ADT::tensor::Shape + */ +tensor::Shape as_tensor_shape(const flatbuffers::Vector *shape); + +} // namespace tflimport + +#endif // __CONVERT_H__ diff --git a/contrib/enco/frontend/tflite/src/Frontend.cpp b/contrib/enco/frontend/tflite/src/Frontend.cpp index 4a1de41..8f2953d 100644 --- a/contrib/enco/frontend/tflite/src/Frontend.cpp +++ b/contrib/enco/frontend/tflite/src/Frontend.cpp @@ -15,119 +15,19 @@ */ #include "Frontend.h" -#include "IRBuilder.h" - -#include - -#include -#include -#include +#include "Context.h" +#include "Convert.h" +#include "TensorBags.h" +#include "GraphBuilderRegistry.h" #include #include -#include - -#include -#include -#include - using namespace nncc::core::ADT; -using namespace morph::tflite; - -using stdex::make_unique; namespace tflimport { -using IndexVector = std::vector; - -/** - * @brief Converts flatbuffers::Vector to IndexVector - */ -IndexVector as_index_vector(const flatbuffers::Vector *array) -{ - const uint32_t size = array->size(); - - std::vector res(size); - - for (uint32_t i = 0; i < size; i++) - { - res[i] = array->Get(i); - } - - return res; -} - -/** - * @brief Converts flatbuffers::Vector to nncc::core::ADT::tensor::Shape - */ -tensor::Shape as_tensor_shape(const flatbuffers::Vector *shape) -{ - const uint32_t rank = shape->size(); - - tensor::Shape res; - - res.resize(rank); - for (uint32_t axis = 0; axis < rank; ++axis) - { - res.dim(axis) = shape->Get(axis); - } - - return res; -} - -/** - * @brief Extracts and holds operand(tensor) information such as name and shape - */ -class TensorContext -{ -public: - void prepare(const tflite::SubGraph *graph) - { - for (uint32_t tensor_id = 0; tensor_id < graph->tensors()->size(); ++tensor_id) - { - auto const tensor_info = graph->tensors()->Get(tensor_id); - auto const tensor_name = tensor_info->name()->str(); - auto const tensor_shape = as_tensor_shape(tensor_info->shape()); - - _name_ctx[tensor_id] = tensor_name; - _shape_ctx[tensor_id] = tensor_shape; - } - } - - const std::string &name(uint32_t tensor_id) { return _name_ctx[tensor_id]; } - const tensor::Shape &shape(uint32_t tensor_id) { return _shape_ctx[tensor_id]; } - -private: - std::map _name_ctx; - std::map _shape_ctx; -}; - -/** - * @brief Pre-creates coco:Bags for each operands(tensors) - */ -class TensorBags -{ -public: - void prepare(const tflite::SubGraph *graph, std::unique_ptr &m) - { - for (uint32_t tensor_id = 0; tensor_id < graph->tensors()->size(); ++tensor_id) - { - auto const tensor_info = graph->tensors()->Get(tensor_id); - auto const tensor_shape = as_tensor_shape(tensor_info->shape()); - auto const tensor_bag = m->entity()->bag()->create(num_elements(tensor_shape)); - - _bag_ctx[tensor_id] = tensor_bag; - } - } - - coco::Bag *bag(int32_t tensor_id) { return _bag_ctx[tensor_id]; } - -private: - std::map _bag_ctx; -}; - /** * @brief Set module input operands and its information */ @@ -176,977 +76,6 @@ void set_module_outputs(coco::Module *m, TensorContext &ctx, TensorBags &bags, } } -/** - * @brief Class that holds operator codes and related methods - */ -class TflOpCodeContext -{ -public: - TflOpCodeContext(const flatbuffers::Vector> *opcodes) - { - for (const tflite::OperatorCode *opcode : *opcodes) - { - _opcodes.push_back(opcode); - } - } - - /** - * @brief Returns BuiltinOperator value of the operator - */ - tflite::BuiltinOperator builtin_code(const tflite::Operator *op) const - { - uint32_t index = op->opcode_index(); - assert(index < _opcodes.size()); - const tflite::OperatorCode *opcode = _opcodes.at(index); - return opcode->builtin_code(); - } - - /** - * @brief Returns human readable name of the operator code of the operator - * - * @note TF lite InterpreterBuilder sets an error state and returns error code - * for invalid opcode. Here we just return human readable message as - * this method returns a name for the operator code. - */ - std::string opcode_name(const tflite::Operator *op) const - { - uint32_t index = op->opcode_index(); - assert(index < _opcodes.size()); - const tflite::OperatorCode *opcode = _opcodes.at(index); - - if (!is_valid(opcode)) - { - std::ostringstream oss; - oss << "(invalid: " << index << ")"; - return oss.str(); - } - - if (is_custom(opcode)) - { - if (!opcode->custom_code()) - return "(invalid custom)"; - - return opcode->custom_code()->c_str(); - } - - tflite::BuiltinOperator code = opcode->builtin_code(); - return EnumNameBuiltinOperator(code); - } - -public: - static bool is_valid(const tflite::OperatorCode *opcode) - { - tflite::BuiltinOperator code = opcode->builtin_code(); - return (tflite::BuiltinOperator_MIN <= code && code <= tflite::BuiltinOperator_MAX); - } - - static bool is_custom(const tflite::OperatorCode *opcode) - { - tflite::BuiltinOperator code = opcode->builtin_code(); - return (code == tflite::BuiltinOperator_CUSTOM); - } - -private: - std::vector _opcodes; -}; - -/** - * @brief Class to read and provide buffer information of tflite - */ -class TflBufferContext -{ -public: - template struct TflBuffer - { - TflBuffer(const T *p, size_t s) : ptr{p}, len{s} {}; - const T *ptr; - size_t len; - }; - -public: - explicit TflBufferContext(const tflite::Model *tfl_model) - { - const flatbuffers::Vector> *tfl_buffers; - - tfl_buffers = tfl_model->buffers(); - - for (uint32_t buffer_id = 0; buffer_id < tfl_buffers->size(); ++buffer_id) - { - _buffer_ctx[buffer_id] = (*tfl_buffers)[buffer_id]; - } - } - -public: - template - TflBuffer tensor_buffer(const tflite::SubGraph *graph, uint32_t tensor_idx) const - { - TflBuffer res{nullptr, 0}; - const auto *tensor = graph->tensors()->Get(tensor_idx); - uint32_t tfl_buf_id = tensor->buffer(); - - assert(_buffer_ctx.size() > tfl_buf_id); - - const tflite::Buffer *tfl_buffer = _buffer_ctx.at(tfl_buf_id); - - if (auto *array = tfl_buffer->data()) - { - if (size_t size = array->size()) - { - assert(size % sizeof(T) == 0); - - res.len = size / sizeof(T); - res.ptr = reinterpret_cast(array->data()); - } - } - - return res; - } - -private: - std::map _buffer_ctx; -}; - -namespace -{ - -coco::Padding2D get_padding(const tensor::Shape &ifm_shape, const int kernel_w, const int kernel_h, - tflite::Padding padding, int stride_w, int stride_h, - int dilation_w_factor, int dilation_h_factor) -{ - assert(ifm_shape.rank() == 4); - - auto compute_padding = [](tflite::Padding padding, int stride, int dilation_rate, int in_size, - int filter_size) { - // code from tensorflow lite 1.9 - int effective_filter_size = (filter_size - 1) * dilation_rate + 1; - int out_size = (padding == tflite::Padding_SAME) - ? (in_size + stride - 1) / stride - : (padding == tflite::Padding_VALID) - ? (in_size - effective_filter_size + stride) / stride - : 0; - int value = ((out_size - 1) * stride + effective_filter_size - in_size) / 2; - return value > 0 ? value : 0; - }; - - // ifm shape is from order of NHWC. ifm W = dim(2), ifm H = dim(1) - int padding_w = compute_padding(padding, stride_w, dilation_w_factor, ifm_shape.dim(2), kernel_w); - int padding_h = compute_padding(padding, stride_h, dilation_h_factor, ifm_shape.dim(1), kernel_h); - - coco::Padding2D coco_padding; - coco_padding.top(padding_h).bottom(padding_h).left(padding_w).right(padding_w); - - return coco_padding; -} - -coco::Padding2D pool2D_padding(const tflite::Pool2DOptions *options, const tensor::Shape &ifm_shape, - const int filter_w, const int filter_h) -{ - return get_padding(ifm_shape, filter_w, filter_h, options->padding(), options->stride_w(), - options->stride_h(), 1, 1); -} - -coco::Padding2D conv2D_padding(const tflite::Conv2DOptions *options, const tensor::Shape &ifm_shape, - const tensor::Shape &kernel_shape) -{ - return get_padding(ifm_shape, kernel_shape.dim(2), kernel_shape.dim(1), /* kernel layout: NHWC */ - options->padding(), options->stride_w(), options->stride_h(), - options->dilation_w_factor(), options->dilation_h_factor()); -} - -/** - * @brief Add coco::Eval for fused activation. - * This method creates an ofm object, appends Eval(ofm object, RELU(...)) into block, - * and returns ofm object. - */ -coco::FeatureObject *build_activation(tflite::ActivationFunctionType act, coco::Block *block, - coco::FeatureObject *ifm) -{ - assert(ifm != nullptr && ifm->asFeature() != nullptr); // support feature only in this version - - coco::Module *m = block->module(); - - auto shape = ifm->asFeature()->shape(); - - // creates output object - auto output_obj = m->entity()->object()->create(); - auto output_bag = m->entity()->bag()->create(num_elements(shape)); - output_obj->bag(output_bag); - output_obj->layout(coco::FeatureLayouts::BHWC::create(shape)); - - switch (act) - { - case tflite::ActivationFunctionType::ActivationFunctionType_NONE: - { - // Create Copy Instr (copying from ifm to output_obj), - // redundant layer but optimized by backend - auto copy_ins = instr_builder(m).copy(output_obj, ifm); - - // Append the instruction to the block - block->instr()->append(copy_ins); - break; - } - case tflite::ActivationFunctionType::ActivationFunctionType_RELU: - { - // Create Eval(output_obj, Relu(load(ifm))) - auto load_op = op_builder(m).load(ifm).pop(); - auto relu_op = m->entity()->op()->create(); - relu_op->arg(load_op); - - auto eval_ins = instr_builder(m).eval(output_obj, relu_op); - - // Append the instruction to the block - block->instr()->append(eval_ins); - break; - } - default: - // TODO support RELU6, TanH, etc - assert(false); - break; - } - - return output_obj; -} - -} // namespace - -/** - * @brief Class to store context to build IR from tflite - */ -class GraphBuilderContext -{ -public: - explicit GraphBuilderContext(coco::Module *m, coco::Data *d, coco::Block *block, - TensorBags &tensor_bags, TensorContext &tensor_context, - TflBufferContext &buffer_context, const tflite::SubGraph *graph) - : _m(m), _d(d), _block(block), _tensor_bags(tensor_bags), _tensor_context(tensor_context), - _buffer_context(buffer_context), _graph(graph) - { - // DO NOTHING - } - - GraphBuilderContext() = delete; - GraphBuilderContext(const GraphBuilderContext &) = delete; - GraphBuilderContext(GraphBuilderContext &&) = delete; - -public: - coco::Module *m() { return _m; } - coco::Data *d() { return _d; } - coco::Block *block() { return _block; } - TensorContext &tensor() { return _tensor_context; } - TensorBags &bags() { return _tensor_bags; } - TflBufferContext &buffer() { return _buffer_context; } - const tflite::SubGraph *graph() { return _graph; } - -private: - coco::Module *_m; - coco::Data *_d; - coco::Block *_block; - TensorContext &_tensor_context; - TensorBags &_tensor_bags; - TflBufferContext &_buffer_context; - const tflite::SubGraph *_graph; -}; - -/** - * @brief Parent class of tflite operation graph builders (e.g., Conv2DGraphBuilder) - */ -class GraphBuilder -{ -public: - virtual void build(const tflite::Operator *op, GraphBuilderContext *) const = 0; - virtual ~GraphBuilder() {} -}; - -/** - * @brief GraphBuilder for Conv2D operator - */ -class Conv2DGraphBuilder : public GraphBuilder -{ -public: - void build(const tflite::Operator *op, GraphBuilderContext *) const override; -}; - -void Conv2DGraphBuilder::build(const tflite::Operator *op, GraphBuilderContext *context) const -{ - assert(context != nullptr); - - // preparation - coco::Module *m = context->m(); - coco::Data *d = context->d(); - coco::Block *blk = context->block(); - TensorContext &tensor_context = context->tensor(); - TensorBags &bags = context->bags(); - TflBufferContext &buffer_context = context->buffer(); - const tflite::SubGraph *graph = context->graph(); - tflimport::IndexVector opinputs = tflimport::as_index_vector(op->inputs()); - tflimport::IndexVector opoutputs = tflimport::as_index_vector(op->outputs()); - - // these are fixed in tflite - // input index 0 : input feature - // input index 1 : kernel - // input index 2 : bias (optional) - bool hasBias = (opinputs.size() == 3); - assert(opinputs.size() == 2 || hasBias); - assert(opoutputs.size() == 1); - - int ifm_idx = opinputs.at(0); - int ker_idx = opinputs.at(1); - int ofm_idx = opoutputs.at(0); - - const tensor::Shape &ifm_shape = tensor_context.shape(ifm_idx); - const tensor::Shape &ofm_shape = tensor_context.shape(ofm_idx); - const tensor::Shape &ker_shape = tensor_context.shape(ker_idx); - - // Create an input feature map object - auto *ifm_obj = m->entity()->object()->create(); - auto *ifm_bag = bags.bag(ifm_idx); - ifm_obj->bag(ifm_bag); - ifm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ifm_shape))); - - // Create an an output feature map object - auto *ofm_obj = m->entity()->object()->create(); - auto *ofm_bag = bags.bag(ofm_idx); - ofm_obj->bag(ofm_bag); - ofm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); - - // Create an kernel object - auto *ker_obj = m->entity()->object()->create(); - auto *ker_bag = bags.bag(ker_idx); - ker_obj->bag(ker_bag); - ker_obj->layout(coco::KernelLayouts::NHWC::create(as_kernel_shape(ker_shape))); - - // Create a kernel overlay for the kernel object - // TODO : support for other types - d->f32()->allocate(ker_obj); - - TflBufferContext::TflBuffer buffer = buffer_context.tensor_buffer(graph, ker_idx); - - auto ker_spn = d->f32()->weight(ker_bag); - for (uint32_t idx = 0; idx < buffer.len; ++idx) - { - ker_spn[idx] = buffer.ptr[idx]; - } - - // Create a Load op - auto load = op_builder(m).load(ifm_obj).pop(); - - // Create a Conv2D op - auto coco_conv2d = m->entity()->op()->create(); - - // populating Conv2D objects and options such as stride and padding - coco_conv2d->ker(ker_obj); - - auto *conv_params = op->builtin_options_as_Conv2DOptions(); - - coco_conv2d->stride()->vertical(conv_params->stride_h()); - coco_conv2d->stride()->horizontal(conv_params->stride_w()); - - // conv_params->padding() to left, top, right, bottom - coco::Padding2D padding = conv2D_padding(conv_params, ifm_shape, ker_shape); - - coco_conv2d->pad()->top(padding.top()); - coco_conv2d->pad()->bottom(padding.bottom()); - coco_conv2d->pad()->left(padding.left()); - coco_conv2d->pad()->right(padding.right()); - - // Link ops - coco_conv2d->arg(load); - - // Object to store Conv2D output - auto *conv2d_obj = m->entity()->object()->create(); - auto *conv2d_bag = m->entity()->bag()->create(num_elements(ofm_shape)); - conv2d_obj->bag(conv2d_bag); - conv2d_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); - - // Create an Eval instruction for Conv2D - auto conv2d_ins = instr_builder(m).eval(conv2d_obj, coco_conv2d); - - // Append the instruction to the block - blk->instr()->append(conv2d_ins); - - // Last Object to make a copy to Output Object - coco::FeatureObject *last_obj = conv2d_obj; - - if (hasBias) - { - // When there is a bias, use btmp_obj as bias add output - // Bias is adding last_obj with bias weight values - auto *btmp_obj = m->entity()->object()->create(); - auto *btmp_bag = m->entity()->bag()->create(num_elements(ofm_shape)); - btmp_obj->bag(btmp_bag); - btmp_obj->layout(coco::FeatureLayouts::BHWC::create(ofm_obj->shape())); - - int bias_idx = opinputs.at(2); - - // Create an object for bias - auto bias_obj = m->entity()->object()->create(); - coco::Bag *bias_bag = bags.bag(bias_idx); - bias_obj->bag(bias_bag); - bias_obj->layout(coco::FeatureLayouts::BC::create(ofm_obj->shape())); - - // Fill bias data - d->f32()->allocate(bias_bag); - - auto bias_span = d->f32()->weight(bias_bag); - buffer = buffer_context.tensor_buffer(graph, bias_idx); - assert(buffer.ptr != nullptr && buffer.len > 0); - for (uint32_t idx = 0; idx < buffer.len; ++idx) - { - bias_span[idx] = buffer.ptr[idx]; - } - - // Create Op of conv2d output (last_obj) + bias values(bias_obj) - auto bias_add = op_builder(m).load(last_obj).load(bias_obj).add().pop(); - - // Create Instr as bias add result write to btmp_obj - auto bias_add_ins = instr_builder(m).eval(btmp_obj, bias_add); - - // Append the instruction - blk->instr()->append(bias_add_ins); - - // Update last_obj to btmp_obj - last_obj = btmp_obj; - } - - // fused activation - coco::FeatureObject *act_output = - build_activation(conv_params->fused_activation_function(), blk, last_obj); - - // Create Copy Instr of last_obj to Output Object - auto copy_ins = instr_builder(m).copy(ofm_obj, act_output); - blk->instr()->append(copy_ins); -} - -/** - * @brief GraphBuilder for AvgPool2D operator - */ -class AvgPool2DGraphBuilder : public GraphBuilder -{ -public: - void build(const tflite::Operator *op, GraphBuilderContext *) const override; -}; - -void AvgPool2DGraphBuilder::build(const tflite::Operator *op, GraphBuilderContext *context) const -{ - assert(context != nullptr); // check if init(..) is called - - coco::Module *m = context->m(); - coco::Block *blk = context->block(); - TensorContext &tensor_context = context->tensor(); - TensorBags &bags = context->bags(); - - tflimport::IndexVector opinputs = tflimport::as_index_vector(op->inputs()); - tflimport::IndexVector opoutputs = tflimport::as_index_vector(op->outputs()); - - // these are fixed in tflite - // input index 0 : input feature - // output index 0 : output feature - assert(opinputs.size() == 1); - assert(opoutputs.size() == 1); - - int ifm_idx = opinputs.at(0); - int ofm_idx = opoutputs.at(0); - - const tensor::Shape &ifm_shape = tensor_context.shape(ifm_idx); - const tensor::Shape &ofm_shape = tensor_context.shape(ofm_idx); - - // Create an object for an input feature map - coco::FeatureObject *ifm_obj = m->entity()->object()->create(); - coco::Bag *ifm_bag = bags.bag(ifm_idx); - ifm_obj->bag(ifm_bag); - ifm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ifm_shape))); - - // Create an object for an output feature map - coco::FeatureObject *ofm_obj = m->entity()->object()->create(); - coco::Bag *ofm_bag = bags.bag(ofm_idx); - ofm_obj->bag(ofm_bag); - ofm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); - - // Create a Load op - auto coco_load = op_builder(m).load(ifm_obj).pop(); - - // Create a AvgPool2D - auto coco_avgpool2d = m->entity()->op()->create(); - auto *params = op->builtin_options_as_Pool2DOptions(); - - // NOTE For Tensorflow lite, PaddingExcluded is needed - coco_avgpool2d->divisor(coco::AvgPool2D::Divisor::PaddingExcluded); - - coco_avgpool2d->window()->height(params->filter_height()); - coco_avgpool2d->window()->width(params->filter_width()); - - coco_avgpool2d->stride()->vertical(params->stride_h()); - coco_avgpool2d->stride()->horizontal(params->stride_w()); - - coco::Padding2D padding = - pool2D_padding(params, ifm_shape, params->filter_width(), params->filter_height()); - - coco_avgpool2d->pad()->top(padding.top()); - coco_avgpool2d->pad()->bottom(padding.bottom()); - coco_avgpool2d->pad()->left(padding.left()); - coco_avgpool2d->pad()->right(padding.right()); - - // Link ops - coco_avgpool2d->arg(coco_load); - - // Create an Eval instruction - auto ins = instr_builder(m).eval(ofm_obj, coco_avgpool2d); - - // Append the instruction to the block - blk->instr()->append(ins); - - // TODO activation, e.g., relu - assert(params->fused_activation_function() == - tflite::ActivationFunctionType::ActivationFunctionType_NONE); -} - -/** - * @brief GraphBuilder for AvgPool2D operator - */ -class MaxPool2DGraphBuilder : public GraphBuilder -{ -public: - void build(const tflite::Operator *op, GraphBuilderContext *) const override; -}; - -void MaxPool2DGraphBuilder::build(const tflite::Operator *op, GraphBuilderContext *context) const -{ - assert(context != nullptr); // check if init(..) is called - - coco::Module *m = context->m(); - coco::Block *blk = context->block(); - TensorContext &tensor_context = context->tensor(); - TensorBags &bags = context->bags(); - - tflimport::IndexVector opinputs = tflimport::as_index_vector(op->inputs()); - tflimport::IndexVector opoutputs = tflimport::as_index_vector(op->outputs()); - - // these are fixed in tflite - // input index 0 : input feature - // output index 0 : output feature - assert(opinputs.size() == 1); - assert(opoutputs.size() == 1); - - int ifm_idx = opinputs.at(0); - int ofm_idx = opoutputs.at(0); - - const tensor::Shape &ifm_shape = tensor_context.shape(ifm_idx); - const tensor::Shape &ofm_shape = tensor_context.shape(ofm_idx); - - // Create an object for an input feature map - coco::FeatureObject *ifm_obj = m->entity()->object()->create(); - coco::Bag *ifm_bag = bags.bag(ifm_idx); - ifm_obj->bag(ifm_bag); - ifm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ifm_shape))); - - // Create an object for an output feature map - coco::FeatureObject *ofm_obj = m->entity()->object()->create(); - coco::Bag *ofm_bag = bags.bag(ofm_idx); - ofm_obj->bag(ofm_bag); - ofm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); - - // Create a Load op - coco::Op *coco_load = op_builder(m).load(ifm_obj).pop(); - - // Create a MaxPool2D - coco::MaxPool2D *coco_maxpool2d = m->entity()->op()->create(); - const tflite::Pool2DOptions *params = op->builtin_options_as_Pool2DOptions(); - - coco_maxpool2d->window()->height(params->filter_height()); - coco_maxpool2d->window()->width(params->filter_width()); - - coco_maxpool2d->stride()->vertical(params->stride_h()); - coco_maxpool2d->stride()->horizontal(params->stride_w()); - - coco::Padding2D padding = - pool2D_padding(params, ifm_shape, params->filter_width(), params->filter_height()); - - coco_maxpool2d->pad()->top(padding.top()); - coco_maxpool2d->pad()->bottom(padding.bottom()); - coco_maxpool2d->pad()->left(padding.left()); - coco_maxpool2d->pad()->right(padding.right()); - - // Link ops - coco_maxpool2d->arg(coco_load); - - // Create an Eval instruction - coco::Eval *ins = instr_builder(m).eval(ofm_obj, coco_maxpool2d); - - // Append the instruction to the block - blk->instr()->append(ins); - - // TODO activation, e.g., relu - assert(params->fused_activation_function() == - tflite::ActivationFunctionType::ActivationFunctionType_NONE); -} - -/** - * @brief GraphBuilder for Concatenation operator - */ -class ConcatenationGraphBuilder : public GraphBuilder -{ -public: - void build(const tflite::Operator *op, GraphBuilderContext *) const override; -}; - -void ConcatenationGraphBuilder::build(const tflite::Operator *op, - GraphBuilderContext *context) const -{ - assert(context != nullptr); - - coco::Module *m = context->m(); - coco::Data *d = context->d(); - coco::Block *blk = context->block(); - TensorContext &tensor_context = context->tensor(); - TensorBags &bags = context->bags(); - tflimport::IndexVector opinputs = tflimport::as_index_vector(op->inputs()); - tflimport::IndexVector opoutputs = tflimport::as_index_vector(op->outputs()); - - // these are fixed in tflite - // input index 0 ~ N : any number of input features - // output index 0 : one output feature - assert(opinputs.size() > 0); - assert(opoutputs.size() == 1); - - // Default parameter values are referenced from schema_generated.h - int32_t concat_axis = 0; - tflite::ActivationFunctionType activation = tflite::ActivationFunctionType_NONE; - - if (auto *concatenation_params = op->builtin_options_as_ConcatenationOptions()) - { - activation = concatenation_params->fused_activation_function(); - concat_axis = concatenation_params->axis(); - - const int32_t rank = static_cast(tensor_context.shape(opinputs.at(0)).rank()); - if (concat_axis < 0) - { - concat_axis += rank; - } - assert(concat_axis >= 0); - assert(concat_axis < rank); - } - // TODO handle other axis - assert(concat_axis == 3); - assert(activation == tflite::ActivationFunctionType_NONE); - - // Construct a vector of input objects - std::vector input_objects; - - for (auto &input_index : opinputs) - { - const tensor::Shape &input_shape = tensor_context.shape(input_index); - coco::FeatureObject *input_obj = m->entity()->object()->create(); - coco::Bag *input_bag = bags.bag(input_index); - input_obj->bag(input_bag); - input_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(input_shape))); - - input_objects.emplace_back(input_obj); - } - - coco::FeatureObject *last_feature = input_objects.at(0); - - assert(last_feature != nullptr); - assert(last_feature->bag() != nullptr); - - // Update coco IR - // - // Given a sequence of input features %in[0] / %in[1] / ... / %in[N] - // the below code constructs a sequence of eval instructions - // - Load is omitted for simplicity - // - // %tmp = eval(ConcatF(%in[0], %in[1])) - // %tmp = eval(ConcatF(%tmp, %in[2])) - // ... - // %tmp = eval(ConcatF(%tmp, %in[N])) - // %out[0] = copy(%tmp) - // - for (uint32_t n = 1; n < input_objects.size(); ++n) - { - auto const left_feature = last_feature; - auto const left_shape = left_feature->layout()->shape(); - - auto right_feature = input_objects.at(n); - auto right_shape = right_feature->layout()->shape(); - - // Batch is not supported, yet - assert(left_feature->layout()->batch() == 1); - assert(right_feature->layout()->batch() == 1); - - // Height and Width SHOULD BE IDENTICAL for depth concat - assert(left_shape.height() == right_shape.height()); - assert(left_shape.width() == right_shape.width()); - - const uint32_t C = left_shape.depth() + right_shape.depth(); - const uint32_t H = left_shape.height(); - const uint32_t W = left_shape.width(); - - const nncc::core::ADT::feature::Shape out_shape{C, H, W}; - - auto out_bag = m->entity()->bag()->create(num_elements(out_shape)); - auto out_feature = m->entity()->object()->create(); - - out_feature->bag(out_bag); - out_feature->layout(coco::FeatureLayouts::BHWC::create(out_shape)); - - auto left_load = op_builder(m).load(left_feature).pop(); - auto right_load = op_builder(m).load(right_feature).pop(); - - auto concat_f = m->entity()->op()->create(); - - concat_f->axis(coco::ConcatF::Axis::Depth); - concat_f->left(left_load); - concat_f->right(right_load); - - auto eval = instr_builder(m).eval(out_feature, concat_f); - - // Append the constructed Shuffle instruction - blk->instr()->append(eval); - - // Update 'last_feature' - last_feature = out_feature; - } - - // Insert copy instruction from last_feature to output operand - int const ofm_idx = opoutputs.at(0); - auto const ofm_shape = tensor_context.shape(ofm_idx); - - auto ofm_bag = bags.bag(ofm_idx); - auto ofm_obj = m->entity()->object()->create(); - - ofm_obj->bag(ofm_bag); - ofm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); - - // Create a Copy instruction from last into ofm - auto copy_ins = instr_builder(m).copy(ofm_obj, last_feature); - - // Append the instruction - blk->instr()->append(copy_ins); -} - -/** - * @brief GraphBuilder for ReLU operator - */ -class ReLUGraphBuilder : public GraphBuilder -{ -public: - void build(const tflite::Operator *op, GraphBuilderContext *) const override; -}; - -void ReLUGraphBuilder::build(const tflite::Operator *op, GraphBuilderContext *context) const -{ - assert(context != nullptr); // check if init(..) is called - - coco::Module *m = context->m(); - coco::Block *blk = context->block(); - TensorContext &tensor_context = context->tensor(); - TensorBags &bags = context->bags(); - - tflimport::IndexVector opinputs = tflimport::as_index_vector(op->inputs()); - tflimport::IndexVector opoutputs = tflimport::as_index_vector(op->outputs()); - - // these are fixed in tflite - // input index 0 : input feature - // output index 0 : output feature - assert(opinputs.size() == 1); - assert(opoutputs.size() == 1); - - auto ifm_idx = opinputs.at(0); - auto ofm_idx = opoutputs.at(0); - - const tensor::Shape &ifm_shape = tensor_context.shape(ifm_idx); - const tensor::Shape &ofm_shape = tensor_context.shape(ofm_idx); - - // Create an object for an input feature map - coco::FeatureObject *ifm_obj = m->entity()->object()->create(); - coco::Bag *ifm_bag = bags.bag(ifm_idx); - ifm_obj->bag(ifm_bag); - ifm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ifm_shape))); - - // Create an object for an output feature map - coco::FeatureObject *ofm_obj = m->entity()->object()->create(); - coco::Bag *ofm_bag = bags.bag(ofm_idx); - ofm_obj->bag(ofm_bag); - ofm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); - - // Create a Load op - auto coco_load = op_builder(m).load(ifm_obj).pop(); - - // Create a ReLU - auto coco_relu = m->entity()->op()->create(); - - // Link ops - coco_relu->arg(coco_load); - - // Create an Eval instruction - auto eval_ins = instr_builder(m).eval(ofm_obj, coco_relu); - - // Append the instruction to the block - blk->instr()->append(eval_ins); -} - -/** - * @brief GraphBuilder for ReLU6 operator - */ -class ReLU6GraphBuilder : public GraphBuilder -{ -public: - void build(const tflite::Operator *op, GraphBuilderContext *) const override; -}; - -void ReLU6GraphBuilder::build(const tflite::Operator *op, GraphBuilderContext *context) const -{ - assert(context != nullptr); // check if init(..) is called - - coco::Module *m = context->m(); - coco::Block *blk = context->block(); - TensorContext &tensor_context = context->tensor(); - TensorBags &bags = context->bags(); - - tflimport::IndexVector opinputs = tflimport::as_index_vector(op->inputs()); - tflimport::IndexVector opoutputs = tflimport::as_index_vector(op->outputs()); - - // these are fixed in tflite - // input index 0 : input feature - // output index 0 : output feature - assert(opinputs.size() == 1); - assert(opoutputs.size() == 1); - - int ifm_idx = opinputs.at(0); - int ofm_idx = opoutputs.at(0); - - const tensor::Shape &ifm_shape = tensor_context.shape(ifm_idx); - const tensor::Shape &ofm_shape = tensor_context.shape(ofm_idx); - - // Create an object for an input feature map - coco::FeatureObject *ifm_obj = m->entity()->object()->create(); - coco::Bag *ifm_bag = bags.bag(ifm_idx); - ifm_obj->bag(ifm_bag); - ifm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ifm_shape))); - - // Create an object for an output feature map - coco::FeatureObject *ofm_obj = m->entity()->object()->create(); - coco::Bag *ofm_bag = bags.bag(ofm_idx); - ofm_obj->bag(ofm_bag); - ofm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); - - // Create a Load op - auto coco_load = op_builder(m).load(ifm_obj).pop(); - - // Create a ReLU6 - auto coco_relu6 = m->entity()->op()->create(); - - // Link ops - coco_relu6->arg(coco_load); - - // Create an Eval instruction - auto eval_ins = instr_builder(m).eval(ofm_obj, coco_relu6); - - // Append the instruction to the block - blk->instr()->append(eval_ins); -} - -/** - * @brief GraphBuilder for Reshape operator - */ -class ReshapeGraphBuilder : public GraphBuilder -{ -public: - void build(const tflite::Operator *op, GraphBuilderContext *) const override; -}; - -void ReshapeGraphBuilder::build(const tflite::Operator *op, GraphBuilderContext *context) const -{ - assert(context != nullptr); // check if init(..) is called - - coco::Module *m = context->m(); - coco::Block *blk = context->block(); - TensorBags &bags = context->bags(); - - tflimport::IndexVector opinputs = tflimport::as_index_vector(op->inputs()); - tflimport::IndexVector opoutputs = tflimport::as_index_vector(op->outputs()); - - // these are fixed in tflite - // input index 0 : input feature - // input index 1 : output shape (int32_t), (optional or not, is not clear) - // output index 0 : output feature - assert(opinputs.size() == 1 || opinputs.size() == 2); - assert(opoutputs.size() == 1); - - // Note: there are actually 3 places where we can get output shape from - // current TF lite implementation. From output operand shape, second input, - // and ReshapeOption (new_shape). Here we use output operand shape - int ifm_idx = opinputs.at(0); - int ofm_idx = opoutputs.at(0); - - auto ifm_bag = bags.bag(ifm_idx); - auto ofm_bag = bags.bag(ofm_idx); - - // TODO: move to InstrBuilder as 'shuffle_elements()' - // Create a 1:1 shuffle instruction from ifm into ofm - // Note: Reshape is change of shape information and there is no value change - // in the bag itself. We implement this as just make a element wise copy of - // the bag from input to output. So there is no need of 'reshape' operator - auto shuffle_ins = m->entity()->instr()->create(); - auto num_elem = ifm_bag->size(); - - assert(num_elem == ofm_bag->size()); - - shuffle_ins->from(ifm_bag); - shuffle_ins->into(ofm_bag); - - for (uint32_t n = 0; n < num_elem; ++n) - { - const auto from = coco::ElemID(n); - const auto into = coco::ElemID(n); - - shuffle_ins->insert(from, into); - } - - // Append the instruction - blk->instr()->append(shuffle_ins); -} - -/** - * @brief Class to return graph builder for passed tflite::builtinOperator - */ -class GraphBuilderRegistry -{ -public: - /** - * @brief Returns registered GraphBuilder pointer for BuiltinOperator or - * nullptr if not registered - */ - const GraphBuilder *lookup(tflite::BuiltinOperator op) const - { - if (_builder_map.find(op) == _builder_map.end()) - return nullptr; - - return _builder_map.at(op).get(); - } - - static GraphBuilderRegistry &get() - { - static GraphBuilderRegistry me; - return me; - } - -private: - GraphBuilderRegistry() - { - // add GraphBuilder for each tflite operation. - _builder_map[tflite::BuiltinOperator_CONV_2D] = make_unique(); - _builder_map[tflite::BuiltinOperator_AVERAGE_POOL_2D] = make_unique(); - _builder_map[tflite::BuiltinOperator_MAX_POOL_2D] = make_unique(); - _builder_map[tflite::BuiltinOperator_CONCATENATION] = make_unique(); - _builder_map[tflite::BuiltinOperator_RELU] = make_unique(); - _builder_map[tflite::BuiltinOperator_RELU6] = make_unique(); - _builder_map[tflite::BuiltinOperator_RESHAPE] = make_unique(); - } - -private: - std::map> _builder_map; -}; - } // namespace tflimport Frontend::Frontend(std::unique_ptr &&raw) : _raw{std::move(raw)} diff --git a/contrib/enco/frontend/tflite/src/GraphBuilder.h b/contrib/enco/frontend/tflite/src/GraphBuilder.h new file mode 100644 index 0000000..613c5ff --- /dev/null +++ b/contrib/enco/frontend/tflite/src/GraphBuilder.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __GRAPH_BUILDER_H__ +#define __GRAPH_BUILDER_H__ + +#include "Context.h" + +#include + +namespace tflimport +{ + +/** + * @brief Parent class of tflite operation graph builders (e.g., Conv2DGraphBuilder) + */ +class GraphBuilder +{ +public: + virtual void build(const tflite::Operator *op, GraphBuilderContext *context) const = 0; + virtual ~GraphBuilder() {} +}; + +} // namespace tflimport + +#endif // __GRAPH_BUILDER_H__ diff --git a/contrib/enco/frontend/tflite/src/GraphBuilderRegistry.h b/contrib/enco/frontend/tflite/src/GraphBuilderRegistry.h new file mode 100644 index 0000000..16d5cc6 --- /dev/null +++ b/contrib/enco/frontend/tflite/src/GraphBuilderRegistry.h @@ -0,0 +1,81 @@ +/* + * 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 __GRAPH_BUILDER_REGISTRY_H__ +#define __GRAPH_BUILDER_REGISTRY_H__ + +#include "Op/Conv2D.h" +#include "Op/AveragePool2D.h" +#include "Op/MaxPool2D.h" +#include "Op/Concatenation.h" +#include "Op/ReLU.h" +#include "Op/ReLU6.h" +#include "Op/Reshape.h" + +#include +#include + +#include + +using stdex::make_unique; + +namespace tflimport +{ + +/** + * @brief Class to return graph builder for passed tflite::builtinOperator + */ +class GraphBuilderRegistry +{ +public: + /** + * @brief Returns registered GraphBuilder pointer for BuiltinOperator or + * nullptr if not registered + */ + const GraphBuilder *lookup(tflite::BuiltinOperator op) const + { + if (_builder_map.find(op) == _builder_map.end()) + return nullptr; + + return _builder_map.at(op).get(); + } + + static GraphBuilderRegistry &get() + { + static GraphBuilderRegistry me; + return me; + } + +private: + GraphBuilderRegistry() + { + // add GraphBuilder for each tflite operation. + _builder_map[tflite::BuiltinOperator_CONV_2D] = make_unique(); + _builder_map[tflite::BuiltinOperator_AVERAGE_POOL_2D] = make_unique(); + _builder_map[tflite::BuiltinOperator_MAX_POOL_2D] = make_unique(); + _builder_map[tflite::BuiltinOperator_CONCATENATION] = make_unique(); + _builder_map[tflite::BuiltinOperator_RELU] = make_unique(); + _builder_map[tflite::BuiltinOperator_RELU6] = make_unique(); + _builder_map[tflite::BuiltinOperator_RESHAPE] = make_unique(); + } + +private: + std::map> _builder_map; +}; + +} // namespace tflimport + +#endif // __GRAPH_BUILDER_REGISTRY_H__ diff --git a/contrib/enco/frontend/tflite/src/IRBuilder.h b/contrib/enco/frontend/tflite/src/IRBuilder.h index 0fb979b..edfe247 100644 --- a/contrib/enco/frontend/tflite/src/IRBuilder.h +++ b/contrib/enco/frontend/tflite/src/IRBuilder.h @@ -127,8 +127,8 @@ private: std::deque _stack; }; -OpBuilder op_builder(coco::Module *m) { return OpBuilder{m}; } -OpBuilder op_builder(const std::unique_ptr &m) { return op_builder(m.get()); } +inline OpBuilder op_builder(coco::Module *m) { return OpBuilder{m}; } +inline OpBuilder op_builder(const std::unique_ptr &m) { return op_builder(m.get()); } class InstrBuilder { @@ -172,7 +172,7 @@ private: using ModuleHandle = std::unique_ptr; -InstrBuilder instr_builder(coco::Module *m) { return InstrBuilder{m}; } -InstrBuilder instr_builder(const ModuleHandle &m) { return instr_builder(m.get()); } +inline InstrBuilder instr_builder(coco::Module *m) { return InstrBuilder{m}; } +inline InstrBuilder instr_builder(const ModuleHandle &m) { return instr_builder(m.get()); } #endif // __IR_BUILDER_H__ diff --git a/contrib/enco/frontend/tflite/src/Op/Activation.cpp b/contrib/enco/frontend/tflite/src/Op/Activation.cpp new file mode 100644 index 0000000..ac5b053 --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Op/Activation.cpp @@ -0,0 +1,81 @@ +/* + * 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 "Activation.h" + +#include + +#include +#include + +#include +#include + +using namespace nncc::core::ADT; + +namespace tflimport +{ + +coco::FeatureObject *build_activation(tflite::ActivationFunctionType act, coco::Block *block, + coco::FeatureObject *ifm) +{ + assert(ifm != nullptr && ifm->asFeature() != nullptr); // support feature only in this version + + coco::Module *m = block->module(); + + auto shape = ifm->asFeature()->shape(); + + // creates output object + auto output_obj = m->entity()->object()->create(); + auto output_bag = m->entity()->bag()->create(num_elements(shape)); + output_obj->bag(output_bag); + output_obj->layout(coco::FeatureLayouts::BHWC::create(shape)); + + switch (act) + { + case tflite::ActivationFunctionType::ActivationFunctionType_NONE: + { + // Create Copy Instr (copying from ifm to output_obj), + // redundant layer but optimized by backend + auto copy_ins = instr_builder(m).copy(output_obj, ifm); + + // Append the instruction to the block + block->instr()->append(copy_ins); + break; + } + case tflite::ActivationFunctionType::ActivationFunctionType_RELU: + { + // Create Eval(output_obj, Relu(load(ifm))) + auto load_op = op_builder(m).load(ifm).pop(); + auto relu_op = m->entity()->op()->create(); + relu_op->arg(load_op); + + auto eval_ins = instr_builder(m).eval(output_obj, relu_op); + + // Append the instruction to the block + block->instr()->append(eval_ins); + break; + } + default: + // TODO support RELU6, TanH, etc + assert(false); + break; + } + + return output_obj; +} + +} // namespace tflimport diff --git a/contrib/enco/frontend/tflite/src/Op/Activation.h b/contrib/enco/frontend/tflite/src/Op/Activation.h new file mode 100644 index 0000000..05306dd --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Op/Activation.h @@ -0,0 +1,37 @@ +/* + * 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 __OP_ACTIVATION_H__ +#define __OP_ACTIVATION_H__ + +#include +#include + +#include + +namespace tflimport +{ + +/** + * @brief Add coco::Eval for fused activation. + * This method creates an ofm object, appends Eval(ofm object, RELU(...)) into block, + * and returns ofm object. + */ +coco::FeatureObject *build_activation(tflite::ActivationFunctionType act, coco::Block *block, + coco::FeatureObject *ifm); +} // namespace tflimport + +#endif // __OP_ACTIVATION_H__ diff --git a/contrib/enco/frontend/tflite/src/Op/AveragePool2D.cpp b/contrib/enco/frontend/tflite/src/Op/AveragePool2D.cpp new file mode 100644 index 0000000..fbc334a --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Op/AveragePool2D.cpp @@ -0,0 +1,112 @@ +/* + * 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 "AveragePool2D.h" + +#include "Convert.h" +#include "IRBuilder.h" +#include "GraphBuilder.h" +#include "Padding.h" +#include "Activation.h" + +#include +#include +#include + +#include +#include + +using namespace nncc::core::ADT; +using namespace morph::tflite; + +namespace tflimport +{ + +void AvgPool2DGraphBuilder::build(const tflite::Operator *op, GraphBuilderContext *context) const +{ + assert(context != nullptr); // check if init(..) is called + + coco::Module *m = context->m(); + coco::Block *blk = context->block(); + TensorContext &tensor_context = context->tensor(); + TensorBags &bags = context->bags(); + + IndexVector opinputs = as_index_vector(op->inputs()); + IndexVector opoutputs = as_index_vector(op->outputs()); + + // these are fixed in tflite + // input index 0 : input feature + // output index 0 : output feature + assert(opinputs.size() == 1); + assert(opoutputs.size() == 1); + + int ifm_idx = opinputs.at(0); + int ofm_idx = opoutputs.at(0); + + const tensor::Shape &ifm_shape = tensor_context.shape(ifm_idx); + const tensor::Shape &ofm_shape = tensor_context.shape(ofm_idx); + + // Create an object for an input feature map + coco::FeatureObject *ifm_obj = m->entity()->object()->create(); + coco::Bag *ifm_bag = bags.bag(ifm_idx); + ifm_obj->bag(ifm_bag); + ifm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ifm_shape))); + + // Create an object for an output feature map + coco::FeatureObject *ofm_obj = m->entity()->object()->create(); + coco::Bag *ofm_bag = bags.bag(ofm_idx); + ofm_obj->bag(ofm_bag); + ofm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); + + // Create a Load op + auto coco_load = op_builder(m).load(ifm_obj).pop(); + + // Create a AvgPool2D + auto coco_avgpool2d = m->entity()->op()->create(); + auto *params = op->builtin_options_as_Pool2DOptions(); + + // NOTE For Tensorflow lite, PaddingExcluded is needed + coco_avgpool2d->divisor(coco::AvgPool2D::Divisor::PaddingExcluded); + + coco_avgpool2d->window()->height(params->filter_height()); + coco_avgpool2d->window()->width(params->filter_width()); + + coco_avgpool2d->stride()->vertical(params->stride_h()); + coco_avgpool2d->stride()->horizontal(params->stride_w()); + + coco::Padding2D padding = + pool2D_padding(params, ifm_shape, params->filter_width(), params->filter_height()); + + coco_avgpool2d->pad()->top(padding.top()); + coco_avgpool2d->pad()->bottom(padding.bottom()); + coco_avgpool2d->pad()->left(padding.left()); + coco_avgpool2d->pad()->right(padding.right()); + + // Link ops + coco_avgpool2d->arg(coco_load); + + // Create an Eval instruction + auto ins = instr_builder(m).eval(ofm_obj, coco_avgpool2d); + + // Append the instruction to the block + blk->instr()->append(ins); + + // TODO activation, e.g., relu + assert(params->fused_activation_function() == + tflite::ActivationFunctionType::ActivationFunctionType_NONE); +} + +} // namespace tflimport diff --git a/contrib/enco/frontend/tflite/src/Op/AveragePool2D.h b/contrib/enco/frontend/tflite/src/Op/AveragePool2D.h new file mode 100644 index 0000000..1cd2b79 --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Op/AveragePool2D.h @@ -0,0 +1,38 @@ +/* + * 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 __OP_AVERAGEPOOL2D_H__ +#define __OP_AVERAGEPOOL2D_H__ + +#include "GraphBuilder.h" + +#include + +namespace tflimport +{ + +/** + * @brief GraphBuilder for AvgPool2D operator + */ +class AvgPool2DGraphBuilder : public GraphBuilder +{ +public: + void build(const tflite::Operator *op, GraphBuilderContext *) const override; +}; + +} // namespace tflimport + +#endif // __OP_AVERAGEPOOL2D_H__ diff --git a/contrib/enco/frontend/tflite/src/Op/Concatenation.cpp b/contrib/enco/frontend/tflite/src/Op/Concatenation.cpp new file mode 100644 index 0000000..4965d1b --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Op/Concatenation.cpp @@ -0,0 +1,169 @@ +/* + * 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 "Concatenation.h" + +#include "IRBuilder.h" +#include "GraphBuilder.h" + +#include +#include +#include + +#include +#include + +using namespace nncc::core::ADT; +using namespace morph::tflite; + +namespace tflimport +{ + +void ConcatenationGraphBuilder::build(const tflite::Operator *op, + GraphBuilderContext *context) const +{ + assert(context != nullptr); + + coco::Module *m = context->m(); + coco::Data *d = context->d(); + coco::Block *blk = context->block(); + TensorContext &tensor_context = context->tensor(); + TensorBags &bags = context->bags(); + IndexVector opinputs = as_index_vector(op->inputs()); + IndexVector opoutputs = as_index_vector(op->outputs()); + + // these are fixed in tflite + // input index 0 ~ N : any number of input features + // output index 0 : one output feature + assert(opinputs.size() > 0); + assert(opoutputs.size() == 1); + + // Default parameter values are referenced from schema_generated.h + int32_t concat_axis = 0; + tflite::ActivationFunctionType activation = tflite::ActivationFunctionType_NONE; + + if (auto *concatenation_params = op->builtin_options_as_ConcatenationOptions()) + { + activation = concatenation_params->fused_activation_function(); + concat_axis = concatenation_params->axis(); + + const int32_t rank = static_cast(tensor_context.shape(opinputs.at(0)).rank()); + if (concat_axis < 0) + { + concat_axis += rank; + } + assert(concat_axis >= 0); + assert(concat_axis < rank); + } + // TODO handle other axis + assert(concat_axis == 3); + assert(activation == tflite::ActivationFunctionType_NONE); + + // Construct a vector of input objects + std::vector input_objects; + + for (auto &input_index : opinputs) + { + const tensor::Shape &input_shape = tensor_context.shape(input_index); + coco::FeatureObject *input_obj = m->entity()->object()->create(); + coco::Bag *input_bag = bags.bag(input_index); + input_obj->bag(input_bag); + input_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(input_shape))); + + input_objects.emplace_back(input_obj); + } + + coco::FeatureObject *last_feature = input_objects.at(0); + + assert(last_feature != nullptr); + assert(last_feature->bag() != nullptr); + + // Update coco IR + // + // Given a sequence of input features %in[0] / %in[1] / ... / %in[N] + // the below code constructs a sequence of eval instructions + // - Load is omitted for simplicity + // + // %tmp = eval(ConcatF(%in[0], %in[1])) + // %tmp = eval(ConcatF(%tmp, %in[2])) + // ... + // %tmp = eval(ConcatF(%tmp, %in[N])) + // %out[0] = copy(%tmp) + // + for (uint32_t n = 1; n < input_objects.size(); ++n) + { + auto const left_feature = last_feature; + auto const left_shape = left_feature->layout()->shape(); + + auto right_feature = input_objects.at(n); + auto right_shape = right_feature->layout()->shape(); + + // Batch is not supported, yet + assert(left_feature->layout()->batch() == 1); + assert(right_feature->layout()->batch() == 1); + + // Height and Width SHOULD BE IDENTICAL for depth concat + assert(left_shape.height() == right_shape.height()); + assert(left_shape.width() == right_shape.width()); + + const uint32_t C = left_shape.depth() + right_shape.depth(); + const uint32_t H = left_shape.height(); + const uint32_t W = left_shape.width(); + + const nncc::core::ADT::feature::Shape out_shape{C, H, W}; + + auto out_bag = m->entity()->bag()->create(num_elements(out_shape)); + auto out_feature = m->entity()->object()->create(); + + out_feature->bag(out_bag); + out_feature->layout(coco::FeatureLayouts::BHWC::create(out_shape)); + + auto left_load = op_builder(m).load(left_feature).pop(); + auto right_load = op_builder(m).load(right_feature).pop(); + + auto concat_f = m->entity()->op()->create(); + + concat_f->axis(coco::ConcatF::Axis::Depth); + concat_f->left(left_load); + concat_f->right(right_load); + + auto eval = instr_builder(m).eval(out_feature, concat_f); + + // Append the constructed Shuffle instruction + blk->instr()->append(eval); + + // Update 'last_feature' + last_feature = out_feature; + } + + // Insert copy instruction from last_feature to output operand + int const ofm_idx = opoutputs.at(0); + auto const ofm_shape = tensor_context.shape(ofm_idx); + + auto ofm_bag = bags.bag(ofm_idx); + auto ofm_obj = m->entity()->object()->create(); + + ofm_obj->bag(ofm_bag); + ofm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); + + // Create a Copy instruction from last into ofm + auto copy_ins = instr_builder(m).copy(ofm_obj, last_feature); + + // Append the instruction + blk->instr()->append(copy_ins); +} + +} // namespace tflimport diff --git a/contrib/enco/frontend/tflite/src/Op/Concatenation.h b/contrib/enco/frontend/tflite/src/Op/Concatenation.h new file mode 100644 index 0000000..eb7625a --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Op/Concatenation.h @@ -0,0 +1,38 @@ +/* + * 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 __OP_CONCATENATION_H__ +#define __OP_CONCATENATION_H__ + +#include "GraphBuilder.h" + +#include + +namespace tflimport +{ + +/** + * @brief GraphBuilder for Concatenation operator + */ +class ConcatenationGraphBuilder : public GraphBuilder +{ +public: + void build(const tflite::Operator *op, GraphBuilderContext *) const override; +}; + +} // namespace tflimport + +#endif // __OP_CONCATENATION_H__ diff --git a/contrib/enco/frontend/tflite/src/Op/Conv2D.cpp b/contrib/enco/frontend/tflite/src/Op/Conv2D.cpp new file mode 100644 index 0000000..874c9ec --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Op/Conv2D.cpp @@ -0,0 +1,190 @@ +/* + * 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 "Conv2D.h" + +#include "Convert.h" +#include "IRBuilder.h" +#include "GraphBuilder.h" +#include "Padding.h" +#include "Activation.h" + +#include +#include +#include +#include + +#include +#include + +using namespace nncc::core::ADT; +using namespace morph::tflite; + +namespace tflimport +{ + +void Conv2DGraphBuilder::build(const tflite::Operator *op, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + // preparation + coco::Module *m = context->m(); + coco::Data *d = context->d(); + coco::Block *blk = context->block(); + TensorContext &tensor_context = context->tensor(); + TensorBags &bags = context->bags(); + TflBufferContext &buffer_context = context->buffer(); + const tflite::SubGraph *graph = context->graph(); + IndexVector opinputs = as_index_vector(op->inputs()); + IndexVector opoutputs = as_index_vector(op->outputs()); + + // these are fixed in tflite + // input index 0 : input feature + // input index 1 : kernel + // input index 2 : bias (optional) + bool hasBias = (opinputs.size() == 3); + assert(opinputs.size() == 2 || hasBias); + assert(opoutputs.size() == 1); + + int ifm_idx = opinputs.at(0); + int ker_idx = opinputs.at(1); + int ofm_idx = opoutputs.at(0); + + const tensor::Shape &ifm_shape = tensor_context.shape(ifm_idx); + const tensor::Shape &ofm_shape = tensor_context.shape(ofm_idx); + const tensor::Shape &ker_shape = tensor_context.shape(ker_idx); + + // Create an input feature map object + auto *ifm_obj = m->entity()->object()->create(); + auto *ifm_bag = bags.bag(ifm_idx); + ifm_obj->bag(ifm_bag); + ifm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ifm_shape))); + + // Create an an output feature map object + auto *ofm_obj = m->entity()->object()->create(); + auto *ofm_bag = bags.bag(ofm_idx); + ofm_obj->bag(ofm_bag); + ofm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); + + // Create an kernel object + auto *ker_obj = m->entity()->object()->create(); + auto *ker_bag = bags.bag(ker_idx); + ker_obj->bag(ker_bag); + ker_obj->layout(coco::KernelLayouts::NHWC::create(as_kernel_shape(ker_shape))); + + // Create a kernel overlay for the kernel object + // TODO : support for other types + d->f32()->allocate(ker_obj); + + TflBufferContext::TflBuffer buffer = buffer_context.tensor_buffer(graph, ker_idx); + + auto ker_spn = d->f32()->weight(ker_bag); + for (uint32_t idx = 0; idx < buffer.len; ++idx) + { + ker_spn[idx] = buffer.ptr[idx]; + } + + // Create a Load op + auto load = op_builder(m).load(ifm_obj).pop(); + + // Create a Conv2D op + auto coco_conv2d = m->entity()->op()->create(); + + // populating Conv2D objects and options such as stride and padding + coco_conv2d->ker(ker_obj); + + auto *conv_params = op->builtin_options_as_Conv2DOptions(); + + coco_conv2d->stride()->vertical(conv_params->stride_h()); + coco_conv2d->stride()->horizontal(conv_params->stride_w()); + + // conv_params->padding() to left, top, right, bottom + coco::Padding2D padding = conv2D_padding(conv_params, ifm_shape, ker_shape); + + coco_conv2d->pad()->top(padding.top()); + coco_conv2d->pad()->bottom(padding.bottom()); + coco_conv2d->pad()->left(padding.left()); + coco_conv2d->pad()->right(padding.right()); + + // Link ops + coco_conv2d->arg(load); + + // Object to store Conv2D output + auto *conv2d_obj = m->entity()->object()->create(); + auto *conv2d_bag = m->entity()->bag()->create(num_elements(ofm_shape)); + conv2d_obj->bag(conv2d_bag); + conv2d_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); + + // Create an Eval instruction for Conv2D + auto conv2d_ins = instr_builder(m).eval(conv2d_obj, coco_conv2d); + + // Append the instruction to the block + blk->instr()->append(conv2d_ins); + + // Last Object to make a copy to Output Object + coco::FeatureObject *last_obj = conv2d_obj; + + if (hasBias) + { + // When there is a bias, use btmp_obj as bias add output + // Bias is adding last_obj with bias weight values + auto *btmp_obj = m->entity()->object()->create(); + auto *btmp_bag = m->entity()->bag()->create(num_elements(ofm_shape)); + btmp_obj->bag(btmp_bag); + btmp_obj->layout(coco::FeatureLayouts::BHWC::create(ofm_obj->shape())); + + int bias_idx = opinputs.at(2); + + // Create an object for bias + auto bias_obj = m->entity()->object()->create(); + coco::Bag *bias_bag = bags.bag(bias_idx); + bias_obj->bag(bias_bag); + bias_obj->layout(coco::FeatureLayouts::BC::create(ofm_obj->shape())); + + // Fill bias data + d->f32()->allocate(bias_bag); + + auto bias_span = d->f32()->weight(bias_bag); + buffer = buffer_context.tensor_buffer(graph, bias_idx); + assert(buffer.ptr != nullptr && buffer.len > 0); + for (uint32_t idx = 0; idx < buffer.len; ++idx) + { + bias_span[idx] = buffer.ptr[idx]; + } + + // Create Op of conv2d output (last_obj) + bias values(bias_obj) + auto bias_add = op_builder(m).load(last_obj).load(bias_obj).add().pop(); + + // Create Instr as bias add result write to btmp_obj + auto bias_add_ins = instr_builder(m).eval(btmp_obj, bias_add); + + // Append the instruction + blk->instr()->append(bias_add_ins); + + // Update last_obj to btmp_obj + last_obj = btmp_obj; + } + + // fused activation + coco::FeatureObject *act_output = + build_activation(conv_params->fused_activation_function(), blk, last_obj); + + // Create Copy Instr of last_obj to Output Object + auto copy_ins = instr_builder(m).copy(ofm_obj, act_output); + blk->instr()->append(copy_ins); +} + +} // namespace tflimport diff --git a/contrib/enco/frontend/tflite/src/Op/Conv2D.h b/contrib/enco/frontend/tflite/src/Op/Conv2D.h new file mode 100644 index 0000000..c2f5eab --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Op/Conv2D.h @@ -0,0 +1,38 @@ +/* + * 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 __OP_CONV2D_H__ +#define __OP_CONV2D_H__ + +#include "GraphBuilder.h" + +#include + +namespace tflimport +{ + +/** + * @brief GraphBuilder for Conv2D operator + */ +class Conv2DGraphBuilder : public GraphBuilder +{ +public: + void build(const tflite::Operator *op, GraphBuilderContext *context) const override; +}; + +} // namespace tflimport + +#endif // __OP_CONV2D_H__ diff --git a/contrib/enco/frontend/tflite/src/Op/MaxPool2D.cpp b/contrib/enco/frontend/tflite/src/Op/MaxPool2D.cpp new file mode 100644 index 0000000..176a177 --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Op/MaxPool2D.cpp @@ -0,0 +1,109 @@ +/* + * 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 "MaxPool2D.h" + +#include "Convert.h" +#include "IRBuilder.h" +#include "GraphBuilder.h" +#include "Padding.h" +#include "Activation.h" + +#include +#include +#include + +#include +#include + +using namespace nncc::core::ADT; +using namespace morph::tflite; + +namespace tflimport +{ + +void MaxPool2DGraphBuilder::build(const tflite::Operator *op, GraphBuilderContext *context) const +{ + assert(context != nullptr); // check if init(..) is called + + coco::Module *m = context->m(); + coco::Block *blk = context->block(); + TensorContext &tensor_context = context->tensor(); + TensorBags &bags = context->bags(); + + IndexVector opinputs = as_index_vector(op->inputs()); + IndexVector opoutputs = as_index_vector(op->outputs()); + + // these are fixed in tflite + // input index 0 : input feature + // output index 0 : output feature + assert(opinputs.size() == 1); + assert(opoutputs.size() == 1); + + int ifm_idx = opinputs.at(0); + int ofm_idx = opoutputs.at(0); + + const tensor::Shape &ifm_shape = tensor_context.shape(ifm_idx); + const tensor::Shape &ofm_shape = tensor_context.shape(ofm_idx); + + // Create an object for an input feature map + coco::FeatureObject *ifm_obj = m->entity()->object()->create(); + coco::Bag *ifm_bag = bags.bag(ifm_idx); + ifm_obj->bag(ifm_bag); + ifm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ifm_shape))); + + // Create an object for an output feature map + coco::FeatureObject *ofm_obj = m->entity()->object()->create(); + coco::Bag *ofm_bag = bags.bag(ofm_idx); + ofm_obj->bag(ofm_bag); + ofm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); + + // Create a Load op + coco::Op *coco_load = op_builder(m).load(ifm_obj).pop(); + + // Create a MaxPool2D + coco::MaxPool2D *coco_maxpool2d = m->entity()->op()->create(); + const tflite::Pool2DOptions *params = op->builtin_options_as_Pool2DOptions(); + + coco_maxpool2d->window()->height(params->filter_height()); + coco_maxpool2d->window()->width(params->filter_width()); + + coco_maxpool2d->stride()->vertical(params->stride_h()); + coco_maxpool2d->stride()->horizontal(params->stride_w()); + + coco::Padding2D padding = + pool2D_padding(params, ifm_shape, params->filter_width(), params->filter_height()); + + coco_maxpool2d->pad()->top(padding.top()); + coco_maxpool2d->pad()->bottom(padding.bottom()); + coco_maxpool2d->pad()->left(padding.left()); + coco_maxpool2d->pad()->right(padding.right()); + + // Link ops + coco_maxpool2d->arg(coco_load); + + // Create an Eval instruction + coco::Eval *ins = instr_builder(m).eval(ofm_obj, coco_maxpool2d); + + // Append the instruction to the block + blk->instr()->append(ins); + + // TODO activation, e.g., relu + assert(params->fused_activation_function() == + tflite::ActivationFunctionType::ActivationFunctionType_NONE); +} + +} // namespace tflimport diff --git a/contrib/enco/frontend/tflite/src/Op/MaxPool2D.h b/contrib/enco/frontend/tflite/src/Op/MaxPool2D.h new file mode 100644 index 0000000..095085f --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Op/MaxPool2D.h @@ -0,0 +1,38 @@ +/* + * 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 __OP_MAXPOOL2D_H__ +#define __OP_MAXPOOL2D_H__ + +#include "GraphBuilder.h" + +#include + +namespace tflimport +{ + +/** + * @brief GraphBuilder for AvgPool2D operator + */ +class MaxPool2DGraphBuilder : public GraphBuilder +{ +public: + void build(const tflite::Operator *op, GraphBuilderContext *) const override; +}; + +} // namespace tflimport + +#endif // __OP_MAXPOOL2D_H__ diff --git a/contrib/enco/frontend/tflite/src/Op/Padding.cpp b/contrib/enco/frontend/tflite/src/Op/Padding.cpp new file mode 100644 index 0000000..7edaee9 --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Op/Padding.cpp @@ -0,0 +1,80 @@ +/* + * 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 "Padding.h" + +#include "Convert.h" +#include "TensorBags.h" + +#include +#include + +#include +#include + +#include +#include + +using namespace nncc::core::ADT; + +namespace tflimport +{ + +coco::Padding2D get_padding(const tensor::Shape &ifm_shape, const int kernel_w, const int kernel_h, + tflite::Padding padding, int stride_w, int stride_h, + int dilation_w_factor, int dilation_h_factor) +{ + assert(ifm_shape.rank() == 4); + + auto compute_padding = [](tflite::Padding padding, int stride, int dilation_rate, int in_size, + int filter_size) { + // code from tensorflow lite 1.9 + int effective_filter_size = (filter_size - 1) * dilation_rate + 1; + int out_size = (padding == tflite::Padding_SAME) + ? (in_size + stride - 1) / stride + : (padding == tflite::Padding_VALID) + ? (in_size - effective_filter_size + stride) / stride + : 0; + int value = ((out_size - 1) * stride + effective_filter_size - in_size) / 2; + return value > 0 ? value : 0; + }; + + // ifm shape is from order of NHWC. ifm W = dim(2), ifm H = dim(1) + int padding_w = compute_padding(padding, stride_w, dilation_w_factor, ifm_shape.dim(2), kernel_w); + int padding_h = compute_padding(padding, stride_h, dilation_h_factor, ifm_shape.dim(1), kernel_h); + + coco::Padding2D coco_padding; + coco_padding.top(padding_h).bottom(padding_h).left(padding_w).right(padding_w); + + return coco_padding; +} + +coco::Padding2D pool2D_padding(const tflite::Pool2DOptions *options, const tensor::Shape &ifm_shape, + const int filter_w, const int filter_h) +{ + return get_padding(ifm_shape, filter_w, filter_h, options->padding(), options->stride_w(), + options->stride_h(), 1, 1); +} + +coco::Padding2D conv2D_padding(const tflite::Conv2DOptions *options, const tensor::Shape &ifm_shape, + const tensor::Shape &kernel_shape) +{ + return get_padding(ifm_shape, kernel_shape.dim(2), kernel_shape.dim(1), /* kernel layout: NHWC */ + options->padding(), options->stride_w(), options->stride_h(), + options->dilation_w_factor(), options->dilation_h_factor()); +} + +} // namespace tflimport diff --git a/contrib/enco/frontend/tflite/src/Op/Padding.h b/contrib/enco/frontend/tflite/src/Op/Padding.h new file mode 100644 index 0000000..dc65aa8 --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Op/Padding.h @@ -0,0 +1,38 @@ +/* + * 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 __OP_PADDING_H__ +#define __OP_PADDING_H__ + +#include +#include + +#include + +using namespace nncc::core::ADT; + +namespace tflimport +{ + +coco::Padding2D pool2D_padding(const tflite::Pool2DOptions *options, const tensor::Shape &ifm_shape, + const int filter_w, const int filter_h); + +coco::Padding2D conv2D_padding(const tflite::Conv2DOptions *options, const tensor::Shape &ifm_shape, + const tensor::Shape &kernel_shape); + +} // namespace tflimport + +#endif // __OP_PADDING_H__ diff --git a/contrib/enco/frontend/tflite/src/Op/ReLU.cpp b/contrib/enco/frontend/tflite/src/Op/ReLU.cpp new file mode 100644 index 0000000..06cdf1d --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Op/ReLU.cpp @@ -0,0 +1,87 @@ +/* + * 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 "ReLU.h" + +#include "IRBuilder.h" +#include "GraphBuilder.h" + +#include +#include +#include + +#include +#include + +using namespace nncc::core::ADT; +using namespace morph::tflite; + +namespace tflimport +{ + +void ReLUGraphBuilder::build(const tflite::Operator *op, GraphBuilderContext *context) const +{ + assert(context != nullptr); // check if init(..) is called + + coco::Module *m = context->m(); + coco::Block *blk = context->block(); + TensorContext &tensor_context = context->tensor(); + TensorBags &bags = context->bags(); + + IndexVector opinputs = as_index_vector(op->inputs()); + IndexVector opoutputs = as_index_vector(op->outputs()); + + // these are fixed in tflite + // input index 0 : input feature + // output index 0 : output feature + assert(opinputs.size() == 1); + assert(opoutputs.size() == 1); + + auto ifm_idx = opinputs.at(0); + auto ofm_idx = opoutputs.at(0); + + const tensor::Shape &ifm_shape = tensor_context.shape(ifm_idx); + const tensor::Shape &ofm_shape = tensor_context.shape(ofm_idx); + + // Create an object for an input feature map + coco::FeatureObject *ifm_obj = m->entity()->object()->create(); + coco::Bag *ifm_bag = bags.bag(ifm_idx); + ifm_obj->bag(ifm_bag); + ifm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ifm_shape))); + + // Create an object for an output feature map + coco::FeatureObject *ofm_obj = m->entity()->object()->create(); + coco::Bag *ofm_bag = bags.bag(ofm_idx); + ofm_obj->bag(ofm_bag); + ofm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); + + // Create a Load op + auto coco_load = op_builder(m).load(ifm_obj).pop(); + + // Create a ReLU + auto coco_relu = m->entity()->op()->create(); + + // Link ops + coco_relu->arg(coco_load); + + // Create an Eval instruction + auto eval_ins = instr_builder(m).eval(ofm_obj, coco_relu); + + // Append the instruction to the block + blk->instr()->append(eval_ins); +} + +} // namespace tflimport diff --git a/contrib/enco/frontend/tflite/src/Op/ReLU.h b/contrib/enco/frontend/tflite/src/Op/ReLU.h new file mode 100644 index 0000000..c78400d --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Op/ReLU.h @@ -0,0 +1,38 @@ +/* + * 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 __OP_RELU_H__ +#define __OP_RELU_H__ + +#include "GraphBuilder.h" + +#include + +namespace tflimport +{ + +/** + * @brief GraphBuilder for ReLU operator + */ +class ReLUGraphBuilder : public GraphBuilder +{ +public: + void build(const tflite::Operator *op, GraphBuilderContext *) const override; +}; + +} // namespace tflimport + +#endif // __OP_RELU_H__ diff --git a/contrib/enco/frontend/tflite/src/Op/ReLU6.cpp b/contrib/enco/frontend/tflite/src/Op/ReLU6.cpp new file mode 100644 index 0000000..8a36e57 --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Op/ReLU6.cpp @@ -0,0 +1,87 @@ +/* + * 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 "ReLU6.h" + +#include "IRBuilder.h" +#include "GraphBuilder.h" + +#include +#include +#include + +#include +#include + +using namespace nncc::core::ADT; +using namespace morph::tflite; + +namespace tflimport +{ + +void ReLU6GraphBuilder::build(const tflite::Operator *op, GraphBuilderContext *context) const +{ + assert(context != nullptr); // check if init(..) is called + + coco::Module *m = context->m(); + coco::Block *blk = context->block(); + TensorContext &tensor_context = context->tensor(); + TensorBags &bags = context->bags(); + + IndexVector opinputs = as_index_vector(op->inputs()); + IndexVector opoutputs = as_index_vector(op->outputs()); + + // these are fixed in tflite + // input index 0 : input feature + // output index 0 : output feature + assert(opinputs.size() == 1); + assert(opoutputs.size() == 1); + + int ifm_idx = opinputs.at(0); + int ofm_idx = opoutputs.at(0); + + const tensor::Shape &ifm_shape = tensor_context.shape(ifm_idx); + const tensor::Shape &ofm_shape = tensor_context.shape(ofm_idx); + + // Create an object for an input feature map + coco::FeatureObject *ifm_obj = m->entity()->object()->create(); + coco::Bag *ifm_bag = bags.bag(ifm_idx); + ifm_obj->bag(ifm_bag); + ifm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ifm_shape))); + + // Create an object for an output feature map + coco::FeatureObject *ofm_obj = m->entity()->object()->create(); + coco::Bag *ofm_bag = bags.bag(ofm_idx); + ofm_obj->bag(ofm_bag); + ofm_obj->layout(coco::FeatureLayouts::BHWC::create(as_feature_shape(ofm_shape))); + + // Create a Load op + auto coco_load = op_builder(m).load(ifm_obj).pop(); + + // Create a ReLU6 + auto coco_relu6 = m->entity()->op()->create(); + + // Link ops + coco_relu6->arg(coco_load); + + // Create an Eval instruction + auto eval_ins = instr_builder(m).eval(ofm_obj, coco_relu6); + + // Append the instruction to the block + blk->instr()->append(eval_ins); +} + +} // namespace tflimport diff --git a/contrib/enco/frontend/tflite/src/Op/ReLU6.h b/contrib/enco/frontend/tflite/src/Op/ReLU6.h new file mode 100644 index 0000000..10bcd4f --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Op/ReLU6.h @@ -0,0 +1,38 @@ +/* + * 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 __OP_RELU6_H__ +#define __OP_RELU6_H__ + +#include "GraphBuilder.h" + +#include + +namespace tflimport +{ + +/** + * @brief GraphBuilder for ReLU6 operator + */ +class ReLU6GraphBuilder : public GraphBuilder +{ +public: + void build(const tflite::Operator *op, GraphBuilderContext *) const override; +}; + +} // namespace tflimport + +#endif // __OP_RELU6_H__ diff --git a/contrib/enco/frontend/tflite/src/Op/Reshape.cpp b/contrib/enco/frontend/tflite/src/Op/Reshape.cpp new file mode 100644 index 0000000..355dfc5 --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Op/Reshape.cpp @@ -0,0 +1,87 @@ +/* + * 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 "Reshape.h" + +#include "IRBuilder.h" +#include "GraphBuilder.h" + +#include +#include +#include + +#include +#include + +using namespace nncc::core::ADT; +using namespace morph::tflite; + +namespace tflimport +{ + +void ReshapeGraphBuilder::build(const tflite::Operator *op, GraphBuilderContext *context) const +{ + assert(context != nullptr); // check if init(..) is called + + coco::Module *m = context->m(); + coco::Block *blk = context->block(); + TensorBags &bags = context->bags(); + + IndexVector opinputs = as_index_vector(op->inputs()); + IndexVector opoutputs = as_index_vector(op->outputs()); + + // these are fixed in tflite + // input index 0 : input feature + // input index 1 : output shape (int32_t), (optional or not, is not clear) + // output index 0 : output feature + assert(opinputs.size() == 1 || opinputs.size() == 2); + assert(opoutputs.size() == 1); + + // Note: there are actually 3 places where we can get output shape from + // current TF lite implementation. From output operand shape, second input, + // and ReshapeOption (new_shape). Here we use output operand shape + int ifm_idx = opinputs.at(0); + int ofm_idx = opoutputs.at(0); + + auto ifm_bag = bags.bag(ifm_idx); + auto ofm_bag = bags.bag(ofm_idx); + + // TODO: move to InstrBuilder as 'shuffle_elements()' + // Create a 1:1 shuffle instruction from ifm into ofm + // Note: Reshape is change of shape information and there is no value change + // in the bag itself. We implement this as just make a element wise copy of + // the bag from input to output. So there is no need of 'reshape' operator + auto shuffle_ins = m->entity()->instr()->create(); + auto num_elem = ifm_bag->size(); + + assert(num_elem == ofm_bag->size()); + + shuffle_ins->from(ifm_bag); + shuffle_ins->into(ofm_bag); + + for (uint32_t n = 0; n < num_elem; ++n) + { + const auto from = coco::ElemID(n); + const auto into = coco::ElemID(n); + + shuffle_ins->insert(from, into); + } + + // Append the instruction + blk->instr()->append(shuffle_ins); +} + +} // namespace tflimport diff --git a/contrib/enco/frontend/tflite/src/Op/Reshape.h b/contrib/enco/frontend/tflite/src/Op/Reshape.h new file mode 100644 index 0000000..7447b56 --- /dev/null +++ b/contrib/enco/frontend/tflite/src/Op/Reshape.h @@ -0,0 +1,38 @@ +/* + * 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 __OP_RESHAPE_H__ +#define __OP_RESHAPE_H__ + +#include "GraphBuilder.h" + +#include + +namespace tflimport +{ + +/** + * @brief GraphBuilder for Reshape operator + */ +class ReshapeGraphBuilder : public GraphBuilder +{ +public: + void build(const tflite::Operator *op, GraphBuilderContext *) const override; +}; + +} // namespace tflimport + +#endif // __OP_RESHAPE_H__ diff --git a/contrib/enco/frontend/tflite/src/TensorBags.h b/contrib/enco/frontend/tflite/src/TensorBags.h new file mode 100644 index 0000000..84bec0b --- /dev/null +++ b/contrib/enco/frontend/tflite/src/TensorBags.h @@ -0,0 +1,60 @@ +/* + * 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 __TENSOR_BAGS_H__ +#define __TENSOR_BAGS_H__ + +#include "Convert.h" + +#include +#include + +#include + +#include + +using namespace nncc::core::ADT; + +namespace tflimport +{ + +/** + * @brief Pre-creates coco:Bags for each operands(tensors) + */ +class TensorBags +{ +public: + void prepare(const tflite::SubGraph *graph, std::unique_ptr &m) + { + for (uint32_t tensor_id = 0; tensor_id < graph->tensors()->size(); ++tensor_id) + { + auto const tensor_info = graph->tensors()->Get(tensor_id); + auto const tensor_shape = as_tensor_shape(tensor_info->shape()); + auto const tensor_bag = m->entity()->bag()->create(num_elements(tensor_shape)); + + _bag_ctx[tensor_id] = tensor_bag; + } + } + + coco::Bag *bag(int32_t tensor_id) { return _bag_ctx[tensor_id]; } + +private: + std::map _bag_ctx; +}; + +} // namespace tflimport + +#endif // __TENSOR_BAGS_H__ -- 2.7.4