diff options
Diffstat (limited to 'compiler/luci')
240 files changed, 15569 insertions, 0 deletions
diff --git a/compiler/luci/CMakeLists.txt b/compiler/luci/CMakeLists.txt new file mode 100644 index 000000000..387c22487 --- /dev/null +++ b/compiler/luci/CMakeLists.txt @@ -0,0 +1,10 @@ +add_subdirectory(log) +add_subdirectory(lang) +add_subdirectory(service) +add_subdirectory(pass) +add_subdirectory(logex) +add_subdirectory(import) +add_subdirectory(export) +add_subdirectory(tester) + +add_subdirectory(tests) diff --git a/compiler/luci/README.md b/compiler/luci/README.md new file mode 100644 index 000000000..49c833121 --- /dev/null +++ b/compiler/luci/README.md @@ -0,0 +1,3 @@ +# luci + +_luci_ provides IR for TFLite/Circle and Graph from FlatBuffer. diff --git a/compiler/luci/export/CMakeLists.txt b/compiler/luci/export/CMakeLists.txt new file mode 100644 index 000000000..e32eca366 --- /dev/null +++ b/compiler/luci/export/CMakeLists.txt @@ -0,0 +1,29 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +# TODO enable tests +#file(GLOB_RECURSE TESTS "src/*.test.cpp") +#list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(luci_export SHARED ${SOURCES}) +target_include_directories(luci_export PRIVATE src) +target_include_directories(luci_export PUBLIC include) +target_link_libraries(luci_export PRIVATE luci_lang) +target_link_libraries(luci_export PRIVATE luci_service) +target_link_libraries(luci_export PRIVATE luci_pass) +target_link_libraries(luci_export PRIVATE mio_circle) +target_link_libraries(luci_export PRIVATE luci_log) +target_link_libraries(luci_export PRIVATE luci_logex) +target_link_libraries(luci_export PRIVATE nncc_common) +target_link_libraries(luci_export PRIVATE locop) +target_link_libraries(luci_export PRIVATE oops) +install(TARGETS luci_export DESTINATION lib) + +#if(NOT ENABLE_TEST) +# return() +#endif(NOT ENABLE_TEST) +# +#nnas_find_package(GTest REQUIRED) +# +#GTest_AddTest(luci_export_test ${TESTS}) +#target_include_directories(luci_export_test PRIVATE src) +#target_link_libraries(luci_export_test luci_export) +#target_link_libraries(luci_export_test oops) diff --git a/compiler/luci/export/README.md b/compiler/luci/export/README.md new file mode 100644 index 000000000..12b190a2f --- /dev/null +++ b/compiler/luci/export/README.md @@ -0,0 +1,3 @@ +# luci-export + +_luci-export_ provides exporting _loco_ graph of Circle IR to Circle model file diff --git a/compiler/luci/export/include/luci/CircleExporter.h b/compiler/luci/export/include/luci/CircleExporter.h new file mode 100644 index 000000000..0584c623c --- /dev/null +++ b/compiler/luci/export/include/luci/CircleExporter.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020 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 __LUCI_CIRCLEEXPORTER_H__ +#define __LUCI_CIRCLEEXPORTER_H__ + +#include <luci/IR/Module.h> + +#include <loco.h> + +#include <memory> + +namespace luci +{ + +class CircleExporter +{ +public: + // This contract class describes the interaction between a exporter and its client. + struct Contract + { + public: + virtual ~Contract() = default; + + public: // Client -> Exporter + // Input Graph (to be exported) + // Exporter expects a loco graph that consists of Circle nodes + virtual loco::Graph *graph(void) const = 0; + + // Input Module (to be exported) + // Exporter expects a luci module that consists of loco graphs + // TODO make this pure virtual + virtual luci::Module *module(void) const; + + public: // Exporter -> Client + // Exporter calls store for export data + // Notice: Please DO NOT STORE ptr and size when implementing this in Client + virtual bool store(const char *ptr, const size_t size) const = 0; + }; + +public: + explicit CircleExporter(); + +public: + // invoke(...) returns false on failure. + bool invoke(Contract *) const; +}; + +} // namespace luci + +#endif // __LUCI_CIRCLEEXPORTER_H__ diff --git a/compiler/luci/export/src/Check.h b/compiler/luci/export/src/Check.h new file mode 100644 index 000000000..e05ec904a --- /dev/null +++ b/compiler/luci/export/src/Check.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 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 __CHECK_H__ +#define __CHECK_H__ + +#include <stdexcept> +#include <cassert> +#include <iostream> + +// TODO Add macro for Release version + +#define LUCI_ASSERT(condition, msg) \ + { \ + if (!(condition)) \ + { \ + std::cerr << "[assert failed] " << (msg) << ". " << std::endl; \ + assert((condition)); \ + } \ + } + +#endif // __CHECK_H__ diff --git a/compiler/luci/export/src/CircleExporter.cpp b/compiler/luci/export/src/CircleExporter.cpp new file mode 100644 index 000000000..125df7802 --- /dev/null +++ b/compiler/luci/export/src/CircleExporter.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020 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 "luci/CircleExporter.h" +#include "luci/IR/Module.h" +#include "CircleExporterImpl.h" + +#include <oops/InternalExn.h> + +#include <fstream> +#include <memory> + +namespace luci +{ + +// TODO remove this +Module *CircleExporter::Contract::module(void) const { return nullptr; } + +CircleExporter::CircleExporter() +{ + // NOTHING TO DO +} + +bool CircleExporter::invoke(Contract *contract) const +{ + auto module = contract->module(); + if (module != nullptr) + { + CircleExporterImpl impl(module); + + const char *ptr = impl.getBufferPointer(); + const size_t size = impl.getBufferSize(); + + // we just send one time + return contract->store(ptr, size); + } + + auto graph = contract->graph(); + if (graph == nullptr) + return false; + + CircleExporterImpl impl(graph); + + const char *ptr = impl.getBufferPointer(); + const size_t size = impl.getBufferSize(); + + // we just send one time + return contract->store(ptr, size); +} + +} // namespace luci diff --git a/compiler/luci/export/src/CircleExporterImpl.cpp b/compiler/luci/export/src/CircleExporterImpl.cpp new file mode 100644 index 000000000..81109ee62 --- /dev/null +++ b/compiler/luci/export/src/CircleExporterImpl.cpp @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2020 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 "CircleExporterImpl.h" +#include "Optimize.h" +#include "CircleTensorExporter.h" +#include "CircleOperationExporter.h" +#include "CircleExporterUtils.h" + +#include <oops/InternalExn.h> +#include <mio/circle/schema_generated.h> +#include <flatbuffers/flatbuffers.h> + +#include <cassert> +#include <unordered_map> +#include <string> +#include <stdexcept> + +namespace +{ + +luci::CircleInput *input_node(loco::Graph *g, const loco::GraphInputIndex &index) +{ + for (uint32_t n = 0; n < g->nodes()->size(); ++n) + { + if (auto pull = dynamic_cast<luci::CircleInput *>(g->nodes()->at(n))) + { + if (pull->indexed() && pull->index() == index) + { + return pull; + } + } + } + return nullptr; +} + +luci::CircleOutput *output_node(loco::Graph *g, const loco::GraphOutputIndex &index) +{ + for (uint32_t n = 0; n < g->nodes()->size(); ++n) + { + if (auto push = dynamic_cast<luci::CircleOutput *>(g->nodes()->at(n))) + { + if (push->indexed() && push->index() == index) + { + return push; + } + } + } + return nullptr; +} + +void registerGraphInputTensors(loco::Graph *graph, luci::SubGraphContext &ctx) +{ + for (uint32_t n = 0; n < graph->inputs()->size(); ++n) + { + auto node = input_node(graph, n); + assert(node != nullptr); + ctx._inputs.push_back(luci::get_tensor_index(node)); + } +} + +void registerGraphOutputTensors(loco::Graph *graph, luci::SubGraphContext &ctx) +{ + for (uint32_t n = 0; n < graph->outputs()->size(); ++n) + { + auto push = output_node(graph, n); + assert(push != nullptr); + auto node = push->from(); + assert(node != nullptr); + ctx._outputs.push_back(luci::get_tensor_index(node)); + } +} + +} // namespace + +namespace +{ + +using namespace circle; +using namespace flatbuffers; + +Offset<Vector<Offset<OperatorCode>>> +encodeOperatorCodes(FlatBufferBuilder &builder, std::unordered_map<luci::OpCode, uint32_t> &opcodes, + std::unordered_map<luci::OpCode, std::string> &custom_opcodes) +{ + std::vector<Offset<OperatorCode>> operator_codes_vec(opcodes.size()); + for (auto it : opcodes) + { + uint32_t idx = it.second; + if (it.first.opcode != BuiltinOperator_CUSTOM) + { + operator_codes_vec[idx] = CreateOperatorCode(builder, it.first.opcode); + } + else // custom op + { + auto opCode = it.first; + auto custom_code = custom_opcodes.find(opCode); + if (custom_code == custom_opcodes.end()) + INTERNAL_EXN("Cannot find code for customop even though opcode is BuiltinOperator_CUSTOM"); + + operator_codes_vec[idx] = + CreateOperatorCode(builder, it.first.opcode, builder.CreateString(custom_code->second)); + } + } + return builder.CreateVector(operator_codes_vec); +} + +} // namespace + +namespace luci +{ + +using namespace circle; +using namespace flatbuffers; + +CircleExporterImpl::CircleExporterImpl(loco::Graph *graph) { exportGraph(graph); } +CircleExporterImpl::CircleExporterImpl(Module *module) { exportModule(module); } + +::flatbuffers::Offset<::circle::SubGraph> +CircleExporterImpl::exportSubgraph(SerializedGraphData &gd) +{ + auto tensors = _builder.CreateVector(gd._tensors); + auto inputs = _builder.CreateVector(gd._inputs); + auto outputs = _builder.CreateVector(gd._outputs); + auto operators = _builder.CreateVector(gd._operators); + auto df = gd._data_format; + auto subgraph = CreateSubGraph(_builder, tensors, inputs, outputs, operators, df); + return subgraph; +} + +void CircleExporterImpl::exportGraph(loco::Graph *graph) +{ + // do graph optimization + optimize(graph); + + _builder.Clear(); + + SerializedModelData md; + SerializedGraphData gd; + + // This version is taken from comment in fbs + constexpr uint32_t version = 0; + + // TODO set this value properly + gd._data_format = circle::DataFormat::DataFormat_CHANNELS_LAST; + + // prepare model data + prepareModelData(_builder, md); + + // parse graph into SerializedModelData structure + exportOpDefinedTensors(graph, _builder, md, gd); + + // NOTE Invoke these register functions only after each node is annotated with its tensor_index + registerGraphInputTensors(graph, gd); + registerGraphOutputTensors(graph, gd); + + exportNodes(graph, _builder, md, gd); + + // encode operator codes + auto operator_codes = + encodeOperatorCodes(_builder, md._operator_codes, md._custom_operator_codes); + + // Subgraphs + Offset<SubGraph> subgraph = exportSubgraph(gd); + auto subgraphs = _builder.CreateVector(std::vector<Offset<SubGraph>>{subgraph}); + + // Description + std::string description_str = "nnpackage"; + auto description = _builder.CreateString(description_str); + + // create array of buffers + auto buffers = _builder.CreateVector(md._buffers); + + // empty metadata + std::vector<int> metadata_buffer_vec; + auto metadata_buffer = _builder.CreateVector(metadata_buffer_vec); + + // Model + auto model_offset = CreateModel(_builder, version, operator_codes, subgraphs, description, + buffers, metadata_buffer); + FinishModelBuffer(_builder, model_offset); +} + +void CircleExporterImpl::exportModule(Module *module) +{ + assert(module->size() > 0); + // do graph optimization + + SerializedModelData md; + + _builder.Clear(); + + std::vector<flatbuffers::Offset<circle::SubGraph>> subgraph_vec; + + for (size_t g = 0; g < module->size(); ++g) + { + auto graph = module->graph(g); + + optimize(graph); + + SerializedGraphData gd; + + // TODO set this value properly + gd._data_format = circle::DataFormat::DataFormat_CHANNELS_LAST; + + // parse graph into SerializedModelData structure + exportOpDefinedTensors(graph, _builder, md, gd); + + // NOTE Invoke these register functions only after each node is annotated with its tensor_index + registerGraphInputTensors(graph, gd); + registerGraphOutputTensors(graph, gd); + + exportNodes(graph, _builder, md, gd); + + // Subgraphs + Offset<SubGraph> subgraph = exportSubgraph(gd); + subgraph_vec.push_back(subgraph); + } + + auto subgraphs = _builder.CreateVector(std::vector<Offset<SubGraph>>{subgraph_vec}); + + // encode operator codes + auto operator_codes = + encodeOperatorCodes(_builder, md._operator_codes, md._custom_operator_codes); + + // Description + std::string description_str = "nnpackage"; + auto description = _builder.CreateString(description_str); + + // create array of buffers + auto buffers = _builder.CreateVector(md._buffers); + + // empty metadata + std::vector<int> metadata_buffer_vec; + auto metadata_buffer = _builder.CreateVector(metadata_buffer_vec); + + // This version is taken from comment in fbs + constexpr uint32_t version = 0; + + // Model + auto model_offset = CreateModel(_builder, version, operator_codes, subgraphs, description, + buffers, metadata_buffer); + FinishModelBuffer(_builder, model_offset); +} + +const char *CircleExporterImpl::getBufferPointer() const +{ + return reinterpret_cast<const char *>(_builder.GetBufferPointer()); +} + +size_t CircleExporterImpl::getBufferSize() const { return _builder.GetSize(); } + +} // namespace luci diff --git a/compiler/luci/export/src/CircleExporterImpl.h b/compiler/luci/export/src/CircleExporterImpl.h new file mode 100644 index 000000000..e5d5b5a00 --- /dev/null +++ b/compiler/luci/export/src/CircleExporterImpl.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2020 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 __CIRCLE_EXPORTER_IMPL_H__ +#define __CIRCLE_EXPORTER_IMPL_H__ + +#include "luci/CircleExporter.h" +#include "luci/IR/Module.h" + +#include "SerializedData.h" + +#include "SerializedData.h" + +#include <mio/circle/schema_generated.h> + +#include <loco.h> + +namespace luci +{ + +/** + * internal implementation of interface exporter class + */ +class CircleExporterImpl +{ +public: + CircleExporterImpl() = delete; + ~CircleExporterImpl() = default; + + explicit CircleExporterImpl(loco::Graph *graph); + explicit CircleExporterImpl(Module *module); + + /** + * @return pointer to buffer with serialized graph + */ + const char *getBufferPointer() const; + + /** + * @return size of buffer with serialized graph + */ + size_t getBufferSize() const; + +private: + /** + * @brief create Subgraph using data stored in SerializedGraphData + * @param gd information about serializer parts of model + * @return offset in buffer corresponding to serialized subgraph + */ + flatbuffers::Offset<circle::SubGraph> exportSubgraph(SerializedGraphData &gd); + + /** + * @brief root function that writes graph into internal buffer + * @param graph + */ + void exportGraph(loco::Graph *graph); + + /** + * @brief root function that writes Module into internal buffer + * @param module + */ + void exportModule(Module *module); + +private: + flatbuffers::FlatBufferBuilder _builder; +}; + +} // namespace luci + +#endif // __CIRCLE_EXPORTER_IMPL_H__ diff --git a/compiler/luci/export/src/CircleExporterUtils.cpp b/compiler/luci/export/src/CircleExporterUtils.cpp new file mode 100644 index 000000000..1272facb2 --- /dev/null +++ b/compiler/luci/export/src/CircleExporterUtils.cpp @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2020 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 "CircleExporterUtils.h" + +#include <oops/InternalExn.h> + +#include <cassert> +#include <memory> + +namespace luci +{ + +circle::ActivationFunctionType to_circle_actfunc(luci::FusedActFunc func) +{ + switch (func) + { + case luci::FusedActFunc::NONE: + return circle::ActivationFunctionType_NONE; + case luci::FusedActFunc::RELU: + return circle::ActivationFunctionType_RELU; + case luci::FusedActFunc::RELU_N1_TO_1: + return circle::ActivationFunctionType_RELU_N1_TO_1; + case luci::FusedActFunc::RELU6: + return circle::ActivationFunctionType_RELU6; + default: + INTERNAL_EXN_V("trying to convert unsupported luci::FusedActFunc", oops::to_uint32(func)); + } +} + +circle::TensorType to_circle_tensortype(loco::DataType type) +{ + switch (type) + { + case loco::DataType::U8: + return circle::TensorType_UINT8; + + case loco::DataType::S8: + return circle::TensorType_INT8; + case loco::DataType::S16: + return circle::TensorType_INT16; + case loco::DataType::S32: + return circle::TensorType_INT32; + case loco::DataType::S64: + return circle::TensorType_INT64; + + case loco::DataType::FLOAT16: + return circle::TensorType_FLOAT16; + case loco::DataType::FLOAT32: + return circle::TensorType_FLOAT32; + + case loco::DataType::BOOL: + return circle::TensorType_BOOL; + + default: + INTERNAL_EXN_V("failed to convert unsupported loco::DataType", oops::to_uint32(type)); + } +} + +} // namespace luci + +namespace luci +{ + +uint32_t SerializedModelData::registerBuiltinOpcode(circle::BuiltinOperator builtin_code) +{ + auto it = _operator_codes.find(OpCode{builtin_code}); + if (it != _operator_codes.end()) + { + return it->second; + } + auto idx = static_cast<uint32_t>(_operator_codes.size()); + _operator_codes.emplace(OpCode{builtin_code}, idx); + return idx; +} + +uint32_t SerializedModelData::registerCustomOpcode(const std::string &custom_op) +{ + circle::BuiltinOperator custom_code = circle::BuiltinOperator_CUSTOM; + auto idx = registerBuiltinOpcode(custom_code); + _custom_operator_codes.emplace(OpCode{custom_code}, custom_op); + return idx; +} + +circle::Padding getOpPadding(const loco::Padding2D *pad, const loco::Stride<2> *stride, + const ShapeDescription &ifm, const ShapeDescription &ofm) +{ + // VALID padding + if (pad->top() == 0 && pad->bottom() == 0 && pad->left() == 0 && pad->right() == 0) + return circle::Padding_VALID; + + // SAME padding + // + // For same padding, by definition, following equation should hold: + // O = floor((I - 1) / S) + 1 + // where input size I, output size O, stride S + // + // NOTE input and output 'feature' map are shape of NHWC + bool same_padding_criterion_1 = + (static_cast<uint32_t>(ofm._dims[1]) == (ifm._dims[1] - 1) / stride->vertical() + 1) && + (static_cast<uint32_t>(ofm._dims[2]) == (ifm._dims[2] - 1) / stride->horizontal() + 1); + + // For same padding, rear padding is same or bigger than front padding by at most 1 + bool same_padding_criterion_2 = + (pad->top() <= pad->bottom()) && (pad->bottom() <= pad->top() + 1) && + (pad->left() <= pad->right()) && (pad->right() <= pad->left() + 1); + + if (same_padding_criterion_1 && same_padding_criterion_2) + return circle::Padding_SAME; + + INTERNAL_EXN("Unsupported padding criteria"); +} + +circle::Padding getOpPadding(const luci::Padding pad) +{ + if (pad == luci::Padding::VALID) + return circle::Padding_VALID; + if (pad == luci::Padding::SAME) + return circle::Padding_SAME; + + INTERNAL_EXN_V("Unsupported luci::Padding", oops::to_uint32(pad)); +} + +namespace +{ + +class CircleTensorIndexAnnotation final : public loco::NodeAnnotation +{ +public: + CircleTensorIndexAnnotation(const CircleTensorIndex &index) : _index{index} + { + // DO NOTHING + } + +public: + const CircleTensorIndex &index(void) const { return _index; } + +private: + CircleTensorIndex _index; +}; + +} // namespace + +void set_tensor_index(loco::Node *node, const CircleTensorIndex &tensor_id) +{ + assert(node->annot<CircleTensorIndexAnnotation>() == nullptr); + node->annot(std::make_unique<CircleTensorIndexAnnotation>(tensor_id)); +} + +CircleTensorIndex get_tensor_index(loco::Node *node) +{ + assert(node->annot<CircleTensorIndexAnnotation>() != nullptr); + return node->annot<CircleTensorIndexAnnotation>()->index(); +} + +} // namespace luci diff --git a/compiler/luci/export/src/CircleExporterUtils.h b/compiler/luci/export/src/CircleExporterUtils.h new file mode 100644 index 000000000..6b970fd3c --- /dev/null +++ b/compiler/luci/export/src/CircleExporterUtils.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020 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 __CIRCLE_EXPORTER_UTILS_H__ +#define __CIRCLE_EXPORTER_UTILS_H__ + +#include "SerializedData.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/Service/ShapeDescription.h> + +#include <loco.h> + +#include <mio/circle/schema_generated.h> + +namespace luci +{ + +circle::ActivationFunctionType to_circle_actfunc(luci::FusedActFunc func); +circle::TensorType to_circle_tensortype(loco::DataType type); + +} // namespace luci + +namespace luci +{ + +circle::Padding getOpPadding(const loco::Padding2D *pad, const loco::Stride<2> *stride, + const ShapeDescription &ifm, const ShapeDescription &ofm); +circle::Padding getOpPadding(const luci::Padding pad); + +using CircleTensorIndex = int32_t; + +void set_tensor_index(loco::Node *node, const CircleTensorIndex &tensor_id); +CircleTensorIndex get_tensor_index(loco::Node *node); + +} // namespace luci + +#endif // __CIRCLE_EXPORTER_UTILS_H__ diff --git a/compiler/luci/export/src/CircleOperationExporter.cpp b/compiler/luci/export/src/CircleOperationExporter.cpp new file mode 100644 index 000000000..ad9c7fd4b --- /dev/null +++ b/compiler/luci/export/src/CircleOperationExporter.cpp @@ -0,0 +1,643 @@ +/* + * Copyright (c) 2020 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 "CircleOperationExporter.h" +#include "CircleExporterUtils.h" +#include "Check.h" + +#include <luci/IR/CircleNode.h> +#include <luci/IR/CircleNodes.h> +#include <luci/IR/CircleNodeVisitor.h> +#include <luci/Service/CircleShapeInference.h> + +#include <loco/IR/CanonicalNodeVisitor.h> +#include <oops/InternalExn.h> + +#include <flatbuffers/flexbuffers.h> + +using namespace flatbuffers; +using namespace circle; + +namespace +{ + +using namespace luci; + +class OperationExporter final : public luci::CircleNodeMutableVisitor<void>, + public loco::CanonicalNodeMutableVisitor<void> +{ +public: + OperationExporter(FlatBufferBuilder &fbb, SerializedModelData &m, SerializedGraphData &g) + : builder{fbb}, md{m}, gd{g} + { + // DO NOTHING + } + +public: + void visit(luci::CircleAbs *) final; + void visit(luci::CircleAdd *) final; + void visit(luci::CircleArgMax *) final; + void visit(luci::CircleAveragePool2D *) final; + void visit(luci::CircleBatchToSpaceND *) final; + void visit(luci::CircleConcatenation *) final; + void visit(luci::CircleConst *) final{/* skip, everything is done in exportOpDefinedTensors */}; + void visit(luci::CircleConv2D *) final; + void visit(luci::CircleCos *) final; + void visit(luci::CircleDepthwiseConv2D *) final; + void visit(luci::CircleDiv *) final; + void visit(luci::CircleExp *) final; + void visit(luci::CircleEqual *) final; + void visit(luci::CircleFullyConnected *) final; + void visit(luci::CircleLogicalNot *) final; + void visit(luci::CircleLogicalOr *) final; + void visit(luci::CircleMaximum *) final; + void visit(luci::CircleMaxPool2D *) final; + void visit(luci::CircleMean *) final; + void visit(luci::CircleMul *) final; + void visit(luci::CirclePack *) final; + void visit(luci::CirclePad *) final; + void visit(luci::CircleRelu *) final; + void visit(luci::CircleRelu6 *) final; + void visit(luci::CircleReshape *) final; + void visit(luci::CircleRsqrt *) final; + void visit(luci::CircleSoftmax *) final; + void visit(luci::CircleSqrt *) final; + void visit(luci::CircleSquaredDifference *) final; + void visit(luci::CircleSub *) final; + // TODO CircleTanh + void visit(luci::CircleTranspose *) final; + void visit(luci::CircleTransposeConv *) final; + // Circle only + void visit(luci::CircleInstanceNorm *) final; + // Virtual + void visit(luci::CircleInput *) final {} + void visit(luci::CircleOutput *) final {} + +private: + /** + * @brief Exports CircleMaxPool2D or CircleAveragePool2D + * + * @note CirclePool2D should be one of CircleMaxPool2D or CircleAveragePool2D + */ + template <class CirclePool2D> + void export_pool_2d(CirclePool2D *node, circle::BuiltinOperator builtin_op); + +private: + FlatBufferBuilder &builder; + SerializedModelData &md; + SerializedGraphData &gd; +}; + +template <class CirclePool2D> +void OperationExporter::export_pool_2d(CirclePool2D *node, circle::BuiltinOperator builtin_op) +{ + LUCI_ASSERT(builtin_op == circle::BuiltinOperator_MAX_POOL_2D || + builtin_op == circle::BuiltinOperator_AVERAGE_POOL_2D, + "Should be MaxPool or AvgPool"); + LUCI_ASSERT(node->padding() != luci::Padding::UNDEFINED, "Padding is not set"); + + uint32_t op_idx = md.registerBuiltinOpcode(builtin_op); + std::vector<int32_t> inputs_vec{get_tensor_index(node->value())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + + circle::Padding padding = getOpPadding(node->padding()); + + auto options = CreatePool2DOptions(builder, padding, node->stride()->w(), node->stride()->h(), + node->filter()->w(), node->filter()->h(), + to_circle_actfunc(node->fusedActivationFunction())); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_Pool2DOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleAbs *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_ABS); + std::vector<int32_t> inputs_vec{get_tensor_index(node->x())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateAbsOptions(builder); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_AbsOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleAdd *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_ADD); + std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateAddOptions(builder, to_circle_actfunc(node->fusedActivationFunction())); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_AddOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleArgMax *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_ARG_MAX); + std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), + get_tensor_index(node->dimension())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateArgMaxOptions(builder, to_circle_tensortype(node->output_type())); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_ArgMaxOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleAveragePool2D *node) +{ + export_pool_2d<luci::CircleAveragePool2D>(node, circle::BuiltinOperator_AVERAGE_POOL_2D); +} + +void OperationExporter::visit(luci::CircleConcatenation *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_CONCATENATION); + std::vector<int32_t> inputs_vec; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + + for (uint32_t i = 0; i < node->numValues(); ++i) + inputs_vec.push_back(get_tensor_index(node->values(i))); + + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateConcatenationOptions(builder, node->axis(), + to_circle_actfunc(node->fusedActivationFunction())); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_ConcatenationOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleBatchToSpaceND *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_BATCH_TO_SPACE_ND); + std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), + get_tensor_index(node->block_shape()), + get_tensor_index(node->crops())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateBatchToSpaceNDOptions(builder); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_BatchToSpaceNDOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleConv2D *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_CONV_2D); + + // Make input, output and options for operator + std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->filter()), + get_tensor_index(node->bias())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + circle::Padding padding = getOpPadding(node->padding()); + auto options = CreateConv2DOptions(builder, padding, node->stride()->w(), node->stride()->h(), + to_circle_actfunc(node->fusedActivationFunction())); + + // Make CONV_2D operator + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_Conv2DOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleCos *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_COS); + + // Make input, output and options for operator + std::vector<int32_t> inputs_vec{get_tensor_index(node->x())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateCosOptions(builder); + + // Make COS operator + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_CosOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleDepthwiseConv2D *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_DEPTHWISE_CONV_2D); + + // Make input, output and options for operator + std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->filter()), + get_tensor_index(node->bias())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + circle::Padding padding = getOpPadding(node->padding()); + auto options = CreateDepthwiseConv2DOptions(builder, padding, node->stride()->w(), + node->stride()->h(), node->depthMultiplier(), + to_circle_actfunc(node->fusedActivationFunction())); + + // Make DEPTHWISE_CONV_2D operator + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_DepthwiseConv2DOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleDiv *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_DIV); + std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateDivOptions(builder, to_circle_actfunc(node->fusedActivationFunction())); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_DivOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleExp *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_EXP); + std::vector<int32_t> inputs_vec{get_tensor_index(node->x())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateAbsOptions(builder); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_ExpOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleFullyConnected *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_FULLY_CONNECTED); + + // Make input, output and options for operator + std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), + get_tensor_index(node->weights()), + get_tensor_index(node->bias())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = + CreateFullyConnectedOptions(builder, to_circle_actfunc(node->fusedActivationFunction())); + + // Make FULLY_CONNECTED operator + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_FullyConnectedOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleLogicalNot *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_LOGICAL_NOT); + + // Make input, output and options for operator + std::vector<int32_t> inputs_vec{get_tensor_index(node->x())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateLogicalNotOptions(builder); + + // Make LOGICAL_NOT operator + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_LogicalNotOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleLogicalOr *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_LOGICAL_OR); + + // Make input, output and options for operator + std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateLogicalOrOptions(builder); + + // Make LOGICAL_OR operator + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_LogicalOrOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleMaximum *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_MAXIMUM); + std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateMaximumMinimumOptions(builder); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_MaximumMinimumOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleMaxPool2D *node) +{ + export_pool_2d<luci::CircleMaxPool2D>(node, circle::BuiltinOperator_MAX_POOL_2D); +} + +void OperationExporter::visit(luci::CircleMean *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_MEAN); + std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), + get_tensor_index(node->reduction_indices())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateReducerOptions(builder, node->keep_dims()); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_ReducerOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleMul *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_MUL); + std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateMulOptions(builder, to_circle_actfunc(node->fusedActivationFunction())); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_MulOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CirclePack *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_PACK); + std::vector<int32_t> inputs_vec; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + + for (uint32_t i = 0; i < node->values_count(); ++i) + inputs_vec.push_back(get_tensor_index(node->values(i))); + + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreatePackOptions(builder, node->values_count(), node->axis()); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_PackOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CirclePad *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_PAD); + std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), + get_tensor_index(node->paddings())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreatePadOptions(builder); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_PadOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleRelu *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_RELU); + std::vector<int32_t> inputs_vec{get_tensor_index(node->features())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleRelu6 *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_RELU6); + std::vector<int32_t> inputs_vec{get_tensor_index(node->features())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleReshape *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_RESHAPE); + + // Create inputs and outputs. + std::vector<int32_t> inputs_vec{get_tensor_index(node->tensor()), + get_tensor_index(node->shape())}; + std::vector<int32_t> outputs_vec{get_tensor_index(node)}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + + // Create options. + auto new_shape = builder.CreateVector<int32_t>( + node->newShape()->rank(), [node](size_t i) { return node->newShape()->dim(i); }); + auto options = CreateReshapeOptions(builder, new_shape); + + // Create the operator. + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_ReshapeOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleRsqrt *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_RSQRT); + std::vector<int32_t> inputs_vec{get_tensor_index(node->x())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleSoftmax *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_SOFTMAX); + std::vector<int32_t> inputs_vec{get_tensor_index(node->logits())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateSoftmaxOptions(builder, node->beta()); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_SoftmaxOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleSqrt *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_SQRT); + std::vector<int32_t> inputs_vec{get_tensor_index(node->x())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleSquaredDifference *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_SQUARED_DIFFERENCE); + std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateSquaredDifferenceOptions(builder); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_SquaredDifferenceOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleSub *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_SUB); + std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateSubOptions(builder, to_circle_actfunc(node->fusedActivationFunction())); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_SubOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +// TODO CircleTanh + +void OperationExporter::visit(luci::CircleTranspose *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_TRANSPOSE); + std::vector<int32_t> inputs_vec{get_tensor_index(node->arg(0)), get_tensor_index(node->arg(1))}; + std::vector<int32_t> outputs_vec{get_tensor_index(node)}; + + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateTransposeOptions(builder); + + auto op_offset = + CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions::BuiltinOptions_TransposeOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleTransposeConv *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_TRANSPOSE_CONV); + + // Make input, output and options for operator + std::vector<int32_t> inputs_vec{get_tensor_index(node->inputSizes()), + get_tensor_index(node->filter()), + get_tensor_index(node->outBackprop())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + circle::Padding padding = getOpPadding(node->padding()); + auto options = + CreateTransposeConvOptions(builder, padding, node->stride()->w(), node->stride()->h()); + + // Make TRANSPOSE_CONV operator + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_TransposeConvOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleInstanceNorm *node) +{ + uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_INSTANCE_NORM); + std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->gamma()), + get_tensor_index(node->beta())}; + std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; + auto inputs = builder.CreateVector(inputs_vec); + auto outputs = builder.CreateVector(outputs_vec); + auto options = CreateInstanceNormOptions(builder, node->epsilon(), + to_circle_actfunc(node->fusedActivationFunction())); + auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions_InstanceNormOptions, options.Union()); + gd._operators.push_back(op_offset); +} + +void OperationExporter::visit(luci::CircleEqual *node) +{ + uint32_t opcode_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_EQUAL); + std::vector<int32_t> inputs{get_tensor_index(node->x()), get_tensor_index(node->y())}; + std::vector<int32_t> outputs{get_tensor_index(node)}; + + auto fb_inputs = builder.CreateVector(inputs); + auto fb_outputs = builder.CreateVector(outputs); + + auto options = CreateEqualOptions(builder); + + auto op_offset = CreateOperator(builder, opcode_idx, fb_inputs, fb_outputs, + circle::BuiltinOptions_EqualOptions, options.Union()); + + gd._operators.push_back(op_offset); +} + +void exportNode(loco::Node *node, flatbuffers::FlatBufferBuilder &builder, SerializedModelData &md, + SerializedGraphData &gd) +{ + // TODO Use explicit tagging to prevent possible mistake + auto isNoOp = [](loco::Node *node) { + // If there is only one input and the TensorIndex for the input is same + // as the TensorIndex of the output then this node is just a dummy node + if (node->arity() == 1) + { + assert(node->arg(0) != nullptr); + return get_tensor_index(node) == get_tensor_index(node->arg(0)); + } + return false; + }; + + if (isNoOp(node)) + { + // Skip if a given node is marked as NoOp (op with no effect) before + return; + } + + if (auto circle_node = dynamic_cast<luci::CircleNode *>(node)) + { + OperationExporter exporter{builder, md, gd}; + circle_node->accept(&exporter); + } + else + { + INTERNAL_EXN("Node with unsupported dialect found"); + } +} + +} // namespace + +namespace luci +{ + +void exportNodes(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &md, + SerializedGraphData &gd) +{ + for (auto node : loco::postorder_traversal(loco::output_nodes(g))) + { + exportNode(node, builder, md, gd); + } +} + +} // namespace luci diff --git a/compiler/luci/export/src/CircleOperationExporter.h b/compiler/luci/export/src/CircleOperationExporter.h new file mode 100644 index 000000000..de6abfc54 --- /dev/null +++ b/compiler/luci/export/src/CircleOperationExporter.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __CIRCLE_OPERATION_EXPORTER_H__ +#define __CIRCLE_OPERATION_EXPORTER_H__ + +#include "CircleExporterUtils.h" + +#include <loco/IR/Graph.h> + +namespace luci +{ + +/** + * @brief create Operators corresponding to model nodes + * @param nodes container with nodes + * @param gd information about serializer parts of model + */ +void exportNodes(loco::Graph *g, flatbuffers::FlatBufferBuilder &builder, SerializedModelData &md, + SerializedGraphData &gd); + +} // namespace luci + +#endif // __CIRCLE_OPERATION_EXPORTER_H__ diff --git a/compiler/luci/export/src/CircleTensorExporter.cpp b/compiler/luci/export/src/CircleTensorExporter.cpp new file mode 100644 index 000000000..ef9b9d7d9 --- /dev/null +++ b/compiler/luci/export/src/CircleTensorExporter.cpp @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2020 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 "CircleTensorExporter.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/IR/CircleNodeVisitor.h> +#include <luci/Service/CircleTypeInference.h> +#include <luci/Service/CircleShapeInference.h> +#include <luci/Log.h> + +#include <loco/IR/Algorithm.h> +#include <loco/IR/CanonicalNode.h> +#include <loco/IR/CanonicalNodeVisitor.h> +#include <loco/IR/DataTypeTraits.h> +#include <oops/InternalExn.h> + +using namespace circle; +using namespace flatbuffers; + +namespace +{ + +using namespace luci; + +class CircleTensoInfo +{ +public: + CircleTensoInfo() = default; + +public: + void name(const std::string &name) { _name = name; } + const std::string &name(void) const { return _name; } + +public: + const circle::TensorType &dtype(void) const { return _dtype; } + void dtype(const circle::TensorType &dtype) { _dtype = dtype; } + + const ShapeDescription &shape(void) const { return _shape; } + void shape(const ShapeDescription &shape) { _shape = shape; } + +public: + luci::CircleConst *content(void) const { return _content; } + void content(luci::CircleConst *c) { _content = c; } + + luci::CircleQuantParam *quantparam(void) const { return _quantparam; } + void quantparam(luci::CircleQuantParam *qp) { _quantparam = qp; } + +private: + std::string _name; + + circle::TensorType _dtype; + ShapeDescription _shape; + + luci::CircleConst *_content = nullptr; + luci::CircleQuantParam *_quantparam = nullptr; +}; + +using CircleTensorContext = std::vector<CircleTensoInfo>; + +struct NoOpDetector final : public luci::CircleNodeMutableVisitor<bool> +{ + // Input is Virtual but does produce a Tensor + // Output is Virtual that does not produce any Tensor + bool visit(luci::CircleOutput *) final { return true; } + + // Return false by default + bool visit(luci::CircleNode *) final { return false; } +}; + +void allocateCircleTensor(CircleNode *node, CircleTensorContext &ctx) +{ + LOGGER(l); + + auto isNoOp = [](loco::Node *node) { + if (auto circle_node = dynamic_cast<luci::CircleNode *>(node)) + { + NoOpDetector d; + return circle_node->accept(&d); + } + return false; + }; + + if (isNoOp(node)) + { + set_tensor_index(node, get_tensor_index(node->arg(0))); + return; + } + + auto tensor_index = static_cast<CircleTensorIndex>(ctx.size()); + // TODO Use Graph-level metadata for Input & Output + // auto tensor_name = "t_" + std::to_string(tensor_index); + std::string tensor_name = node->name(); + if (tensor_name.empty()) + tensor_name = "t_" + std::to_string(tensor_index); + INFO(l) << "[luci] Tensor for " << tensor_name << ": " << tensor_index << std::endl; + + CircleTensoInfo tensor_info; + + tensor_info.name(tensor_name); + tensor_info.dtype(TypeInference::get(node)); + tensor_info.shape(ShapeInference::get(node)); + + tensor_info.content(dynamic_cast<luci::CircleConst *>(node)); + tensor_info.quantparam(node->quantparam()); + + set_tensor_index(node, tensor_index); + + ctx.emplace_back(tensor_info); +} + +} // namespace + +namespace +{ + +flatbuffers::Offset<Vector<int32_t>> encodeShape(FlatBufferBuilder &builder, + const ShapeDescription &shape) +{ + assert(shape._rank_known && "unknown number of dimensions is not supported"); + return builder.CreateVector(shape._dims); +} + +flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder) +{ + return CreateBuffer(builder); +} + +template <typename NodeT> +flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder, NodeT *) +{ + return CreateBuffer(builder); +} + +template <loco::DataType DT> +flatbuffers::Offset<circle::Buffer> encodeOpBufferByDType(FlatBufferBuilder &builder, + luci::CircleConst *c) +{ + using NativeType = typename loco::DataTypeImpl<DT>::Type; + + std::vector<NativeType> raw_data; + const uint32_t size = c->size<DT>(); + raw_data.reserve(size); + for (uint32_t i = 0; i < size; ++i) + { + raw_data.push_back(c->at<DT>(i)); + } + const size_t raw_size = size * sizeof(NativeType); + auto array_offset = builder.CreateVector(reinterpret_cast<uint8_t *>(raw_data.data()), raw_size); + return CreateBuffer(builder, array_offset); +} + +template <> +flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder, luci::CircleConst *c) +{ + // TODO use switch + if (c->dtype() == loco::DataType::FLOAT32) + { + return encodeOpBufferByDType<loco::DataType::FLOAT32>(builder, c); + } + else if (c->dtype() == loco::DataType::S32) + { + return encodeOpBufferByDType<loco::DataType::S32>(builder, c); + } + else if (c->dtype() == loco::DataType::U8) + { + return encodeOpBufferByDType<loco::DataType::U8>(builder, c); + } + + INTERNAL_EXN_V("Unsupported datatype", oops::to_uint32(c->dtype())); +} + +flatbuffers::Offset<circle::QuantizationParameters> +encodeQuantizationParameters(FlatBufferBuilder &builder, luci::CircleQuantParam *quantparam) +{ + if (quantparam == nullptr) + return 0; + + flatbuffers::Offset<flatbuffers::Vector<float>> min; + flatbuffers::Offset<flatbuffers::Vector<float>> max; + flatbuffers::Offset<flatbuffers::Vector<float>> scale; + flatbuffers::Offset<flatbuffers::Vector<int64_t>> zero_point; + if (quantparam->min.size() && quantparam->max.size()) + { + min = builder.CreateVector(quantparam->min); + max = builder.CreateVector(quantparam->max); + } + if (quantparam->scale.size() && quantparam->zerop.size()) + { + scale = builder.CreateVector(quantparam->scale); + zero_point = builder.CreateVector(quantparam->zerop); + } + return circle::CreateQuantizationParameters(builder, min, max, scale, zero_point); +} + +void exportOpDefinedTensor(const CircleTensoInfo &info, FlatBufferBuilder &builder, + SerializedModelData &md, SerializedGraphData &gd) +{ + // Create and register output tensor shape + auto shape_offset = encodeShape(builder, info.shape()); + + // encode and register output tensor buffer + auto buffer = + info.content() == nullptr ? encodeOpBuffer(builder) : encodeOpBuffer(builder, info.content()); + + auto quantparam = encodeQuantizationParameters(builder, info.quantparam()); + + auto buffer_id = static_cast<uint32_t>(md._buffers.size()); + md._buffers.push_back(buffer); + + auto name_offset = builder.CreateString(info.name()); + auto tensor_offset = CreateTensor(builder, shape_offset, info.dtype(), buffer_id, name_offset, + quantparam, /*is_variable*/ false); + gd._tensors.push_back(tensor_offset); +} + +} // namespace + +namespace luci +{ + +void prepareModelData(FlatBufferBuilder &builder, SerializedModelData &md) +{ + // add one empty buffer + // note: this follows TFLite + // note: there's a comment in tflite fbs file + // - Note the 0th entry of this array must be an empty buffer (sentinel). + // - This is a convention so that tensors without a buffer can provide 0 as + // - their buffer. + auto buffer = encodeOpBuffer(builder); + md._buffers.push_back(buffer); +} + +void exportOpDefinedTensors(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &md, + SerializedGraphData &gd) +{ + CircleTensorContext tensor_ctx; + + for (auto node : loco::postorder_traversal(loco::output_nodes(g))) + { + CircleNode *circle_node = dynamic_cast<luci::CircleNode *>(node); + allocateCircleTensor(circle_node, tensor_ctx); + } + + for (const auto &tensor_info : tensor_ctx) + { + exportOpDefinedTensor(tensor_info, builder, md, gd); + } +} + +} // namespace luci diff --git a/compiler/luci/export/src/CircleTensorExporter.h b/compiler/luci/export/src/CircleTensorExporter.h new file mode 100644 index 000000000..f9d6107b4 --- /dev/null +++ b/compiler/luci/export/src/CircleTensorExporter.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 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 __CIRCLE_TENSOR_EXPORTER_H__ +#define __CIRCLE_TENSOR_EXPORTER_H__ + +#include "CircleExporterUtils.h" + +#include <loco/IR/Graph.h> + +#include <flatbuffers/flatbuffers.h> + +namespace luci +{ + +/** + * @brief one time preparation for SerializedModelData + */ +void prepareModelData(flatbuffers::FlatBufferBuilder &builder, SerializedModelData &md); + +/** + * @brief create Tensors corresponding to results of all nodes in graph + * @param computational graph + * @param gd information about serialized parts of model + */ +void exportOpDefinedTensors(loco::Graph *g, flatbuffers::FlatBufferBuilder &builder, + SerializedModelData &md, SerializedGraphData &gd); + +} // namespace luci + +#endif // __CIRCLE_TENSOR_EXPORTER_H__ diff --git a/compiler/luci/export/src/Optimize.cpp b/compiler/luci/export/src/Optimize.cpp new file mode 100644 index 000000000..6fa50b564 --- /dev/null +++ b/compiler/luci/export/src/Optimize.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 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 "Optimize.h" +#include "ProgressReporter.h" + +#include <luci/Pass/ShapeInferencePass.h> +#include <luci/Pass/TypeInferencePass.h> + +#include <logo/Phase.h> + +#include <memory> + +namespace luci +{ + +void optimize(loco::Graph *g) +{ + logo::Phase phase; + { + // prepare type and shape before optimization + phase.emplace_back(std::make_unique<TypeInferencePass>()); + phase.emplace_back(std::make_unique<ShapeInferencePass>()); + + // TODO add more optimization passes (with a knob) + } + + logo::PhaseRunner<logo::PhaseStrategy::Restart> phase_runner{g}; + + ProgressReporter prog(g, logo::PhaseStrategy::Restart); + phase_runner.attach(&prog); + phase_runner.run(phase); +} + +} // namespace luci diff --git a/compiler/luci/export/src/Optimize.h b/compiler/luci/export/src/Optimize.h new file mode 100644 index 000000000..c3af7a04c --- /dev/null +++ b/compiler/luci/export/src/Optimize.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020 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 __OPTIMIZE_H__ +#define __OPTIMIZE_H__ + +#include <loco.h> + +namespace luci +{ + +/** + * @brief Run passes of graph transformations + * + */ +void optimize(loco::Graph *); + +} // namespace luci + +#endif // __OPTIMIZE_H__ diff --git a/compiler/luci/export/src/ProgressReporter.cpp b/compiler/luci/export/src/ProgressReporter.cpp new file mode 100644 index 000000000..ac9c3d9a8 --- /dev/null +++ b/compiler/luci/export/src/ProgressReporter.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2020 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 "ProgressReporter.h" + +#include "luci/Log.h" +#include "luci/LogHelper.h" + +#include <logo/Phase.h> +#include <logo/Pass.h> + +#include <cassert> + +namespace +{ + +char to_char(bool b) { return b ? 'Y' : 'N'; } + +const char *to_str(logo::PhaseStrategy s) +{ + switch (s) + { + case logo::PhaseStrategy::Saturate: + return "Saturate"; + case logo::PhaseStrategy::Restart: + return "Restart"; + } + assert(false); + return ""; +} + +} // namespace + +namespace luci +{ + +void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseBegin> *) +{ + LOGGER(prime); + + INFO(prime) << "=============================================================="; + INFO(prime) << "luci::PhaseRunner<" << to_str(strategy()) << ">"; + INFO(prime) << "Initial graph"; + INFO(prime) << fmt(graph()); +} + +void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseEnd> *) +{ + LOGGER(prime); + + INFO(prime) << "luci::PhaseRunner<" << to_str(strategy()) << "> - done"; +} + +void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassBegin> *info) +{ + LOGGER(prime); + + INFO(prime) << "--------------------------------------------------------------"; + INFO(prime) << "Before " << logo::pass_name(info->pass()); +} + +void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassEnd> *info) +{ + LOGGER(prime); + + INFO(prime) << "After " << logo::pass_name(info->pass()) + << " (changed: " << to_char(info->changed()) << ")"; + INFO(prime) << fmt(graph()); +} + +} // namespace luci diff --git a/compiler/luci/export/src/ProgressReporter.h b/compiler/luci/export/src/ProgressReporter.h new file mode 100644 index 000000000..e91f42592 --- /dev/null +++ b/compiler/luci/export/src/ProgressReporter.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020 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 __PROGRESSREPORTER_H__ +#define __PROGRESSREPORTER_H__ + +#include <logo/Phase.h> + +#include <loco.h> + +namespace luci +{ + +class ProgressReporter : public logo::PhaseEventListener +{ +public: + ProgressReporter(loco::Graph *graph, logo::PhaseStrategy strategy) + : _graph{graph}, _strategy{strategy} + { + // DO NOTHING + } + +public: + void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseBegin> *) override; + void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseEnd> *) override; + void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassBegin> *) override; + void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassEnd> *) override; + +public: + loco::Graph *graph(void) const { return _graph; } + logo::PhaseStrategy strategy(void) const { return _strategy; } + +private: + loco::Graph *_graph; + logo::PhaseStrategy _strategy; +}; + +} // namespace luci + +#endif // __PROGRESSREPORTER_H__ diff --git a/compiler/luci/export/src/SerializedData.h b/compiler/luci/export/src/SerializedData.h new file mode 100644 index 000000000..84249653c --- /dev/null +++ b/compiler/luci/export/src/SerializedData.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2020 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 __SERIALIZED_DATA_H__ +#define __SERIALIZED_DATA_H__ + +#include <mio/circle/schema_generated.h> + +#include <vector> + +#include <unordered_map> + +namespace luci +{ + +struct OpCode +{ + circle::BuiltinOperator opcode; + + bool operator==(const OpCode &rhs) const { return opcode == rhs.opcode; } +}; + +} // namespace luci + +namespace std +{ + +template <> struct hash<luci::OpCode> +{ + size_t operator()(const luci::OpCode &x) const { return hash<int>()(x.opcode); } +}; + +} // namespace std + +namespace luci +{ + +/** + * @breif Record the information of T/F Lite SubGraph and its mapping to loco + */ +struct SubGraphContext +{ + /// @brief SubGraph input tensor id + std::vector<int32_t> _inputs; + /// @brief SubGraph output tensor id + std::vector<int32_t> _outputs; + /// @DataFormat for SubGraph + circle::DataFormat _data_format{circle::DataFormat::DataFormat_CHANNELS_LAST}; +}; + +// Prerequisites for circle::Model object creation +struct SerializedModelData final +{ + SerializedModelData() = default; + SerializedModelData(const SerializedModelData &) = delete; + + std::unordered_map<OpCode, uint32_t> _operator_codes; + std::unordered_map<OpCode, std::string> _custom_operator_codes; + std::vector<flatbuffers::Offset<circle::Buffer>> _buffers; + + /** + * @brief if opcode is not registered in table of opcodes add it + * @param builtin_code + * @return idx of opcode in table of opcodes (see schema) + */ + uint32_t registerBuiltinOpcode(circle::BuiltinOperator builtin_code); + uint32_t registerCustomOpcode(const std::string &custom_op); +}; + +// Prerequisites for circle::Model object creation +struct SerializedGraphData final : public SubGraphContext +{ + SerializedGraphData() = default; + SerializedGraphData(const SerializedModelData &) = delete; + + std::vector<flatbuffers::Offset<circle::Operator>> _operators; + std::vector<flatbuffers::Offset<circle::Tensor>> _tensors; +}; + +} // namespace luci + +#endif // __SERIALIZED_DATA_H__ diff --git a/compiler/luci/import/CMakeLists.txt b/compiler/luci/import/CMakeLists.txt new file mode 100644 index 000000000..bc9a9152a --- /dev/null +++ b/compiler/luci/import/CMakeLists.txt @@ -0,0 +1,26 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(luci_import SHARED ${SOURCES}) +target_include_directories(luci_import PRIVATE src) +target_include_directories(luci_import PUBLIC include) +target_link_libraries(luci_import PUBLIC luci_lang) +target_link_libraries(luci_import PUBLIC mio_circle) +target_link_libraries(luci_import PRIVATE luci_log) +target_link_libraries(luci_import PRIVATE luci_logex) +target_link_libraries(luci_import PRIVATE nncc_common) +target_link_libraries(luci_import PRIVATE locop) +target_link_libraries(luci_import PRIVATE oops) +install(TARGETS luci_import DESTINATION lib) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(luci_import_test ${TESTS}) +target_include_directories(luci_import_test PRIVATE src) +target_link_libraries(luci_import_test luci_import) +target_link_libraries(luci_import_test oops) diff --git a/compiler/luci/import/README.md b/compiler/luci/import/README.md new file mode 100644 index 000000000..4ae81ff67 --- /dev/null +++ b/compiler/luci/import/README.md @@ -0,0 +1,3 @@ +# luci-import + +_luci-import_ provides importing Circle model file to _loco_ graph of _luci_ Circle Dialect IR diff --git a/compiler/luci/import/include/luci/Import/CircleReader.h b/compiler/luci/import/include/luci/Import/CircleReader.h new file mode 100644 index 000000000..fcbe09ceb --- /dev/null +++ b/compiler/luci/import/include/luci/Import/CircleReader.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_GRAPHREADER_H__ +#define __LUCI_IMPORT_GRAPHREADER_H__ + +#include <mio/circle/schema_generated.h> + +#include <luci/IR/AttrFusedActFunc.h> +#include <luci/IR/AttrPadding.h> +#include <luci/IR/CircleQuantParam.h> + +#include <loco.h> + +#include <map> +#include <memory> +#include <string> +#include <vector> + +namespace luci +{ + +bool is_valid(const circle::OperatorCodeT &opcode); +bool is_custom(const circle::OperatorCodeT &opcode); +std::string opcode_name(const circle::OperatorCodeT &opcode); +const char *tensor_name(const circle::TensorT &tensor); +const circle::QuantizationParametersT *tensor_quantization(const circle::TensorT &tensor); + +loco::DataType luci_datatype(circle::TensorType type); +FusedActFunc luci_actfunc(const circle::ActivationFunctionType type); +Padding luci_padding(const circle::Padding padding); +std::unique_ptr<CircleQuantParam> +luci_quantparam(const circle::QuantizationParametersT *quantization); + +/** + * @brief Loads Circle file and provides helpers to access attributes + */ +class CircleReader +{ +private: + using CircleBuffers_t = std::vector<std::unique_ptr<circle::BufferT>>; + using CircleTensors_t = std::vector<std::unique_ptr<circle::TensorT>>; + using CircleOperators_t = std::vector<std::unique_ptr<circle::OperatorT>>; + using CircleOperatorCodes_t = std::vector<std::unique_ptr<circle::OperatorCodeT>>; + +public: + CircleReader() = default; + +public: + const CircleOperatorCodes_t &opcodes() const { return _model->operator_codes; } + const CircleBuffers_t &buffers() const { return _model->buffers; } + const CircleTensors_t &tensors() const { return _current_subgraph->tensors; } + const CircleOperators_t &operators() const { return _current_subgraph->operators; } + const std::vector<int32_t> &inputs() const { return _current_subgraph->inputs; } + const std::vector<int32_t> &outputs() const { return _current_subgraph->outputs; } + const std::string &name() const { return _current_subgraph->name; } + + uint32_t num_subgraph() const { return _model->subgraphs.size(); } + + circle::BuiltinOperator builtin_code(const circle::OperatorT &op) const; + std::string opcode_name(const circle::OperatorT &op) const; + +public: + bool parse(const circle::Model *model); + bool select_subgraph(uint32_t subgraph); + +private: + std::unique_ptr<const circle::ModelT> _model; + const circle::SubGraphT *_current_subgraph{nullptr}; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_GRAPHREADER_H__ diff --git a/compiler/luci/import/include/luci/Import/GraphBuilder.h b/compiler/luci/import/include/luci/Import/GraphBuilder.h new file mode 100644 index 000000000..61f673fb6 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/GraphBuilder.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_GRAPH_BUILDER_H__ +#define __LUCI_IMPORT_GRAPH_BUILDER_H__ + +#include "GraphBuilderContext.h" + +#include <mio/circle/schema_generated.h> + +namespace luci +{ + +/** + * @brief Interface of convert circle:: NodeDef to loco::Node (e.g., Conv2DGraphBuilder) + */ +class GraphBuilder +{ +public: + struct ValidateArgs + { + ValidateArgs(const circle::OperatorT &o, const CircleReader &r) : op(o), reader(r) {} + + const circle::OperatorT &op; + const CircleReader &reader; + }; + +public: + virtual ~GraphBuilder() = default; + + virtual bool validate(const ValidateArgs &) const = 0; + + void build(const circle::OperatorT &op, GraphBuilderContext *context) const; + +private: + virtual CircleNode *build_node(const circle::OperatorT &op, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const = 0; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_GRAPH_BUILDER_H__ diff --git a/compiler/luci/import/include/luci/Import/GraphBuilderContext.h b/compiler/luci/import/include/luci/Import/GraphBuilderContext.h new file mode 100644 index 000000000..8d464181d --- /dev/null +++ b/compiler/luci/import/include/luci/Import/GraphBuilderContext.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_GRAPH_BUILDER_CONTEXT_H__ +#define __LUCI_IMPORT_GRAPH_BUILDER_CONTEXT_H__ + +#include "CircleReader.h" + +#include <luci/IR/CircleNode.h> + +#include <loco.h> + +#include <map> + +namespace luci +{ + +using TensorIndex = int32_t; + +/* + * @brief Tensor Index to CircleNode + * To find CircleNode from TensorIndex + */ +class IndexNodeFinder +{ +public: + void enroll(TensorIndex idx, CircleNode *node); + + CircleNode *node(TensorIndex idx) const; + +private: + using MapIndexNode_t = std::map<TensorIndex, CircleNode *>; + + MapIndexNode_t _table; +}; + +/** + * @brief Class to store context to build loco graph IR from TensorFlow + */ +class GraphBuilderContext +{ +public: + GraphBuilderContext(loco::Graph *g, CircleReader *reader, IndexNodeFinder *nodefinder) + : _g(g), _reader(reader), _indexnodefinder(nodefinder) + { + // DO NOTHING + } + + GraphBuilderContext(const GraphBuilderContext &) = delete; + GraphBuilderContext(GraphBuilderContext &&) = delete; + +public: + loco::Graph *graph() { return _g; } + CircleReader *reader() { return _reader; } + + IndexNodeFinder *nodefinder() { return _indexnodefinder; } + +private: + loco::Graph *_g; + CircleReader *_reader; + IndexNodeFinder *_indexnodefinder; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_GRAPH_BUILDER_CONTEXT_H__ diff --git a/compiler/luci/import/include/luci/Import/GraphBuilderRegistry.h b/compiler/luci/import/include/luci/Import/GraphBuilderRegistry.h new file mode 100644 index 000000000..99054e7b6 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/GraphBuilderRegistry.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_GRAPH_BUILDER_REGISTRY_H__ +#define __LUCI_IMPORT_GRAPH_BUILDER_REGISTRY_H__ + +#include "GraphBuilder.h" + +#include <map> + +namespace luci +{ + +struct GraphBuilderSource +{ + virtual ~GraphBuilderSource() = default; + + /** + * @brief Returns registered GraphBuilder pointer for operator (nullptr if not present) + */ + virtual const GraphBuilder *lookup(const circle::BuiltinOperator &op) const = 0; +}; + +/** + * @brief Class to return graph builder for TF nodes + */ +class GraphBuilderRegistry final : public GraphBuilderSource +{ +public: + GraphBuilderRegistry(); + +public: + GraphBuilderRegistry(const GraphBuilderSource *parent) : _parent{parent} + { + // DO NOTHING + } + +public: + /** + * @brief Returns registered GraphBuilder pointer for operator or + * nullptr if not registered + */ + const GraphBuilder *lookup(const circle::BuiltinOperator &op) const final + { + if (_builder_map.find(op) == _builder_map.end()) + return (_parent == nullptr) ? nullptr : _parent->lookup(op); + + return _builder_map.at(op).get(); + } + + static GraphBuilderRegistry &get() + { + static GraphBuilderRegistry me; + return me; + } + +public: + void add(const circle::BuiltinOperator op, std::unique_ptr<GraphBuilder> &&builder) + { + _builder_map[op] = std::move(builder); + } + +private: + const GraphBuilderSource *_parent = nullptr; + +private: + std::map<const circle::BuiltinOperator, std::unique_ptr<GraphBuilder>> _builder_map; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_GRAPH_BUILDER_REGISTRY_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes.h b/compiler/luci/import/include/luci/Import/Nodes.h new file mode 100644 index 000000000..381d02b97 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_NODES_H__ +#define __LUCI_IMPORT_NODES_H__ + +#include "Nodes/CircleAbs.h" +#include "Nodes/CircleAdd.h" +#include "Nodes/CircleArgMax.h" +#include "Nodes/CircleAveragePool2D.h" +#include "Nodes/CircleBatchToSpaceND.h" +#include "Nodes/CircleConcatenation.h" +#include "Nodes/CircleConst.h" +#include "Nodes/CircleConv2D.h" +#include "Nodes/CircleCos.h" +#include "Nodes/CircleDepthwiseConv2D.h" +#include "Nodes/CircleDiv.h" +#include "Nodes/CircleEqual.h" +#include "Nodes/CircleExp.h" +#include "Nodes/CircleFullyConnected.h" +#include "Nodes/CircleLogicalNot.h" +#include "Nodes/CircleLogicalOr.h" +#include "Nodes/CircleMaxPool2D.h" +#include "Nodes/CircleMean.h" +#include "Nodes/CircleMul.h" +#include "Nodes/CirclePack.h" +#include "Nodes/CirclePad.h" +#include "Nodes/CircleRelu.h" +#include "Nodes/CircleReshape.h" +#include "Nodes/CircleRsqrt.h" +#include "Nodes/CircleSoftmax.h" +#include "Nodes/CircleSub.h" +#include "Nodes/CircleTranspose.h" + +#endif // __LUCI_IMPORT_NODES_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleAbs.h b/compiler/luci/import/include/luci/Import/Nodes/CircleAbs.h new file mode 100644 index 000000000..e0cec26d9 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleAbs.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_ABS_H__ +#define __LUCI_IMPORT_OP_CIRCLE_ABS_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleAbsGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_ABS_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleAdd.h b/compiler/luci/import/include/luci/Import/Nodes/CircleAdd.h new file mode 100644 index 000000000..d852ee8b3 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleAdd.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_ADD_H__ +#define __LUCI_IMPORT_OP_CIRCLE_ADD_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleAddGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_ADD_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleArgMax.h b/compiler/luci/import/include/luci/Import/Nodes/CircleArgMax.h new file mode 100644 index 000000000..dae4691dc --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleArgMax.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_ARGMAX_H__ +#define __LUCI_IMPORT_OP_CIRCLE_ARGMAX_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleArgMaxGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_ARGMAX_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleAveragePool2D.h b/compiler/luci/import/include/luci/Import/Nodes/CircleAveragePool2D.h new file mode 100644 index 000000000..07f6565bc --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleAveragePool2D.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_AVERAGEPOOL2D_H__ +#define __LUCI_IMPORT_OP_CIRCLE_AVERAGEPOOL2D_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleAveragePool2DGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_AVERAGEPOOL2D_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleBatchToSpaceND.h b/compiler/luci/import/include/luci/Import/Nodes/CircleBatchToSpaceND.h new file mode 100644 index 000000000..4168d248e --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleBatchToSpaceND.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_BATCHTOSPACEND_H__ +#define __LUCI_IMPORT_OP_CIRCLE_BATCHTOSPACEND_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleBatchToSpaceNDGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_BATCHTOSPACEND_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleConcatenation.h b/compiler/luci/import/include/luci/Import/Nodes/CircleConcatenation.h new file mode 100644 index 000000000..9b4c9ffd1 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleConcatenation.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_CONCATENATION_H__ +#define __LUCI_IMPORT_OP_CIRCLE_CONCATENATION_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleConcatenationGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_CONCATENATION_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleConst.h b/compiler/luci/import/include/luci/Import/Nodes/CircleConst.h new file mode 100644 index 000000000..7d4f10a59 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleConst.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_CONST_H__ +#define __LUCI_IMPORT_OP_CIRCLE_CONST_H__ + +#include "luci/Import/GraphBuilderContext.h" + +#include <luci/IR/Nodes/CircleConst.h> + +/* + * @note Circle does not have Const operator. + * Methods here provide helper that creates CircleConst from + * Tensor and Buffer in circle flatbuffer file. + */ + +namespace luci +{ + +CircleConst *create_circleconst(GraphBuilderContext *context, int32_t tensor_index); + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_CONST_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleConv2D.h b/compiler/luci/import/include/luci/Import/Nodes/CircleConv2D.h new file mode 100644 index 000000000..4529a4f11 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleConv2D.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_CONV_2D_H__ +#define __LUCI_IMPORT_OP_CIRCLE_CONV_2D_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleConv2DGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_CONV_2D_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleCos.h b/compiler/luci/import/include/luci/Import/Nodes/CircleCos.h new file mode 100644 index 000000000..fb472977e --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleCos.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_COS_H__ +#define __LUCI_IMPORT_OP_CIRCLE_COS_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleCosGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_COS_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleDepthwiseConv2D.h b/compiler/luci/import/include/luci/Import/Nodes/CircleDepthwiseConv2D.h new file mode 100644 index 000000000..1953cb76c --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleDepthwiseConv2D.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_DEPTHWISECONV_2D_H__ +#define __LUCI_IMPORT_OP_CIRCLE_DEPTHWISECONV_2D_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleDepthwiseConv2DGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_DEPTHWISECONV_2D_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleDiv.h b/compiler/luci/import/include/luci/Import/Nodes/CircleDiv.h new file mode 100644 index 000000000..6a38118fe --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleDiv.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_DIV_H__ +#define __LUCI_IMPORT_OP_CIRCLE_DIV_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleDivGraphBuilder : public GraphBuilder +{ + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const override; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_DIV_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleEqual.h b/compiler/luci/import/include/luci/Import/Nodes/CircleEqual.h new file mode 100644 index 000000000..a98adcd08 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleEqual.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_EQUAL_H__ +#define __LUCI_IMPORT_OP_CIRCLE_EQUAL_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleEqualGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_EQUAL_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleExp.h b/compiler/luci/import/include/luci/Import/Nodes/CircleExp.h new file mode 100644 index 000000000..521809fe4 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleExp.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_EXP_H__ +#define __LUCI_IMPORT_OP_CIRCLE_EXP_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleExpGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_EXP_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleFullyConnected.h b/compiler/luci/import/include/luci/Import/Nodes/CircleFullyConnected.h new file mode 100644 index 000000000..b7798c688 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleFullyConnected.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_FULLYCONNECTED_H__ +#define __LUCI_IMPORT_OP_CIRCLE_FULLYCONNECTED_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleFullyConnectedGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_FULLYCONNECTED_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleLogicalNot.h b/compiler/luci/import/include/luci/Import/Nodes/CircleLogicalNot.h new file mode 100644 index 000000000..ec890ecf7 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleLogicalNot.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_LOGICALNOT_H__ +#define __LUCI_IMPORT_OP_CIRCLE_LOGICALNOT_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleLogicalNotGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_LOGICALNOT_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleLogicalOr.h b/compiler/luci/import/include/luci/Import/Nodes/CircleLogicalOr.h new file mode 100644 index 000000000..9fb0086c1 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleLogicalOr.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_LOGICALOR_H__ +#define __LUCI_IMPORT_OP_CIRCLE_LOGICALOR_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleLogicalOrGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_LOGICALOR_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleMaxPool2D.h b/compiler/luci/import/include/luci/Import/Nodes/CircleMaxPool2D.h new file mode 100644 index 000000000..bcd2acb30 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleMaxPool2D.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_MAXPOOL2D_H__ +#define __LUCI_IMPORT_OP_CIRCLE_MAXPOOL2D_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleMaxPool2DGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_MAXPOOL2D_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleMean.h b/compiler/luci/import/include/luci/Import/Nodes/CircleMean.h new file mode 100644 index 000000000..a7919a57c --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleMean.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_MEAN_H__ +#define __LUCI_IMPORT_OP_CIRCLE_MEAN_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleMeanGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_MEAN_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleMul.h b/compiler/luci/import/include/luci/Import/Nodes/CircleMul.h new file mode 100644 index 000000000..13027a155 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleMul.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_MUL_H__ +#define __LUCI_IMPORT_OP_CIRCLE_MUL_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleMulGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const override; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_MUL_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CirclePack.h b/compiler/luci/import/include/luci/Import/Nodes/CirclePack.h new file mode 100644 index 000000000..8e4b71995 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CirclePack.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_PACK_H__ +#define __LUCI_IMPORT_OP_CIRCLE_PACK_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CirclePackGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const override; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_PACK_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CirclePad.h b/compiler/luci/import/include/luci/Import/Nodes/CirclePad.h new file mode 100644 index 000000000..e333ee912 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CirclePad.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_PAD_H__ +#define __LUCI_IMPORT_OP_CIRCLE_PAD_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CirclePadGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_PAD_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleRelu.h b/compiler/luci/import/include/luci/Import/Nodes/CircleRelu.h new file mode 100644 index 000000000..deb913243 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleRelu.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_RELU_H__ +#define __LUCI_IMPORT_OP_CIRCLE_RELU_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleReluGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_RELU_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleReshape.h b/compiler/luci/import/include/luci/Import/Nodes/CircleReshape.h new file mode 100644 index 000000000..eb4fb13ba --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleReshape.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_RESHAPE_H__ +#define __LUCI_IMPORT_OP_CIRCLE_RESHAPE_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleReshapeGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_RESHAPE_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleRsqrt.h b/compiler/luci/import/include/luci/Import/Nodes/CircleRsqrt.h new file mode 100644 index 000000000..90d568f1f --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleRsqrt.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_RSQRT_H__ +#define __LUCI_IMPORT_OP_CIRCLE_RSQRT_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleRsqrtGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_RSQRT_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSoftmax.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSoftmax.h new file mode 100644 index 000000000..b93846d67 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSoftmax.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_SOFTMAX_H__ +#define __LUCI_IMPORT_OP_CIRCLE_SOFTMAX_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleSoftmaxGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_SOFTMAX_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSub.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSub.h new file mode 100644 index 000000000..315d1c2f9 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSub.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_SUB_H__ +#define __LUCI_IMPORT_OP_CIRCLE_SUB_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleSubGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_SUB_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleTranspose.h b/compiler/luci/import/include/luci/Import/Nodes/CircleTranspose.h new file mode 100644 index 000000000..ac0f1fb41 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleTranspose.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORT_OP_CIRCLE_TRANSPOSE_H__ +#define __LUCI_IMPORT_OP_CIRCLE_TRANSPOSE_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleTransposeGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const override; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_TRANSPOSE_H__ diff --git a/compiler/luci/import/include/luci/Importer.h b/compiler/luci/import/include/luci/Importer.h new file mode 100644 index 000000000..246df9f27 --- /dev/null +++ b/compiler/luci/import/include/luci/Importer.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 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 __LUCI_IMPORTER_H__ +#define __LUCI_IMPORTER_H__ + +#include "luci/Import/GraphBuilderRegistry.h" + +#include "luci/IR/Module.h" + +#include <loco.h> + +#include <mio/circle/schema_generated.h> + +#include <memory> + +namespace luci +{ + +class Importer final +{ +public: + Importer(); + +public: + explicit Importer(const GraphBuilderSource *source) : _source{source} + { + // DO NOTHING + } + +public: + std::unique_ptr<loco::Graph> import(const circle::Model *model) const; + std::unique_ptr<Module> importModule(const circle::Model *model) const; + +private: + const GraphBuilderSource *_source = nullptr; +}; + +} // namespace luci + +#endif // __MOCO_IMPORTER_H__ diff --git a/compiler/luci/import/src/CircleReader.cpp b/compiler/luci/import/src/CircleReader.cpp new file mode 100644 index 000000000..ead0093b8 --- /dev/null +++ b/compiler/luci/import/src/CircleReader.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2020 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 "luci/Import/CircleReader.h" + +#include <memory> +#include <sstream> +#include <string> + +namespace luci +{ + +bool is_valid(const circle::OperatorCodeT &opcode) +{ + circle::BuiltinOperator code = opcode.builtin_code; + return (circle::BuiltinOperator_MIN <= code && code <= circle::BuiltinOperator_MAX); +} + +bool is_custom(const circle::OperatorCodeT &opcode) +{ + circle::BuiltinOperator code = opcode.builtin_code; + return (code == circle::BuiltinOperator_CUSTOM); +} + +std::string opcode_name(const circle::OperatorCodeT &opcode) +{ + if (!is_valid(opcode)) + { + std::ostringstream oss; + oss << "(invalid)"; + return oss.str(); + } + + if (is_custom(opcode)) + { + if (opcode.custom_code.empty()) + return "(invalid custom)"; + + return opcode.custom_code; + } + + circle::BuiltinOperator code = opcode.builtin_code; + return circle::EnumNameBuiltinOperator(code); +} + +const char *tensor_name(const circle::TensorT &tensor) +{ + static const char *kEmptyTensorName = "(noname)"; + + if (!tensor.name.empty()) + return tensor.name.c_str(); + + return kEmptyTensorName; +} + +const circle::QuantizationParametersT *tensor_quantization(const circle::TensorT &tensor) +{ + return tensor.quantization.get(); +} + +loco::DataType luci_datatype(const circle::TensorType type) +{ + switch (type) + { + case circle::TensorType_FLOAT32: + return loco::DataType::FLOAT32; + case circle::TensorType_FLOAT16: + return loco::DataType::FLOAT16; + case circle::TensorType_INT32: + return loco::DataType::S32; + case circle::TensorType_UINT8: + return loco::DataType::U8; + case circle::TensorType_INT64: + return loco::DataType::S64; + case circle::TensorType_STRING: + break; + case circle::TensorType_BOOL: + return loco::DataType::BOOL; + case circle::TensorType_INT16: + return loco::DataType::S16; + case circle::TensorType_COMPLEX64: + break; + case circle::TensorType_INT8: + return loco::DataType::S8; + default: + break; + } + assert(false); + return loco::DataType::Unknown; +} + +FusedActFunc luci_actfunc(const circle::ActivationFunctionType type) +{ + switch (type) + { + case circle::ActivationFunctionType::ActivationFunctionType_NONE: + return luci::FusedActFunc::NONE; + case circle::ActivationFunctionType::ActivationFunctionType_RELU: + return luci::FusedActFunc::RELU; + case circle::ActivationFunctionType::ActivationFunctionType_RELU_N1_TO_1: + return luci::FusedActFunc::RELU_N1_TO_1; + case circle::ActivationFunctionType::ActivationFunctionType_RELU6: + return luci::FusedActFunc::RELU6; + case circle::ActivationFunctionType::ActivationFunctionType_TANH: + break; + default: + break; + } + assert(false); + return luci::FusedActFunc::UNDEFINED; +} + +Padding luci_padding(const circle::Padding padding) +{ + switch (padding) + { + case circle::Padding::Padding_SAME: + return Padding::SAME; + case circle::Padding::Padding_VALID: + return Padding::VALID; + } + assert(false); + return Padding::UNDEFINED; +} + +std::unique_ptr<CircleQuantParam> +luci_quantparam(const circle::QuantizationParametersT *quantization) +{ + const auto &min = quantization->min; + const auto &max = quantization->max; + const auto &scale = quantization->scale; + const auto &zero_point = quantization->zero_point; + + if ((!min.empty() && !max.empty()) || (!scale.empty() && !zero_point.empty())) + { + auto quantparam = std::make_unique<CircleQuantParam>(); + + quantparam->min = min; + quantparam->max = max; + quantparam->scale = scale; + quantparam->zerop = zero_point; + + return quantparam; + } + + return nullptr; +} + +circle::BuiltinOperator CircleReader::builtin_code(const circle::OperatorT &op) const +{ + const auto &op_codes = opcodes(); + uint32_t index = op.opcode_index; + assert(index < op_codes.size()); + const circle::OperatorCodeT &opcode = *op_codes[index]; + + return opcode.builtin_code; +} + +std::string CircleReader::opcode_name(const circle::OperatorT &op) const +{ + const auto &op_codes = opcodes(); + uint32_t index = op.opcode_index; + assert(index < op_codes.size()); + const circle::OperatorCodeT &opcode = *op_codes[index]; + + if (!is_valid(opcode)) + { + std::ostringstream oss; + oss << "(invalid: " << index << ")"; + return oss.str(); + } + + return ::luci::opcode_name(opcode); +} + +bool CircleReader::parse(const circle::Model *model) +{ + assert(model != nullptr); + + _model.reset(model->UnPack()); + + return true; +} + +bool CircleReader::select_subgraph(uint32_t sgindex) +{ + if (_model->subgraphs.size() <= sgindex) + { + assert(false); + return false; + } + + _current_subgraph = _model->subgraphs[sgindex].get(); + + return true; +} + +} // namespace luci diff --git a/compiler/luci/import/src/GraphBuilder.cpp b/compiler/luci/import/src/GraphBuilder.cpp new file mode 100644 index 000000000..e0ec9ded5 --- /dev/null +++ b/compiler/luci/import/src/GraphBuilder.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2020 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 "luci/Import/GraphBuilder.h" + +namespace luci +{ + +void GraphBuilder::build(const circle::OperatorT &op, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + const std::vector<int32_t> &inputs = op.inputs; + const std::vector<int32_t> &outputs = op.outputs; + const auto &tensors = context->reader()->tensors(); + + std::vector<CircleNode *> input_nodes; + for (const int32_t input_tensor_index : inputs) + { + input_nodes.push_back(context->nodefinder()->node(input_tensor_index)); + } + + CircleNode *node = build_node(op, input_nodes, context->graph()); + + // Set up node parameters. + assert(outputs.size() == 1); + { + const circle::TensorT &output_tensor = *tensors[outputs[0]]; + + node->name(tensor_name(output_tensor)); + + auto quantization = tensor_quantization(output_tensor); + if (quantization) + { + auto quantparam = luci_quantparam(quantization); + if (quantparam) + node->quantparam(std::move(quantparam)); + } + } + + // Register node's only output. + assert(outputs.size() == 1); + { + context->nodefinder()->enroll(outputs[0], node); + } +} + +} // namespace luci diff --git a/compiler/luci/import/src/GraphBuilderContext.cpp b/compiler/luci/import/src/GraphBuilderContext.cpp new file mode 100644 index 000000000..a5162ce83 --- /dev/null +++ b/compiler/luci/import/src/GraphBuilderContext.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020 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 "luci/Import/GraphBuilderContext.h" + +#include <luci/Log.h> + +#include <oops/UserExn.h> + +namespace luci +{ + +void IndexNodeFinder::enroll(TensorIndex idx, CircleNode *node) +{ + if (_table.find(idx) != _table.end()) + { + LOGGER(l); + INFO(l) << "[luci] NodeFinder SKIP (" << idx << ") " << node << std::endl; + return; + } + + _table[idx] = node; +} + +CircleNode *IndexNodeFinder::node(TensorIndex idx) const +{ + MapIndexNode_t::const_iterator iter = _table.find(idx); + + assert(iter != _table.end() && iter->second != nullptr); + + return iter->second; +} + +} // namespace luci diff --git a/compiler/luci/import/src/GraphBuilderRegistry.cpp b/compiler/luci/import/src/GraphBuilderRegistry.cpp new file mode 100644 index 000000000..929b71a7d --- /dev/null +++ b/compiler/luci/import/src/GraphBuilderRegistry.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2020 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 "luci/Import/GraphBuilderRegistry.h" + +#include "luci/Import/Nodes.h" + +#include <memory> + +namespace luci +{ + +GraphBuilderRegistry::GraphBuilderRegistry() +{ +#define CIRCLE_NODE(OPCODE, CLASS) add(circle::BuiltinOperator_##OPCODE, std::make_unique<CLASS>()); + + CIRCLE_NODE(ABS, CircleAbsGraphBuilder); // 101 + CIRCLE_NODE(ADD, CircleAddGraphBuilder); // 0 + CIRCLE_NODE(ARG_MAX, CircleArgMaxGraphBuilder); // 56 + CIRCLE_NODE(AVERAGE_POOL_2D, CircleAveragePool2DGraphBuilder); // 1 + CIRCLE_NODE(BATCH_TO_SPACE_ND, CircleBatchToSpaceNDGraphBuilder); // 37 + CIRCLE_NODE(CONCATENATION, CircleConcatenationGraphBuilder); // 2 + CIRCLE_NODE(CONV_2D, CircleConv2DGraphBuilder); // 3 + CIRCLE_NODE(COS, CircleCosGraphBuilder); // 108 + CIRCLE_NODE(DEPTHWISE_CONV_2D, CircleDepthwiseConv2DGraphBuilder); // 4 + CIRCLE_NODE(DIV, CircleDivGraphBuilder); // 42 + CIRCLE_NODE(EQUAL, CircleEqualGraphBuilder); // 71 + CIRCLE_NODE(EXP, CircleExpGraphBuilder); // 47 + CIRCLE_NODE(FULLY_CONNECTED, CircleFullyConnectedGraphBuilder); // 9 + CIRCLE_NODE(LOGICAL_NOT, CircleLogicalNotGraphBuilder); // 87 + CIRCLE_NODE(LOGICAL_OR, CircleLogicalOrGraphBuilder); // 84 + CIRCLE_NODE(MAX_POOL_2D, CircleMaxPool2DGraphBuilder); // 17 + CIRCLE_NODE(MEAN, CircleMeanGraphBuilder); // 40 + CIRCLE_NODE(MUL, CircleMulGraphBuilder); // 18 + CIRCLE_NODE(PACK, CirclePackGraphBuilder); // 83 + CIRCLE_NODE(PAD, CirclePadGraphBuilder); // 34 + CIRCLE_NODE(RELU, CircleReluGraphBuilder); // 19 + CIRCLE_NODE(RESHAPE, CircleReshapeGraphBuilder); // 22 + CIRCLE_NODE(RSQRT, CircleRsqrtGraphBuilder); // 76 + CIRCLE_NODE(SOFTMAX, CircleSoftmaxGraphBuilder); // 25 + CIRCLE_NODE(SUB, CircleSubGraphBuilder); // 41 + CIRCLE_NODE(TRANSPOSE, CircleTransposeGraphBuilder); // 39 + +#undef CIRCLE_NODE + + // BuiltinOperator_DEQUANTIZE = 6, + // BuiltinOperator_EMBEDDING_LOOKUP = 7, + // BuiltinOperator_FLOOR = 8, + // BuiltinOperator_HASHTABLE_LOOKUP = 10, + // BuiltinOperator_L2_NORMALIZATION = 11, + // BuiltinOperator_L2_POOL_2D = 12, + // BuiltinOperator_LOCAL_RESPONSE_NORMALIZATION = 13, + // BuiltinOperator_LOGISTIC = 14, + // BuiltinOperator_LSH_PROJECTION = 15, + // BuiltinOperator_LSTM = 16, + // BuiltinOperator_RELU_N1_TO_1 = 20, + // BuiltinOperator_RELU6 = 21, + // BuiltinOperator_RESIZE_BILINEAR = 23, + // BuiltinOperator_RNN = 24, + // BuiltinOperator_SPACE_TO_DEPTH = 26, + // BuiltinOperator_SVDF = 27, + // BuiltinOperator_TANH = 28, + // BuiltinOperator_CONCAT_EMBEDDINGS = 29, + // BuiltinOperator_SKIP_GRAM = 30, + // BuiltinOperator_CALL = 31, + // BuiltinOperator_CUSTOM = 32, + // BuiltinOperator_EMBEDDING_LOOKUP_SPARSE = 33, + // BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_RNN = 35, + // BuiltinOperator_GATHER = 36, + // BuiltinOperator_SPACE_TO_BATCH_ND = 38, + // BuiltinOperator_SQUEEZE = 43, + // BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM = 44, + // BuiltinOperator_STRIDED_SLICE = 45, + // BuiltinOperator_BIDIRECTIONAL_SEQUENCE_RNN = 46, + // BuiltinOperator_TOPK_V2 = 48, + // BuiltinOperator_SPLIT = 49, + // BuiltinOperator_LOG_SOFTMAX = 50, + // BuiltinOperator_DELEGATE = 51, + // BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM = 52, + // BuiltinOperator_CAST = 53, + // BuiltinOperator_PRELU = 54, + // BuiltinOperator_MAXIMUM = 55, + // BuiltinOperator_ARG_MAX = 56, + // BuiltinOperator_MINIMUM = 57, + // BuiltinOperator_LESS = 58, + // BuiltinOperator_NEG = 59, + // BuiltinOperator_PADV2 = 60, + // BuiltinOperator_GREATER = 61, + // BuiltinOperator_GREATER_EQUAL = 62, + // BuiltinOperator_LESS_EQUAL = 63, + // BuiltinOperator_SELECT = 64, + // BuiltinOperator_SLICE = 65, + // BuiltinOperator_SIN = 66, + // BuiltinOperator_TRANSPOSE_CONV = 67, + // BuiltinOperator_SPARSE_TO_DENSE = 68, + // BuiltinOperator_TILE = 69, + // BuiltinOperator_EXPAND_DIMS = 70, + // BuiltinOperator_NOT_EQUAL = 72, + // BuiltinOperator_LOG = 73, + // BuiltinOperator_SUM = 74, + // BuiltinOperator_SQRT = 75, + // BuiltinOperator_SHAPE = 77, + // BuiltinOperator_POW = 78, + // BuiltinOperator_ARG_MIN = 79, + // BuiltinOperator_FAKE_QUANT = 80, + // BuiltinOperator_REDUCE_PROD = 81, + // BuiltinOperator_REDUCE_MAX = 82, + // BuiltinOperator_ONE_HOT = 85, + // BuiltinOperator_LOGICAL_AND = 86, + // BuiltinOperator_UNPACK = 88, + // BuiltinOperator_REDUCE_MIN = 89, + // BuiltinOperator_FLOOR_DIV = 90, + // BuiltinOperator_REDUCE_ANY = 91, + // BuiltinOperator_SQUARE = 92, + // BuiltinOperator_ZEROS_LIKE = 93, + // BuiltinOperator_FILL = 94, + // BuiltinOperator_FLOOR_MOD = 95, + // BuiltinOperator_RANGE = 96, + // BuiltinOperator_RESIZE_NEAREST_NEIGHBOR = 97, + // BuiltinOperator_LEAKY_RELU = 98, + // BuiltinOperator_SQUARED_DIFFERENCE = 99, + // BuiltinOperator_MIRROR_PAD = 100, + // BuiltinOperator_SPLIT_V = 102, + // BuiltinOperator_UNIQUE = 103, + // BuiltinOperator_CEIL = 104, + // BuiltinOperator_REVERSE_V2 = 105, + // BuiltinOperator_ADD_N = 106, + // BuiltinOperator_GATHER_ND = 107, + // BuiltinOperator_WHERE = 109, + // BuiltinOperator_RANK = 110, + // BuiltinOperator_ELU = 111, + // BuiltinOperator_REVERSE_SEQUENCE = 112, + // BuiltinOperator_MATRIX_DIAG = 113, + // BuiltinOperator_QUANTIZE = 114, + // BuiltinOperator_MATRIX_SET_DIAG = 115, + // BuiltinOperator_ROUND = 116, + // BuiltinOperator_HARD_SWISH = 117, + // BuiltinOperator_IF = 118, + // BuiltinOperator_WHILE = 119, + // BuiltinOperator_NON_MAX_SUPPRESSION_V4 = 120, + // BuiltinOperator_NON_MAX_SUPPRESSION_V5 = 121, + // BuiltinOperator_SCATTER_ND = 122, + // BuiltinOperator_SELECT_V2 = 123, + // BuiltinOperator_DENSIFY = 124, + // BuiltinOperator_SEGMENT_SUM = 125, + // BuiltinOperator_BATCH_MATMUL = 126, + // BuiltinOperator_INSTANCE_NORM = 254, +} + +} // namespace luci diff --git a/compiler/luci/import/src/Importer.cpp b/compiler/luci/import/src/Importer.cpp new file mode 100644 index 000000000..964c47633 --- /dev/null +++ b/compiler/luci/import/src/Importer.cpp @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2020 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 "luci/Importer.h" + +#include "luci/Import/GraphBuilder.h" +#include "luci/Import/GraphBuilderContext.h" +#include "luci/Import/GraphBuilderRegistry.h" +#include "luci/Import/CircleReader.h" +#include "luci/Import/Nodes/CircleConst.h" + +#include <luci/IR/Module.h> +#include <luci/IR/CircleNodes.h> +#include <luci/Log.h> +#include <luci/LogHelper.h> + +#include <oops/UserExn.h> + +#include <memory> + +namespace +{ + +void convert_graph(const luci::GraphBuilderSource &source, luci::CircleReader &reader, + loco::Graph *graph) +{ + LOGGER(l); + + auto nodefinder = std::make_unique<luci::IndexNodeFinder>(); + + luci::GraphBuilderContext gb_context(graph, &reader, nodefinder.get()); + + const auto &operators = reader.operators(); + const auto &tensors = reader.tensors(); + + // graph inputs; there are no input nodes in TFlite but just Tensors + // creating virtual input nodes will make possible to connect nodes that uses them + // all attributes of tensor should be copied to CircleInput node + for (const auto input : reader.inputs()) + { + auto input_node = graph->nodes()->create<luci::CircleInput>(); + assert(input_node != nullptr); + const circle::TensorT &tensor = *tensors[input]; + + auto tname = luci::tensor_name(tensor); + input_node->name(tname); + auto quantization = luci::tensor_quantization(tensor); + if (quantization) + { + auto quantparam = luci::luci_quantparam(quantization); + if (quantparam.get()) + input_node->quantparam(std::move(quantparam)); + } + + INFO(l) << "[luci] NodeFinder INPUT(" << input << ") = " << input_node << std::endl; + nodefinder->enroll(input, input_node); + + // Shape of Input + const std::vector<int32_t> &input_dims = tensor.shape; // in NHWC + input_node->rank(input_dims.size()); + for (uint32_t r = 0; r < input_dims.size(); ++r) + input_node->dim(r) = loco::Dimension(input_dims[r]); + + // Data type of Input + auto dtype = luci::luci_datatype(tensor.type); + input_node->dtype(dtype); + + // Name + auto graph_input = graph->inputs()->create(); + graph_input->name(tname); + + // Set GraphInputOutputIndex for graph + input_node->index(graph_input->index()); + + // Data type + graph_input->dtype(dtype); + } + + // Create CircleConst nodes for constant tensors. + const auto &buffers = reader.buffers(); + for (uint32_t i = 0; i < tensors.size(); ++i) + { + const circle::TensorT &tensor = *tensors[i]; + const std::vector<uint8_t> &buffer = buffers[tensor.buffer]->data; + if (!buffer.empty()) + { + luci::CircleConst *const_node = luci::create_circleconst(&gb_context, i); + nodefinder->enroll(i, const_node); + } + } + + // Import the operators. + // Note that operators in model are stored in execution order. This means that when importing + // an operator, its input operators have already been imported. We exploit this fact to set up + // node's inputs right after creating the node. + for (uint32_t i = 0; i < operators.size(); ++i) + { + const circle::OperatorT &op = *operators[i]; + circle::BuiltinOperator builtincode = reader.builtin_code(op); + + if (const auto *builder = source.lookup(builtincode)) + { + luci::GraphBuilder::ValidateArgs args(op, reader); + if (!builder->validate(args)) + { + throw oops::UserExn("Invalid operator", reader.opcode_name(op)); + } + + builder->build(op, &gb_context); + } + else + { + throw oops::UserExn("Not supported", reader.opcode_name(op)); + } + } + + // graph outputs + for (auto output : reader.outputs()) + { + auto output_node = graph->nodes()->create<luci::CircleOutput>(); + assert(output_node != nullptr); + output_node->from(nodefinder->node(output)); + + INFO(l) << "[luci] NodeFinder OUTPUT(" << output << ") = " << output_node << std::endl; + + // set the graph output name and node object + const circle::TensorT &tensor = *tensors[output]; + auto graph_output = graph->outputs()->create(); + std::string tname = luci::tensor_name(tensor); + graph_output->name("output_" + tname); + + // Set GraphInputOutputIndex for graph + output_node->index(graph_output->index()); + + // Shape of Output + auto output_shape = std::make_unique<loco::TensorShape>(); + const std::vector<int32_t> &output_dims = tensor.shape; // in NHWC + output_shape->rank(output_dims.size()); + for (uint32_t r = 0; r < output_dims.size(); ++r) + output_shape->dim(r) = loco::Dimension(output_dims[r]); + graph_output->shape(std::move(output_shape)); + + // Data type + auto dtype = luci::luci_datatype(tensor.type); + graph_output->dtype(dtype); + } +} + +class ValidateCollector final : public loco::ErrorListener +{ +public: + void notify(const loco::ErrorDetail<loco::ErrorCategory::MissingArgument> &d) override + { + LOGGER(l); + INFO(l) << "[luci] GraphValidate error " << d.node() << "(" << d.index() << ")" << std::endl; + } +}; + +} // namespace + +namespace luci +{ + +Importer::Importer() +{ + // DO NOTHING +} + +std::unique_ptr<loco::Graph> Importer::import(const circle::Model *model) const +{ + auto graph = loco::make_graph(); + + const GraphBuilderSource *source_ptr = &GraphBuilderRegistry::get(); + + if (_source != nullptr) + { + // Use user-defined GraphBuilderSource + source_ptr = _source; + } + + CircleReader reader; + if (!reader.parse(model)) + return nullptr; + + // TODO support multiple subgraph when Circle supports + assert(reader.num_subgraph() == 1); + if (!reader.select_subgraph(0)) + return nullptr; + + // Convert circle::Model to loco::Graph + convert_graph(*source_ptr, reader, graph.get()); + + LOGGER(l); + INFO(l) << fmt(graph.get()); + + assert(loco::valid(graph.get(), std::make_unique<ValidateCollector>())); + + return std::move(graph); +} + +std::unique_ptr<Module> Importer::importModule(const circle::Model *model) const +{ + auto module = make_module(); + + const GraphBuilderSource *source_ptr = &GraphBuilderRegistry::get(); + + if (_source != nullptr) + { + // Use user-defined GraphBuilderSource + source_ptr = _source; + } + + CircleReader reader; + if (!reader.parse(model)) + return nullptr; + + for (uint32_t g = 0; g < reader.num_subgraph(); ++g) + { + auto graph = loco::make_graph(); + + if (!reader.select_subgraph(g)) + return nullptr; + + graph->name(reader.name()); + + // Convert circle::Model to loco::Graph + convert_graph(*source_ptr, reader, graph.get()); + + LOGGER(l); + INFO(l) << fmt(graph.get()); + + assert(loco::valid(graph.get(), std::make_unique<ValidateCollector>())); + + module->add(std::move(graph)); + } + + return std::move(module); +} + +} // namespace luci diff --git a/compiler/luci/import/src/Importer.test.cpp b/compiler/luci/import/src/Importer.test.cpp new file mode 100644 index 000000000..4426e15fd --- /dev/null +++ b/compiler/luci/import/src/Importer.test.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2020 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 "luci/Importer.h" + +#include <loco.h> + +#include <gtest/gtest.h> + +TEST(TensorFlowLiteImport, Dummy) { luci::Importer import; } diff --git a/compiler/luci/import/src/Nodes/CircleAbs.cpp b/compiler/luci/import/src/Nodes/CircleAbs.cpp new file mode 100644 index 000000000..9054986bd --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleAbs.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleAbs.h" + +#include <luci/IR/Nodes/CircleAbs.h> + +#include <loco.h> + +namespace luci +{ +bool CircleAbsGraphBuilder::validate(const ValidateArgs &args) const +{ + if (args.op.inputs.size() != 1) + return false; + + // TODO Support type check + return true; +} + +CircleNode *CircleAbsGraphBuilder::build_node(const circle::OperatorT &, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleAbs>(); + node->x(inputs[0]); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleAdd.cpp b/compiler/luci/import/src/Nodes/CircleAdd.cpp new file mode 100644 index 000000000..3b1bb734f --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleAdd.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleAdd.h" + +#include <luci/IR/Nodes/CircleAdd.h> + +#include <loco.h> + +namespace luci +{ + +bool CircleAddGraphBuilder::validate(const ValidateArgs &args) const +{ + if (args.op.inputs.size() != 2) + return false; + + return true; +} + +CircleNode *CircleAddGraphBuilder::build_node(const circle::OperatorT &op, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleAdd>(); + node->x(inputs[0]); + node->y(inputs[1]); + + const auto *options = op.builtin_options.AsAddOptions(); + node->fusedActivationFunction(luci_actfunc(options->fused_activation_function)); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleArgMax.cpp b/compiler/luci/import/src/Nodes/CircleArgMax.cpp new file mode 100644 index 000000000..2679827e2 --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleArgMax.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleArgMax.h" + +#include <luci/IR/Nodes/CircleArgMax.h> + +#include <loco.h> + +namespace luci +{ + +bool CircleArgMaxGraphBuilder::validate(const ValidateArgs &args) const +{ + if (args.op.inputs.size() != 2) + return false; + + return true; +} + +CircleNode *CircleArgMaxGraphBuilder::build_node(const circle::OperatorT &op, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleArgMax>(); + node->input(inputs[0]); + node->dimension(inputs[1]); + + const auto *options = op.builtin_options.AsArgMaxOptions(); + node->output_type(luci_datatype(options->output_type)); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleAveragePool2D.cpp b/compiler/luci/import/src/Nodes/CircleAveragePool2D.cpp new file mode 100644 index 000000000..cfc3cf126 --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleAveragePool2D.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleAveragePool2D.h" + +#include <luci/IR/Nodes/CircleAveragePool2D.h> + +namespace luci +{ + +bool CircleAveragePool2DGraphBuilder::validate(const ValidateArgs &args) const +{ + if (args.op.inputs.size() != 1) + return false; + + return true; +} + +CircleNode *CircleAveragePool2DGraphBuilder::build_node(const circle::OperatorT &op, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleAveragePool2D>(); + node->value(inputs[0]); + + const auto *options = op.builtin_options.AsPool2DOptions(); + node->padding(luci_padding(options->padding)); + node->stride()->w(options->stride_w); + node->stride()->h(options->stride_h); + node->filter()->w(options->filter_width); + node->filter()->h(options->filter_height); + node->fusedActivationFunction(luci_actfunc(options->fused_activation_function)); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleBatchToSpaceND.cpp b/compiler/luci/import/src/Nodes/CircleBatchToSpaceND.cpp new file mode 100644 index 000000000..4bbfadf64 --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleBatchToSpaceND.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleBatchToSpaceND.h" + +#include <luci/IR/Nodes/CircleBatchToSpaceND.h> + +#include <loco.h> + +#include <cassert> + +namespace luci +{ + +bool CircleBatchToSpaceNDGraphBuilder::validate(const ValidateArgs &args) const +{ + const auto &inputs = args.op.inputs; + if (inputs.size() != 3) + return false; + + // input 1 and 2 should have INT32/INT64 type + const auto &tensors = args.reader.tensors(); + const auto &tensor_1 = tensors.at(inputs[1]); + switch (tensor_1->type) + { + case circle::TensorType_INT32: + case circle::TensorType_INT64: + break; + default: + return false; + } + const auto &tensor_2 = tensors.at(inputs[2]); + switch (tensor_2->type) + { + case circle::TensorType_INT32: + case circle::TensorType_INT64: + break; + default: + return false; + } + + // Only support input shape dimension 3 and 4 only + const auto &tensor_0 = tensors.at(inputs[0]); + const auto t_0_s = tensor_0->shape.size(); + if (t_0_s != 3 && t_0_s != 4) + return false; + + // TODO check input shape + + return true; +} + +CircleNode *CircleBatchToSpaceNDGraphBuilder::build_node(const circle::OperatorT &, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleBatchToSpaceND>(); + node->input(inputs[0]); + node->block_shape(inputs[1]); + node->crops(inputs[2]); + + // No options for BatchToSpaceND + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleConcatenation.cpp b/compiler/luci/import/src/Nodes/CircleConcatenation.cpp new file mode 100644 index 000000000..7fc616aa0 --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleConcatenation.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleConcatenation.h" + +#include <luci/IR/Nodes/CircleConcatenation.h> + +namespace luci +{ + +bool CircleConcatenationGraphBuilder::validate(const ValidateArgs &args) const +{ + if (args.op.inputs.size() < 1) + return false; + + if (args.op.outputs.size() != 1) + return false; + + return true; +} + +CircleNode *CircleConcatenationGraphBuilder::build_node(const circle::OperatorT &op, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleConcatenation>(inputs.size()); + for (uint32_t i = 0; i < inputs.size(); ++i) + { + node->values(i, inputs[i]); + } + + const auto *options = op.builtin_options.AsConcatenationOptions(); + node->axis(options->axis); + node->fusedActivationFunction(luci_actfunc(options->fused_activation_function)); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleConst.cpp b/compiler/luci/import/src/Nodes/CircleConst.cpp new file mode 100644 index 000000000..1d798983b --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleConst.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleConst.h" + +#include <luci/IR/Nodes/CircleConst.h> +#include <luci/Log.h> + +#include <loco.h> +#include <oops/UserExn.h> + +#include <cassert> + +namespace luci +{ + +template <loco::DataType DT> +static void copy_data(const std::vector<uint8_t> &raw_data, uint32_t num_elements, + CircleConst *const_node) +{ + using T = typename loco::DataTypeImpl<DT>::Type; + + assert(raw_data.size() == num_elements * sizeof(T)); + const auto *data = reinterpret_cast<const T *>(raw_data.data()); + + const_node->size<DT>(num_elements); + for (uint32_t i = 0; i < num_elements; ++i) + { + const_node->at<DT>(i) = data[i]; + } +} + +// +// circleconst_from_tensor() ? +// +CircleConst *create_circleconst(GraphBuilderContext *context, int32_t tensor_index) +{ + LOGGER(l); + + auto graph = context->graph(); + auto reader = context->reader(); + const auto &tensors = reader->tensors(); + + // (1) create CircleConst + auto const_node = graph->nodes()->create<CircleConst>(); + const circle::TensorT &const_tensor = *tensors[tensor_index]; + const_node->name(tensor_name(const_tensor)); + auto quantization = luci::tensor_quantization(const_tensor); + if (quantization) + { + auto quantparam = luci::luci_quantparam(quantization); + if (quantparam.get()) + const_node->quantparam(std::move(quantparam)); + } + + INFO(l) << "[luci] NodeFinder const_node(" << tensor_index << ") -> " << const_node << std::endl; + + // (2) set data_type to CircleConst + const_node->dtype(luci_datatype(const_tensor.type)); + + // (3) set shape to CicleConst + std::vector<int32_t> const_dims = const_tensor.shape; // in NHWC + const_node->rank(const_dims.size()); + uint32_t num_elements = 1; + for (uint32_t r = 0; r < const_dims.size(); ++r) + { + const_node->dim(r) = loco::Dimension(const_dims[r]); + num_elements = num_elements * const_dims[r]; + } + + // (4) constant values from circle buffer + const std::vector<uint8_t> &buffer = reader->buffers()[const_tensor.buffer]->data; + if (buffer.empty()) + throw oops::UserExn("Empty buffer"); + + switch (luci_datatype(const_tensor.type)) + { + case loco::DataType::FLOAT32: + copy_data<loco::DataType::FLOAT32>(buffer, num_elements, const_node); + break; + + case loco::DataType::U8: + copy_data<loco::DataType::U8>(buffer, num_elements, const_node); + break; + + case loco::DataType::S32: + copy_data<loco::DataType::S32>(buffer, num_elements, const_node); + break; + + default: + throw oops::UserExn("Unsupported tensor type", circle::EnumNameTensorType(const_tensor.type)); + } + + return const_node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleConv2D.cpp b/compiler/luci/import/src/Nodes/CircleConv2D.cpp new file mode 100644 index 000000000..ec9dce0d2 --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleConv2D.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleConv2D.h" + +#include <luci/IR/Nodes/CircleConv2D.h> + +#include <loco.h> + +#include <cassert> + +namespace luci +{ + +bool CircleConv2DGraphBuilder::validate(const ValidateArgs &args) const +{ + // Circle Conv2D may not have a bias but we won't support this + if (args.op.inputs.size() != 3) + return false; + + return true; +} + +CircleNode *CircleConv2DGraphBuilder::build_node(const circle::OperatorT &op, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleConv2D>(); + node->input(inputs[0]); + node->filter(inputs[1]); + // For now, bias is required (checked in `verify` method). + assert(inputs.size() == 3); + node->bias(inputs[2]); + + const auto *options = op.builtin_options.AsConv2DOptions(); + node->padding(luci_padding(options->padding)); + node->stride()->w(options->stride_w); + node->stride()->h(options->stride_h); + node->fusedActivationFunction(luci_actfunc(options->fused_activation_function)); + // FIXME Check dilation_w_factor, dilation_h_factor. + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleCos.cpp b/compiler/luci/import/src/Nodes/CircleCos.cpp new file mode 100644 index 000000000..5f61cc7f6 --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleCos.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleCos.h" + +#include <luci/IR/Nodes/CircleCos.h> + +#include <loco.h> + +namespace luci +{ + +bool CircleCosGraphBuilder::validate(const ValidateArgs &args) const +{ + if (args.op.inputs.size() != 1) + return false; + + return true; +} + +CircleNode *CircleCosGraphBuilder::build_node(const circle::OperatorT &, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleCos>(); + node->x(inputs[0]); + + // No options for Cos + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleDepthwiseConv2D.cpp b/compiler/luci/import/src/Nodes/CircleDepthwiseConv2D.cpp new file mode 100644 index 000000000..c6d3b1f1e --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleDepthwiseConv2D.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleDepthwiseConv2D.h" + +#include <luci/IR/Nodes/CircleDepthwiseConv2D.h> + +#include <oops/UserExn.h> + +namespace luci +{ + +bool CircleDepthwiseConv2DGraphBuilder::validate(const ValidateArgs &args) const +{ + // Circle DepthwiseConv2D may not have a bias but we won't support this + if (args.op.inputs.size() != 3 && args.op.inputs.size() != 2) + return false; + + if (args.op.outputs.size() != 1) + return false; + + return true; +} + +CircleNode *CircleDepthwiseConv2DGraphBuilder::build_node(const circle::OperatorT &op, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleDepthwiseConv2D>(); + node->input(inputs[0]); + node->filter(inputs[1]); + if (inputs.size() != 3) + throw oops::UserExn("DepthwiseConv2d without bias is unsupported"); + node->bias(inputs[2]); + + const auto *options = op.builtin_options.AsDepthwiseConv2DOptions(); + node->padding(luci_padding(options->padding)); + node->stride()->w(options->stride_w); + node->stride()->h(options->stride_h); + node->depthMultiplier(options->depth_multiplier); + node->fusedActivationFunction(luci_actfunc(options->fused_activation_function)); + // FIXME Check dilation_w_factor, dilation_h_factor. + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleDiv.cpp b/compiler/luci/import/src/Nodes/CircleDiv.cpp new file mode 100644 index 000000000..d09cfb815 --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleDiv.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleDiv.h" + +#include <luci/IR/Nodes/CircleDiv.h> + +namespace luci +{ + +bool CircleDivGraphBuilder::validate(const ValidateArgs &args) const +{ + if (args.op.inputs.size() != 2) + return false; + + if (args.op.outputs.size() != 1) + return false; + + return true; +} + +CircleNode *CircleDivGraphBuilder::build_node(const circle::OperatorT &op, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto node = graph->nodes()->create<CircleDiv>(); + node->x(inputs[0]); + node->y(inputs[1]); + + const auto *options = op.builtin_options.AsDivOptions(); + node->fusedActivationFunction(luci_actfunc(options->fused_activation_function)); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleEqual.cpp b/compiler/luci/import/src/Nodes/CircleEqual.cpp new file mode 100644 index 000000000..a53f6e94b --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleEqual.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleEqual.h" + +#include <luci/IR/Nodes/CircleEqual.h> + +#include <loco.h> + +namespace luci +{ + +bool CircleEqualGraphBuilder::validate(const ValidateArgs &args) const +{ + const auto &inputs = args.op.inputs; + + if (inputs.size() != 2) + { + return false; + } + + const auto &tensors = args.reader.tensors(); + + return tensors[inputs[0]]->type == tensors[inputs[1]]->type; +} + +CircleNode *CircleEqualGraphBuilder::build_node(const circle::OperatorT &, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleEqual>(); + node->x(inputs[0]); + node->y(inputs[1]); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleExp.cpp b/compiler/luci/import/src/Nodes/CircleExp.cpp new file mode 100644 index 000000000..44fc93d09 --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleExp.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleExp.h" + +#include <luci/IR/Nodes/CircleAbs.h> + +#include <loco.h> + +namespace luci +{ + +bool CircleExpGraphBuilder::validate(const ValidateArgs &args) const +{ + const auto &inputs = args.op.inputs; + if (inputs.size() != 1) + return false; + + // input type check + const auto &tensors = args.reader.tensors(); + const auto &tensor = tensors.at(inputs[0]); + switch (tensor->type) + { + case circle::TensorType_FLOAT16: + case circle::TensorType_FLOAT32: + case circle::TensorType_FLOAT64: + break; + // TODO support TensorType_COMPLEX64, complex128, bfloat16 + default: + return false; + } + + return true; +} + +CircleNode *CircleExpGraphBuilder::build_node(const circle::OperatorT &, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleAbs>(); + node->x(inputs[0]); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleFullyConnected.cpp b/compiler/luci/import/src/Nodes/CircleFullyConnected.cpp new file mode 100644 index 000000000..8f74fe9ce --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleFullyConnected.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleFullyConnected.h" + +#include <luci/IR/Nodes/CircleFullyConnected.h> + +#include <loco.h> +#include <oops/UserExn.h> + +namespace luci +{ + +bool CircleFullyConnectedGraphBuilder::validate(const ValidateArgs &args) const +{ + if (args.op.inputs.size() != 3) + return false; + + return true; +} + +CircleNode *CircleFullyConnectedGraphBuilder::build_node(const circle::OperatorT &op, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleFullyConnected>(); + node->input(inputs[0]); + node->weights(inputs[1]); + node->bias(inputs[2]); + + const auto *options = op.builtin_options.AsFullyConnectedOptions(); + node->fusedActivationFunction(luci_actfunc(options->fused_activation_function)); + if (options->weights_format != circle::FullyConnectedOptionsWeightsFormat_DEFAULT) + { + throw oops::UserExn( + "Unsupported weights format", + circle::EnumNameFullyConnectedOptionsWeightsFormat(options->weights_format)); + } + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleLogicalNot.cpp b/compiler/luci/import/src/Nodes/CircleLogicalNot.cpp new file mode 100644 index 000000000..b1ed3ea37 --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleLogicalNot.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleLogicalNot.h" + +#include <luci/IR/Nodes/CircleLogicalNot.h> + +#include <loco.h> + +namespace luci +{ + +bool CircleLogicalNotGraphBuilder::validate(const ValidateArgs &args) const +{ + if (args.op.inputs.size() != 1) + return false; + + // Only BOOL type is allowed for the input + const auto &inputs = args.op.inputs; + const auto &tensors = args.reader.tensors(); + const auto &tensor = tensors.at(inputs[0]); + if (tensor->type != circle::TensorType::TensorType_BOOL) + return false; + + return true; +} + +CircleNode *CircleLogicalNotGraphBuilder::build_node(const circle::OperatorT &, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleLogicalNot>(); + node->x(inputs[0]); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleLogicalOr.cpp b/compiler/luci/import/src/Nodes/CircleLogicalOr.cpp new file mode 100644 index 000000000..00eb9c5df --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleLogicalOr.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleLogicalOr.h" + +#include <luci/IR/Nodes/CircleLogicalOr.h> + +#include <loco.h> + +namespace luci +{ + +bool CircleLogicalOrGraphBuilder::validate(const ValidateArgs &args) const +{ + if (args.op.inputs.size() != 2) + return false; + + // Only BOOL type is allowed for inputs + const auto &inputs = args.op.inputs; + const auto &tensors = args.reader.tensors(); + for (auto input : inputs) + { + const auto &tensor = tensors.at(input); + if (tensor->type != circle::TensorType::TensorType_BOOL) + return false; + } + + return true; +} + +CircleNode *CircleLogicalOrGraphBuilder::build_node(const circle::OperatorT &, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleLogicalOr>(); + node->x(inputs[0]); + node->y(inputs[1]); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleMaxPool2D.cpp b/compiler/luci/import/src/Nodes/CircleMaxPool2D.cpp new file mode 100644 index 000000000..1798819cf --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleMaxPool2D.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleMaxPool2D.h" + +#include <luci/IR/Nodes/CircleMaxPool2D.h> + +#include <loco.h> + +namespace luci +{ + +bool CircleMaxPool2DGraphBuilder::validate(const ValidateArgs &args) const +{ + if (args.op.inputs.size() != 1) + return false; + + return true; +} + +CircleNode *CircleMaxPool2DGraphBuilder::build_node(const circle::OperatorT &op, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleMaxPool2D>(); + node->value(inputs[0]); + + const auto *options = op.builtin_options.AsPool2DOptions(); + node->padding(luci_padding(options->padding)); + node->stride()->w(options->stride_w); + node->stride()->h(options->stride_h); + node->filter()->w(options->filter_width); + node->filter()->h(options->filter_height); + node->fusedActivationFunction(luci_actfunc(options->fused_activation_function)); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleMean.cpp b/compiler/luci/import/src/Nodes/CircleMean.cpp new file mode 100644 index 000000000..8261c7b38 --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleMean.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleMean.h" + +#include <luci/IR/Nodes/CircleMean.h> + +namespace luci +{ + +bool CircleMeanGraphBuilder::validate(const ValidateArgs &args) const +{ + if (args.op.inputs.size() != 2) + return false; + + return true; +} + +CircleNode *CircleMeanGraphBuilder::build_node(const circle::OperatorT &op, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleMean>(); + node->input(inputs[0]); + node->reduction_indices(inputs[1]); + + const auto *options = op.builtin_options.AsReducerOptions(); + node->keep_dims(options->keep_dims); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleMul.cpp b/compiler/luci/import/src/Nodes/CircleMul.cpp new file mode 100644 index 000000000..d4412b96b --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleMul.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleMul.h" + +#include <luci/IR/Nodes/CircleMul.h> + +namespace luci +{ + +bool CircleMulGraphBuilder::validate(const ValidateArgs &args) const +{ + if (args.op.inputs.size() != 2) + return false; + + if (args.op.outputs.size() != 1) + return false; + + return true; +} + +CircleNode *CircleMulGraphBuilder::build_node(const circle::OperatorT &op, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleMul>(); + node->x(inputs[0]); + node->y(inputs[1]); + + const auto *options = op.builtin_options.AsMulOptions(); + node->fusedActivationFunction(luci_actfunc(options->fused_activation_function)); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CirclePack.cpp b/compiler/luci/import/src/Nodes/CirclePack.cpp new file mode 100644 index 000000000..6ba6fae11 --- /dev/null +++ b/compiler/luci/import/src/Nodes/CirclePack.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CirclePack.h" + +#include <luci/IR/Nodes/CirclePack.h> + +#include <loco.h> +#include <oops/UserExn.h> + +namespace luci +{ + +bool CirclePackGraphBuilder::validate(const ValidateArgs &args) const +{ + const auto &inputs = args.op.inputs; + const auto &outputs = args.op.outputs; + const auto *options = args.op.builtin_options.AsPackOptions(); + + if (options->values_count < 1) + return false; + + if (inputs.size() != static_cast<uint32_t>(options->values_count)) + return false; + + if (outputs.size() != 1) + return false; + + return true; +} + +CircleNode *CirclePackGraphBuilder::build_node(const circle::OperatorT &op, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CirclePack>(inputs.size()); + for (uint32_t i = 0; i < inputs.size(); ++i) + { + node->values(i, inputs[i]); + } + + const auto *options = op.builtin_options.AsPackOptions(); + node->axis(options->axis); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CirclePad.cpp b/compiler/luci/import/src/Nodes/CirclePad.cpp new file mode 100644 index 000000000..6abcf2d6c --- /dev/null +++ b/compiler/luci/import/src/Nodes/CirclePad.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CirclePad.h" + +#include <luci/IR/Nodes/CirclePad.h> + +#include <loco.h> + +namespace luci +{ + +bool CirclePadGraphBuilder::validate(const ValidateArgs &args) const +{ + if (args.op.inputs.size() != 2) + return false; + + // TODO do attribute checks + + return true; +} + +CircleNode *CirclePadGraphBuilder::build_node(const circle::OperatorT &op, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CirclePad>(); + node->input(inputs[0]); + node->paddings(inputs[1]); + + const auto *options = op.builtin_options.AsPadOptions(); + (void)options; // There are no options. + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleRelu.cpp b/compiler/luci/import/src/Nodes/CircleRelu.cpp new file mode 100644 index 000000000..056268a5b --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleRelu.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleRelu.h" + +#include <luci/IR/Nodes/CircleRelu.h> + +#include <loco.h> + +namespace luci +{ + +bool CircleReluGraphBuilder::validate(const ValidateArgs &args) const +{ + if (args.op.inputs.size() != 1) + return false; + + if (args.op.outputs.size() != 1) + return false; + + return true; +} + +CircleNode *CircleReluGraphBuilder::build_node(const circle::OperatorT &, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleRelu>(); + node->features(inputs[0]); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleReshape.cpp b/compiler/luci/import/src/Nodes/CircleReshape.cpp new file mode 100644 index 000000000..c83f143a6 --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleReshape.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleReshape.h" + +#include <luci/IR/Nodes/CircleConst.h> +#include <luci/IR/Nodes/CircleReshape.h> + +namespace luci +{ + +bool CircleReshapeGraphBuilder::validate(const ValidateArgs &args) const +{ + if (args.op.inputs.size() != 1 && args.op.inputs.size() != 2) + return false; + + if (args.op.outputs.size() != 1) + return false; + + return true; +} + +static void setup_shape_attribute(const std::vector<int32_t> &shape, CircleReshape *node) +{ + node->newShape()->rank(shape.size()); + for (uint32_t i = 0; i < shape.size(); ++i) + { + node->newShape()->dim(i) = shape[i]; + } +} + +static CircleNode *create_shape_node(const std::vector<int32_t> &shape, loco::Graph *graph) +{ + auto *shape_node = graph->nodes()->create<luci::CircleConst>(); + shape_node->dtype(loco::DataType::S32); + shape_node->rank(1); + shape_node->dim(0) = shape.size(); + shape_node->size<loco::DataType::S32>(shape.size()); + for (uint32_t i = 0; i < shape.size(); ++i) + { + shape_node->at<loco::DataType::S32>(i) = shape[i]; + } + return shape_node; +} + +CircleNode *CircleReshapeGraphBuilder::build_node(const circle::OperatorT &op, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + // If the second input is not provided, generate it based on the value of the attribute. + // TODO Presence of the second input is the current requirement of the IR. + auto *shape_node = (inputs.size() == 2) ? inputs[1] : nullptr; + if (shape_node == nullptr) + { + const auto *options = op.builtin_options.AsReshapeOptions(); + shape_node = create_shape_node(options->new_shape, graph); + } + + auto *node = graph->nodes()->create<CircleReshape>(); + node->tensor(inputs[0]); + node->shape(shape_node); + + const auto *options = op.builtin_options.AsReshapeOptions(); + setup_shape_attribute(options->new_shape, node); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleRsqrt.cpp b/compiler/luci/import/src/Nodes/CircleRsqrt.cpp new file mode 100644 index 000000000..b5de0b575 --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleRsqrt.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleRsqrt.h" + +#include <luci/IR/Nodes/CircleRsqrt.h> + +#include <loco.h> + +namespace luci +{ + +bool CircleRsqrtGraphBuilder::validate(const ValidateArgs &args) const +{ + const auto &inputs = args.op.inputs; + if (inputs.size() != 1) + return false; + + // Must be one of the following types + // bfloat16, half (float16), float32, float64, complex64, complex128 + // Currently, circle supports float16, float32, complex64 + const auto &tensors = args.reader.tensors(); + const auto &tensor = tensors.at(inputs[0]); + switch (tensor->type) + { + case circle::TensorType_FLOAT16: + case circle::TensorType_FLOAT32: + case circle::TensorType_COMPLEX64: + break; + default: + return false; + } + + return true; +} + +CircleNode *CircleRsqrtGraphBuilder::build_node(const circle::OperatorT &, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleRsqrt>(); + node->x(inputs[0]); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleSoftmax.cpp b/compiler/luci/import/src/Nodes/CircleSoftmax.cpp new file mode 100644 index 000000000..0d316e18c --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleSoftmax.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleSoftmax.h" + +#include <luci/IR/Nodes/CircleSoftmax.h> + +#include <loco.h> + +namespace luci +{ + +bool CircleSoftmaxGraphBuilder::validate(const ValidateArgs &args) const +{ + if (args.op.inputs.size() != 1) + return false; + + // TODO do attribute checks + + return true; +} + +CircleNode *CircleSoftmaxGraphBuilder::build_node(const circle::OperatorT &op, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleSoftmax>(); + node->logits(inputs[0]); + + const auto *options = op.builtin_options.AsSoftmaxOptions(); + node->beta(options->beta); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleSub.cpp b/compiler/luci/import/src/Nodes/CircleSub.cpp new file mode 100644 index 000000000..968e9f51f --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleSub.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleSub.h" + +#include <luci/IR/Nodes/CircleSub.h> + +#include <loco.h> + +namespace luci +{ + +bool CircleSubGraphBuilder::validate(const ValidateArgs &args) const +{ + if (args.op.inputs.size() != 2) + return false; + + if (args.op.outputs.size() != 1) + return false; + + return true; +} + +CircleNode *CircleSubGraphBuilder::build_node(const circle::OperatorT &op, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleSub>(); + node->x(inputs[0]); + node->y(inputs[1]); + + const auto *options = op.builtin_options.AsSubOptions(); + node->fusedActivationFunction(luci_actfunc(options->fused_activation_function)); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleTranspose.cpp b/compiler/luci/import/src/Nodes/CircleTranspose.cpp new file mode 100644 index 000000000..8622c8b80 --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleTranspose.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020 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 "luci/Import/Nodes/CircleTranspose.h" + +#include <luci/IR/Nodes/CircleTranspose.h> + +#include <loco.h> + +namespace luci +{ + +bool CircleTransposeGraphBuilder::validate(const ValidateArgs &args) const +{ + if (args.op.inputs.size() != 2) + return false; + + if (args.op.outputs.size() != 1) + return false; + + return true; +} + +CircleNode *CircleTransposeGraphBuilder::build_node(const circle::OperatorT &op, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleTranspose>(); + node->a(inputs[0]); + node->perm(inputs[1]); + + const auto *options = op.builtin_options.AsTransposeOptions(); + (void)options; + + return node; +} + +} // namespace luci diff --git a/compiler/luci/lang/CMakeLists.txt b/compiler/luci/lang/CMakeLists.txt new file mode 100644 index 000000000..564e777fb --- /dev/null +++ b/compiler/luci/lang/CMakeLists.txt @@ -0,0 +1,22 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(luci_lang SHARED ${SOURCES}) +target_include_directories(luci_lang PRIVATE src) +target_include_directories(luci_lang PUBLIC include) +target_link_libraries(luci_lang PUBLIC loco) +target_link_libraries(luci_lang PUBLIC oops) +target_link_libraries(luci_lang PRIVATE nncc_common) + +install(TARGETS luci_lang DESTINATION lib) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(luci_lang_test ${TESTS}) +target_include_directories(luci_lang_test PRIVATE src) +target_link_libraries(luci_lang_test luci_lang) diff --git a/compiler/luci/lang/README.md b/compiler/luci/lang/README.md new file mode 100644 index 000000000..ea0e3d5da --- /dev/null +++ b/compiler/luci/lang/README.md @@ -0,0 +1,3 @@ +# luci-lang + +`luci-lang` provides TensorFlow Lite and Circle Dialect IR diff --git a/compiler/luci/lang/include/luci/IR/AttrFilter.h b/compiler/luci/lang/include/luci/IR/AttrFilter.h new file mode 100644 index 000000000..7909fa523 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/AttrFilter.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_ATTRFILTER_H__ +#define __LUCI_IR_ATTRFILTER_H__ + +#include <stdint.h> + +namespace luci +{ + +class Filter final +{ +public: + Filter() : _w(1), _h(1) {} + + int32_t w() const { return _w; } + void w(int32_t w) { _w = w; } + + int32_t h() const { return _h; } + void h(int32_t h) { _h = h; } + +private: + int32_t _w; + int32_t _h; +}; + +} // namespace luci + +#endif // __LUCI_IR_ATTRFILTER_H__ diff --git a/compiler/luci/lang/include/luci/IR/AttrFusedActFunc.h b/compiler/luci/lang/include/luci/IR/AttrFusedActFunc.h new file mode 100644 index 000000000..2abae604b --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/AttrFusedActFunc.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_ATTRFUSEDACTFUNC_H__ +#define __LUCI_IR_ATTRFUSEDACTFUNC_H__ + +namespace luci +{ + +// TODO Divide into TFL version and Circle version when they go different approach +enum class FusedActFunc +{ + UNDEFINED, // This is not defined by TFLite or Circle. This was added to + // prevent programming error. + NONE, + RELU, + RELU_N1_TO_1, + RELU6 +}; + +} // namespace luci + +#endif // __LUCI_IR_ATTRFUSEDACTFUNC_H__ diff --git a/compiler/luci/lang/include/luci/IR/AttrPadding.h b/compiler/luci/lang/include/luci/IR/AttrPadding.h new file mode 100644 index 000000000..5c295e0cd --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/AttrPadding.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_ATTRPADDING_H__ +#define __LUCI_IR_ATTRPADDING_H__ + +namespace luci +{ + +enum class Padding +{ + UNDEFINED, // This is not defined by TFLite. This was added to prevent programming error. + + SAME, + VALID, +}; + +} // namespace luci + +#endif // __LUCI_IR_ATTRPADDING_H__ diff --git a/compiler/luci/lang/include/luci/IR/AttrStride.h b/compiler/luci/lang/include/luci/IR/AttrStride.h new file mode 100644 index 000000000..654967d73 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/AttrStride.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_ATTRSTRIDE_H__ +#define __LUCI_IR_ATTRSTRIDE_H__ + +#include <stdint.h> + +namespace luci +{ + +class Stride final +{ +public: + Stride() : _w(1), _h(1) {} + + int32_t w() const { return _w; } + void w(int32_t w) { _w = w; } + + int32_t h() const { return _h; } + void h(int32_t h) { _h = h; } + +private: + int32_t _w; + int32_t _h; +}; + +} // namespace luci + +#endif // __LUCI_IR_ATTRSTRIDE_H__ diff --git a/compiler/luci/lang/include/luci/IR/CircleDialect.h b/compiler/luci/lang/include/luci/IR/CircleDialect.h new file mode 100644 index 000000000..1b25dc9c2 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/CircleDialect.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLEDIALECT_H__ +#define __LUCI_IR_CIRCLEDIALECT_H__ + +#include <loco/IR/Dialect.h> + +namespace luci +{ + +/** + * @brief A singleton for Circle Dialect + */ +class CircleDialect final : public loco::Dialect +{ +private: + CircleDialect(); + +public: + CircleDialect(const CircleDialect &) = delete; + CircleDialect(CircleDialect &&) = delete; + +public: + static loco::Dialect *get(void); +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEDIALECT_H__ diff --git a/compiler/luci/lang/include/luci/IR/CircleNode.h b/compiler/luci/lang/include/luci/IR/CircleNode.h new file mode 100644 index 000000000..92816ef04 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/CircleNode.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLENODE_H__ +#define __LUCI_IR_CIRCLENODE_H__ + +#include "CircleNodeDecl.h" +#include "CircleNodeImpl.h" + +#endif // __LUCI_IR_CIRCLENODE_H__ diff --git a/compiler/luci/lang/include/luci/IR/CircleNodeDecl.h b/compiler/luci/lang/include/luci/IR/CircleNodeDecl.h new file mode 100644 index 000000000..b87bdf9d0 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/CircleNodeDecl.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLENODEDECL_H__ +#define __LUCI_IR_CIRCLENODEDECL_H__ + +#include <loco/IR/Node.h> +#include <loco/IR/Dialect.h> + +#include "CircleOpcode.h" +#include "CircleNodeVisitor.forward.h" +#include "CircleQuantParam.h" + +#include <memory> + +namespace luci +{ + +using NodeName = std::string; + +struct CircleNode : public loco::Node +{ + virtual ~CircleNode() = default; + + const loco::Dialect *dialect(void) const final; + virtual CircleOpcode opcode(void) const = 0; + + template <typename T> T accept(CircleNodeVisitorBase<T> *) const; + template <typename T> T accept(CircleNodeMutableVisitorBase<T> *); + + NodeName name(void) const { return _name; } + void name(const NodeName &name) { _name = name; } + + CircleQuantParam *quantparam(void) const { return _quantparam.get(); } + void quantparam(std::unique_ptr<CircleQuantParam> &&quantparam) + { + _quantparam = std::move(quantparam); + } + +private: + NodeName _name; + std::unique_ptr<CircleQuantParam> _quantparam; +}; + +template <CircleOpcode Code> struct CircleNodeImpl : public CircleNode +{ + virtual ~CircleNodeImpl() = default; + + uint32_t opnum(void) const final { return static_cast<uint32_t>(Code); } + CircleOpcode opcode(void) const final { return Code; } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLENODEDECL_H__ diff --git a/compiler/luci/lang/include/luci/IR/CircleNodeImpl.h b/compiler/luci/lang/include/luci/IR/CircleNodeImpl.h new file mode 100644 index 000000000..bdcfc9c9d --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/CircleNodeImpl.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLENODEIMPL_H__ +#define __LUCI_IR_CIRCLENODEIMPL_H__ + +#include "CircleNodes.h" +#include "CircleNodeVisitor.h" + +#include <oops/InternalExn.h> + +#include <cassert> + +namespace luci +{ + +template <typename T> T CircleNode::accept(CircleNodeVisitorBase<T> *v) const +{ + switch (this->opcode()) + { +#define CIRCLE_NODE(OPCODE, CLASS) \ + \ + case CircleOpcode::OPCODE: \ + return v->visit(dynamic_cast<const CLASS *>(this)); + +#include "CircleNodes.lst" +#undef CIRCLE_NODE + + default: + break; + } + + INTERNAL_EXN("CircleNode::accept(CircleNodeVisitorBase) not handled"); +} + +template <typename T> T CircleNode::accept(CircleNodeMutableVisitorBase<T> *v) +{ + switch (this->opcode()) + { +#define CIRCLE_NODE(OPCODE, CLASS) \ + \ + case CircleOpcode::OPCODE: \ + return v->visit(dynamic_cast<CLASS *>(this)); + +#include "CircleNodes.lst" +#undef CIRCLE_NODE + + default: + break; + } + + INTERNAL_EXN("CircleNode::accept(CircleNodeMutableVisitorBase) not handled"); +} + +} // namespace luci + +#endif // __LUCI_IR_CIRCLENODEIMPL_H__ diff --git a/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.forward.h b/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.forward.h new file mode 100644 index 000000000..70901ca87 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.forward.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLENODE_VISITOR_FORWARD_H__ +#define __LUCI_IR_CIRCLENODE_VISITOR_FORWARD_H__ + +namespace luci +{ + +// NOTE These forward declarations SHOULD BE aligned with Node delcarations in +// "CircleNodeVisitor.h" +template <typename T> struct CircleNodeVisitorBase; +template <typename T> struct CircleNodeMutableVisitorBase; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLENODE_VISITOR_FORWARD_H__ diff --git a/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.h b/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.h new file mode 100644 index 000000000..43339fe84 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLENODE_VISITOR_H__ +#define __LUCI_IR_CIRCLENODE_VISITOR_H__ + +#include "CircleNode.h" +#include "CircleNodes.h" + +#include <oops/InternalExn.h> + +namespace luci +{ + +/** + * DO NOT use this class. Use CircleNodeVisitor instead. + */ +template <typename T> struct CircleNodeVisitorBase +{ + virtual ~CircleNodeVisitorBase() = default; + +#define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) virtual T visit(const CIRCLE_CLASS *) = 0; + +#include "CircleNodes.lst" +#undef CIRCLE_NODE +}; + +template <typename T> struct CircleNodeVisitor : public CircleNodeVisitorBase<T> +{ + virtual ~CircleNodeVisitor() = default; + +#define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) \ + virtual T visit(const CIRCLE_CLASS *node) { return visit(static_cast<const CircleNode *>(node)); } + +#include "CircleNodes.lst" + +#undef CIRCLE_NODE + + /// @brief Default fallback + virtual T visit(const CircleNode *) { INTERNAL_EXN("CircleNodeVisitor: NYI node"); } +}; + +/** + * DO NOT use this class. Use CircleNodeMutableVisitor instead. + */ +template <typename T> struct CircleNodeMutableVisitorBase +{ + virtual ~CircleNodeMutableVisitorBase() = default; + +#define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) virtual T visit(CIRCLE_CLASS *) = 0; + +#include "CircleNodes.lst" + +#undef CIRCLE_NODE +}; + +template <typename T> struct CircleNodeMutableVisitor : public CircleNodeMutableVisitorBase<T> +{ + virtual ~CircleNodeMutableVisitor() = default; + +#define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) \ + virtual T visit(CIRCLE_CLASS *node) { return visit(static_cast<CircleNode *>(node)); } + +#include "CircleNodes.lst" + +#undef CIRCLE_NODE + + /// @brief Default fallback + virtual T visit(CircleNode *) { INTERNAL_EXN("CircleNodeMutableVisitor: NYI node"); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CircleNode_VISITOR_H__ diff --git a/compiler/luci/lang/include/luci/IR/CircleNodes.h b/compiler/luci/lang/include/luci/IR/CircleNodes.h new file mode 100644 index 000000000..cc822842b --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/CircleNodes.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLENODES_H__ +#define __LUCI_IR_CIRCLENODES_H__ + +#include "Nodes/CircleAbs.h" +#include "Nodes/CircleAdd.h" +#include "Nodes/CircleArgMax.h" +#include "Nodes/CircleAveragePool2D.h" +#include "Nodes/CircleBatchToSpaceND.h" +#include "Nodes/CircleConcatenation.h" +#include "Nodes/CircleConst.h" +#include "Nodes/CircleConv2D.h" +#include "Nodes/CircleCos.h" +#include "Nodes/CircleDepthwiseConv2D.h" +#include "Nodes/CircleDiv.h" +#include "Nodes/CircleEqual.h" +#include "Nodes/CircleExp.h" +#include "Nodes/CircleFullyConnected.h" +#include "Nodes/CircleGather.h" +#include "Nodes/CircleLogicalNot.h" +#include "Nodes/CircleLogicalOr.h" +#include "Nodes/CircleMaximum.h" +#include "Nodes/CircleMaxPool2D.h" +#include "Nodes/CircleMean.h" +#include "Nodes/CircleMul.h" +#include "Nodes/CirclePack.h" +#include "Nodes/CirclePad.h" +#include "Nodes/CircleRelu6.h" +#include "Nodes/CircleRelu.h" +#include "Nodes/CircleReshape.h" +#include "Nodes/CircleRsqrt.h" +#include "Nodes/CircleSoftmax.h" +#include "Nodes/CircleSqrt.h" +#include "Nodes/CircleSquaredDifference.h" +#include "Nodes/CircleSub.h" +#include "Nodes/CircleTransposeConv.h" +#include "Nodes/CircleTranspose.h" +// Circle only +#include "Nodes/CircleInstanceNorm.h" +// Virtual nodes +#include "Nodes/CircleInput.h" +#include "Nodes/CircleOutput.h" + +namespace luci +{ + +/** + * @brief Set both CircleReshape's 2nd input as CircleConst, and newShape attribute + * with same value + * @note Shape inference for TFLReshape forces them to be same + * + * TODO find better place for this helper + */ +void set_new_shape(CircleReshape *node, int32_t *base, uint32_t size); + +} // namespace luci + +#endif // __LUCI_IR_CIRCLENODES_H__ diff --git a/compiler/luci/lang/include/luci/IR/CircleNodes.lst b/compiler/luci/lang/include/luci/IR/CircleNodes.lst new file mode 100644 index 000000000..ca3f7fb0f --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/CircleNodes.lst @@ -0,0 +1,52 @@ +#ifndef CIRCLE_NODE +#error "Define CIRCLE_NODE" +#endif // CIRCLE_NODE + +// +// PLEASE SORT NODE DECLS IN ALPHABETICAL ORDER +// +// Naming rule: Follow names in TensorFlow C++ source; same as TFDialect +// ex) for AvgPool, tensorflow/core/ops/nn_ops.cc +// REGISTER_OP("AvgPool") <-- OPCODE: AvgPool. Prefix `Circle` for CLASS name +// .Input("value: T") <-- Input name is 'value' +// + +CIRCLE_NODE(ABS, luci::CircleAbs) +CIRCLE_NODE(ADD, luci::CircleAdd) +CIRCLE_NODE(ARG_MAX, luci::CircleArgMax) +CIRCLE_NODE(AVERAGE_POOL_2D, luci::CircleAveragePool2D) +CIRCLE_NODE(BATCH_TO_SPACE_ND, luci::CircleBatchToSpaceND) +CIRCLE_NODE(CONCATENATION, luci::CircleConcatenation) +CIRCLE_NODE(CONST, luci::CircleConst) +CIRCLE_NODE(CONV_2D, luci::CircleConv2D) +CIRCLE_NODE(COS, luci::CircleCos) +CIRCLE_NODE(DEPTHWISE_CONV_2D, luci::CircleDepthwiseConv2D) +CIRCLE_NODE(DIV, luci::CircleDiv) +CIRCLE_NODE(EQUAL, luci::CircleEqual) +CIRCLE_NODE(EXP, luci::CircleExp) +CIRCLE_NODE(FULLY_CONNECTED, luci::CircleFullyConnected) +CIRCLE_NODE(GATHER, luci::CircleGather) +CIRCLE_NODE(LOGICAL_NOT, luci::CircleLogicalNot) +CIRCLE_NODE(LOGICAL_OR, luci::CircleLogicalOr) +CIRCLE_NODE(MAXIMUM, luci::CircleMaximum) +CIRCLE_NODE(MAX_POOL_2D, luci::CircleMaxPool2D) +CIRCLE_NODE(MEAN, luci::CircleMean) +CIRCLE_NODE(MUL, luci::CircleMul) +CIRCLE_NODE(PACK, luci::CirclePack) +CIRCLE_NODE(PAD, luci::CirclePad) +CIRCLE_NODE(RELU, luci::CircleRelu) +CIRCLE_NODE(RELU6, luci::CircleRelu6) +CIRCLE_NODE(RESHAPE, luci::CircleReshape) +CIRCLE_NODE(RSQRT, luci::CircleRsqrt) +CIRCLE_NODE(SOFTMAX, luci::CircleSoftmax) +CIRCLE_NODE(SQRT, luci::CircleSqrt) +CIRCLE_NODE(SQUARED_DIFFERENCE, luci::CircleSquaredDifference) +CIRCLE_NODE(SUB, luci::CircleSub) +// TODO TFLTanh +CIRCLE_NODE(TRANSPOSE, luci::CircleTranspose) +CIRCLE_NODE(TRANSPOSE_CONV, luci::CircleTransposeConv) +// Circle Only +CIRCLE_NODE(INSTANCE_NORM, luci::CircleInstanceNorm) +// Virtual node(s) +CIRCLE_NODE(CIRCLEINPUT, luci::CircleInput) +CIRCLE_NODE(CIRCLEOUTPUT, luci::CircleOutput) diff --git a/compiler/luci/lang/include/luci/IR/CircleOpcode.h b/compiler/luci/lang/include/luci/IR/CircleOpcode.h new file mode 100644 index 000000000..703b70da2 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/CircleOpcode.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLEOPCODE_H__ +#define __LUCI_IR_CIRCLEOPCODE_H__ + +namespace luci +{ + +enum class CircleOpcode +{ +#define CIRCLE_NODE(OPCODE, CLASS) OPCODE, +#include "CircleNodes.lst" +#undef CIRCLE_NODE +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEOPCODE_H__ diff --git a/compiler/luci/lang/include/luci/IR/CircleQuantParam.h b/compiler/luci/lang/include/luci/IR/CircleQuantParam.h new file mode 100644 index 000000000..7253e657b --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/CircleQuantParam.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLEQUANTPARAM_H__ +#define __LUCI_IR_CIRCLEQUANTPARAM_H__ + +#include <cstdint> +#include <vector> + +namespace luci +{ + +struct CircleQuantParam +{ + std::vector<float> min; + std::vector<float> max; + std::vector<float> scale; + std::vector<int64_t> zerop; +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEQUANTPARAM_H__ diff --git a/compiler/luci/lang/include/luci/IR/LuciNodeMixins.h b/compiler/luci/lang/include/luci/IR/LuciNodeMixins.h new file mode 100644 index 000000000..b18ac5dc4 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/LuciNodeMixins.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_LUCINODEMIXINS_H__ +#define __LUCI_IR_LUCINODEMIXINS_H__ + +#include "luci/IR/AttrFusedActFunc.h" + +#include <loco/IR/Node.h> +#include <loco/IR/NodeMixins.h> + +namespace luci +{ + +/// @brief enumeration of mixin class +enum class LuciNodeTrait +{ + FusedActFunc, + Bias +}; + +template <LuciNodeTrait T> class LuciNodeMixin; + +template <> class LuciNodeMixin<LuciNodeTrait::FusedActFunc> +{ +public: + LuciNodeMixin() = default; + +public: + FusedActFunc fusedActivationFunction() const { return _fused_act_fun; } + void fusedActivationFunction(FusedActFunc fused_act_fun) { _fused_act_fun = fused_act_fun; } + +private: + FusedActFunc _fused_act_fun = FusedActFunc::UNDEFINED; +}; + +/** + * @brief Mixin class for nodes that has a bias input + */ +template <> class LuciNodeMixin<LuciNodeTrait::Bias> +{ +public: + LuciNodeMixin() = default; + +public: + virtual loco::Node *bias(void) const = 0; /// @brief get the input for bias. + virtual void bias(loco::Node *node) = 0; /// @brief set the input for bias. +}; + +/** + * @brief Nodes with the fixed number of inputs + * + * TODO Deprecated this class, and use loco::FixedArity instead + */ +template <unsigned N, typename Base> class FixedArityNode : public Base +{ +public: + FixedArityNode() + { + for (uint32_t n = 0; n < N; ++n) + { + _args[n] = std::make_unique<loco::Use>(this); + } + } + + virtual ~FixedArityNode() = default; + +public: + unsigned arity(void) const final { return N; } + + loco::Node *arg(uint32_t n) const final { return _args.at(n)->node(); } + + void drop(void) final + { + for (uint32_t n = 0; n < N; ++n) + { + _args.at(n)->node(nullptr); + } + } + +protected: + // This API allows inherited classes to access "_args" field. + loco::Use *at(unsigned n) const { return _args.at(n).get(); } + +private: + std::array<std::unique_ptr<loco::Use>, N> _args; +}; + +} // namespace luci + +#endif // __LUCI_IR_LUCINODEMIXINS_H__ diff --git a/compiler/luci/lang/include/luci/IR/Module.h b/compiler/luci/lang/include/luci/IR/Module.h new file mode 100644 index 000000000..30eac59ce --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Module.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020 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 __LUCI_MODULE_H__ +#define __LUCI_MODULE_H__ + +#include <loco/IR/Graph.h> + +#include <memory> +#include <vector> + +namespace luci +{ + +/** + * @brief Collection of 'loco::Graph's + */ +class Module final +{ +public: + Module() = default; + + // Copy/Move is not allowed for Module + Module(const Module &) = delete; + Module(Module &&) = delete; + + ~Module() = default; + +public: + size_t size(void) const { return _graphs.size(); } + +public: + void add(std::unique_ptr<loco::Graph> &&g); + + /** + * @brief provide main graph + */ + loco::Graph *graph(void) const; + + /** + * @brief provide graph with an index + * + * @note graph(0) is interpreted as a main graph + */ + loco::Graph *graph(size_t idx) const; + + // TODO provide graph accessor with a name + +private: + std::vector<std::unique_ptr<loco::Graph>> _graphs; +}; + +std::unique_ptr<Module> make_module(void); + +} // namespace luci + +#endif // __LUCI_MODULE_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleAbs.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleAbs.h new file mode 100644 index 000000000..45dba15bf --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleAbs.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCELABS_H__ +#define __LUCI_IR_CIRCELABS_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief ABS in Circle + */ +class CircleAbs final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::ABS>> +{ +public: + loco::Node *x(void) const { return at(0)->node(); } + void x(loco::Node *node) { at(0)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCELABS_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleAdd.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleAdd.h new file mode 100644 index 000000000..f26eccd1a --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleAdd.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCELADD_H__ +#define __LUCI_IR_CIRCELADD_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief ADD in Circle + */ +class CircleAdd final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::ADD>>, + public LuciNodeMixin<LuciNodeTrait::FusedActFunc> +{ +public: + loco::Node *x(void) const { return at(0)->node(); } + void x(loco::Node *node) { at(0)->node(node); } + + loco::Node *y(void) const { return at(1)->node(); } + void y(loco::Node *node) { at(1)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCELADD_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleArgMax.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleArgMax.h new file mode 100644 index 000000000..dbc4b2b3a --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleArgMax.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCELARGMAX_H__ +#define __LUCI_IR_CIRCELARGMAX_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief ARG_MAX in Circle + */ +class CircleArgMax final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::ARG_MAX>> +{ +public: + loco::Node *input(void) const { return at(0)->node(); } + void input(loco::Node *node) { at(0)->node(node); } + + loco::Node *dimension(void) const { return at(1)->node(); } + void dimension(loco::Node *node) { at(1)->node(node); } + +public: + loco::DataType output_type(void) const { return _output_type; } + void output_type(loco::DataType ot) { _output_type = ot; } + +private: + loco::DataType _output_type{loco::DataType::S64}; +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCELARGMAX_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleAveragePool2D.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleAveragePool2D.h new file mode 100644 index 000000000..0b43b40c8 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleAveragePool2D.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLEAVERAGEPOOL2D_H__ +#define __LUCI_IR_CIRCLEAVERAGEPOOL2D_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFilter.h" +#include "luci/IR/AttrPadding.h" +#include "luci/IR/AttrStride.h" +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief AVERAGE_POOL_2D in Circle + */ +class CircleAveragePool2D final + : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::AVERAGE_POOL_2D>>, + public LuciNodeMixin<LuciNodeTrait::FusedActFunc> +{ +public: + CircleAveragePool2D() : _padding(Padding::UNDEFINED) { /* empty */} + +public: + loco::Node *value(void) const { return at(0)->node(); } + void value(loco::Node *node) { at(0)->node(node); } + + Padding padding() const { return _padding; } + void padding(Padding padding) { _padding = padding; } + + const Filter *filter(void) const { return &_filter; } + Filter *filter(void) { return &_filter; } + + const Stride *stride(void) const { return &_stride; } + Stride *stride(void) { return &_stride; } + +private: + Padding _padding; + Stride _stride; + Filter _filter; +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEAVERAGEPOOL2D_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleBatchToSpaceND.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleBatchToSpaceND.h new file mode 100644 index 000000000..67c0a2102 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleBatchToSpaceND.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLEBATCHTOSPACEND_H__ +#define __LUCI_IR_CIRCLEBATCHTOSPACEND_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief BATCH_TO_SPACE_ND in Circle + */ +class CircleBatchToSpaceND final + : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::BATCH_TO_SPACE_ND>> +{ +public: + loco::Node *input(void) const { return at(0)->node(); } + void input(loco::Node *node) { at(0)->node(node); } + + loco::Node *block_shape(void) const { return at(1)->node(); } + void block_shape(loco::Node *node) { at(1)->node(node); } + + loco::Node *crops(void) const { return at(2)->node(); } + void crops(loco::Node *node) { at(2)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEBATCHTOSPACEND_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleConcatenation.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleConcatenation.h new file mode 100644 index 000000000..8a6778a2f --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleConcatenation.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLECONCATENATION_H__ +#define __LUCI_IR_CIRCLECONCATENATION_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/VariadicArityNode.h" + +#include <cassert> + +namespace luci +{ + +/** + * @brief CONCATENATION in Circle + */ +class CircleConcatenation final + : public VariadicArityNode<CircleNodeImpl<CircleOpcode::CONCATENATION>>, + public LuciNodeMixin<LuciNodeTrait::FusedActFunc> +{ +public: + CircleConcatenation(uint32_t arity) + : VariadicArityNode<CircleNodeImpl<CircleOpcode::CONCATENATION>>(arity) + { + // TODO Support when arity is 0 + assert(arity >= 1); + } + +public: + uint32_t numValues(void) const { return arity(); } + +public: + Node *values(uint32_t index) const + { + assert(index < numValues()); + return at(index)->node(); + } + void values(uint32_t index, Node *node) + { + assert(index < numValues()); + at(index)->node(node); + } + +public: + int32_t axis(void) const { return _axis; } + void axis(int32_t axis) { _axis = axis; } + +private: + int32_t _axis; +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLECONCATENATION_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleConst.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleConst.h new file mode 100644 index 000000000..089836eb9 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleConst.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLECONST_H__ +#define __LUCI_IR_CIRCLECONST_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +#include <loco/IR/DataTypeTraits.h> + +namespace luci +{ + +/** + * @brief Class to build tensor data + * @note This will not be exported as a specific op + */ +class CircleConst final : public FixedArityNode<0, CircleNodeImpl<CircleOpcode::CONST>>, + public loco::NodeMixin<loco::NodeTrait::DataType>, + public loco::NodeMixin<loco::NodeTrait::TensorShape> +{ +public: + CircleConst() = default; + +public: + template <loco::DataType DT> uint32_t size(void) const; + template <loco::DataType DT> void size(uint32_t size); + template <loco::DataType DT> const typename loco::DataTypeImpl<DT>::Type &at(uint32_t n) const; + template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &at(uint32_t n); + + template <loco::DataType DT> const typename loco::DataTypeImpl<DT>::Type &scalar(void) const; + template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &scalar(void); + +private: + std::vector<uint8_t> _data; +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLECONST_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleConv2D.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleConv2D.h new file mode 100644 index 000000000..54318e65c --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleConv2D.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLECONV2D_H__ +#define __LUCI_IR_CIRCLECONV2D_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrPadding.h" +#include "luci/IR/AttrStride.h" +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief CONV_2D in Circle + */ +class CircleConv2D final : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::CONV_2D>>, + public LuciNodeMixin<LuciNodeTrait::FusedActFunc>, + public LuciNodeMixin<LuciNodeTrait::Bias> +{ +public: + loco::Node *input(void) const { return at(0)->node(); } + void input(loco::Node *node) { at(0)->node(node); } + + loco::Node *filter(void) const { return at(1)->node(); } + void filter(loco::Node *node) { at(1)->node(node); } + + loco::Node *bias(void) const override { return at(2)->node(); } + void bias(loco::Node *node) override { at(2)->node(node); } + +public: + Padding padding() const { return _padding; } + void padding(Padding padding) { _padding = padding; } + + const Stride *stride(void) const { return &_stride; } + Stride *stride(void) { return &_stride; } + +private: + Padding _padding = Padding::UNDEFINED; + Stride _stride; +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLECONV2D_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleCos.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleCos.h new file mode 100644 index 000000000..07ced620a --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleCos.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLE_COS_H__ +#define __LUCI_IR_CIRCLE_COS_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief COS in Circle + */ +class CircleCos final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::COS>> +{ +public: + loco::Node *x(void) const { return at(0)->node(); } + void x(loco::Node *node) { at(0)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLE_COS_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleDepthwiseConv2D.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleDepthwiseConv2D.h new file mode 100644 index 000000000..15ee62ba7 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleDepthwiseConv2D.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLEDEPTHWISECONV2D_H__ +#define __LUCI_IR_CIRCLEDEPTHWISECONV2D_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFilter.h" +#include "luci/IR/AttrPadding.h" +#include "luci/IR/AttrStride.h" +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief DEPTHWISE_CONV_2D in Circle + */ +class CircleDepthwiseConv2D final + : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::DEPTHWISE_CONV_2D>>, + public LuciNodeMixin<LuciNodeTrait::FusedActFunc>, + public LuciNodeMixin<LuciNodeTrait::Bias> +{ +public: + loco::Node *input(void) const { return at(0)->node(); } + void input(loco::Node *node) { at(0)->node(node); } + + loco::Node *filter(void) const { return at(1)->node(); } + void filter(loco::Node *node) { at(1)->node(node); } + + loco::Node *bias(void) const override { return at(2)->node(); } + void bias(loco::Node *node) override { at(2)->node(node); } + +public: + Padding padding() const { return _padding; } + void padding(Padding padding) { _padding = padding; } + + const Stride *stride(void) const { return &_stride; } + Stride *stride(void) { return &_stride; } + + int32_t depthMultiplier(void) const { return _depth_multiplier; } + void depthMultiplier(int32_t arg) { _depth_multiplier = arg; } + +private: + Padding _padding = Padding::UNDEFINED; + Stride _stride; + int32_t _depth_multiplier = 0; +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEDEPTHWISECONV2D_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleDiv.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleDiv.h new file mode 100644 index 000000000..1d4d3a239 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleDiv.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLEDIV_H__ +#define __LUCI_IR_CIRCLEDIV_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFilter.h" +#include "luci/IR/AttrPadding.h" +#include "luci/IR/AttrStride.h" +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief DIV in Circle + */ +class CircleDiv final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::DIV>>, + public LuciNodeMixin<LuciNodeTrait::FusedActFunc> +{ +public: + CircleDiv() = default; + +public: + loco::Node *x(void) const { return at(0)->node(); } + void x(loco::Node *node) { at(0)->node(node); } + + loco::Node *y(void) const { return at(1)->node(); } + void y(loco::Node *node) { at(1)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEDIV_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleEqual.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleEqual.h new file mode 100644 index 000000000..2087d097a --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleEqual.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLE_EQUAL_H__ +#define __LUCI_IR_CIRCLE_EQUAL_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief EQUAL in Circle + */ +class CircleEqual final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::EQUAL>> +{ +public: + loco::Node *x(void) const { return at(0)->node(); } + void x(loco::Node *node) { at(0)->node(node); } + + loco::Node *y(void) const { return at(1)->node(); } + void y(loco::Node *node) { at(1)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLE_EQUAL_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleExp.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleExp.h new file mode 100644 index 000000000..97aecb30a --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleExp.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLE_EXP_H__ +#define __LUCI_IR_CIRCLE_EXP_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief EXP in Circle + */ +class CircleExp final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::EXP>> +{ +public: + loco::Node *x(void) const { return at(0)->node(); } + void x(loco::Node *node) { at(0)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLE_EXP_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleFullyConnected.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleFullyConnected.h new file mode 100644 index 000000000..d78f39494 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleFullyConnected.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLEFULLYCONNECTED_H__ +#define __LUCI_IR_CIRCLEFULLYCONNECTED_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief FULLY_CONNECTED in Circle + */ +class CircleFullyConnected final + : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::FULLY_CONNECTED>>, + public LuciNodeMixin<LuciNodeTrait::FusedActFunc>, + public LuciNodeMixin<LuciNodeTrait::Bias> +{ +public: + loco::Node *input(void) const { return at(0)->node(); } + void input(loco::Node *node) { at(0)->node(node); } + + loco::Node *weights(void) const { return at(1)->node(); } + void weights(loco::Node *node) { at(1)->node(node); } + + loco::Node *bias(void) const override { return at(2)->node(); } + void bias(loco::Node *node) override { at(2)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEFULLYCONNECTED_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleGather.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleGather.h new file mode 100644 index 000000000..489596c04 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleGather.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLEGATHER_H__ +#define __LUCI_IR_CIRCLEGATHER_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief GATHER in Circle + */ +class CircleGather final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::GATHER>> +{ +public: + loco::Node *input(void) const { return at(0)->node(); } + void input(loco::Node *node) { at(0)->node(node); } + + loco::Node *positions(void) const { return at(1)->node(); } + void positions(loco::Node *node) { at(1)->node(node); } + +public: + int32_t axis(void) const { return _axis; } + void axis(int32_t axis) { _axis = axis; } + +private: + int32_t _axis = 0; +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEGATHER_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleInput.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleInput.h new file mode 100644 index 000000000..2c4d60253 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleInput.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLEINPUT_H__ +#define __LUCI_IR_CIRCLEINPUT_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +#include <loco/IR/DataTypeTraits.h> +#include <loco/IR/GraphInputIndex.h> + +namespace luci +{ + +/** + * @brief CircleNode used for Input of the Graph + * @note This will not be exported as a specific op + */ +class CircleInput final : public FixedArityNode<0, CircleNodeImpl<CircleOpcode::CIRCLEINPUT>>, + public loco::NodeMixin<loco::NodeTrait::DataType>, + public loco::NodeMixin<loco::NodeTrait::TensorShape> +{ +public: + CircleInput() = default; + +public: + void index(const loco::GraphInputIndex &index); + loco::GraphInputIndex index(void) const; + + bool indexed(void) const { return _index != -1; } + +private: + int64_t _index = -1; // Uninitialized +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEINPUT_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleInstanceNorm.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleInstanceNorm.h new file mode 100644 index 000000000..db0faa05e --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleInstanceNorm.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLEINSTANCENORM_H__ +#define __LUCI_IR_CIRCLEINSTANCENORM_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief INSTANCE_NORM in Circle + */ +class CircleInstanceNorm final + : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::INSTANCE_NORM>>, + public LuciNodeMixin<LuciNodeTrait::FusedActFunc> +{ +public: + /// @note Currently only support FLOAT32 as input node + loco::Node *input(void) const { return at(0)->node(); } + void input(loco::Node *node) { at(0)->node(node); } + + loco::Node *gamma(void) const { return at(1)->node(); } + void gamma(loco::Node *node) { at(1)->node(node); } + + loco::Node *beta(void) const { return at(2)->node(); } + void beta(loco::Node *node) { at(2)->node(node); } + + float epsilon() const { return _epsilon; } + void epsilon(float epsilon) { _epsilon = epsilon; } + +private: + float _epsilon = 1e-05; +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEINSTANCENORM_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalNot.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalNot.h new file mode 100644 index 000000000..749dbe518 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalNot.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLE_LOGICALNOT_H__ +#define __LUCI_IR_CIRCLE_LOGICALNOT_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief LOGICAL_NOT in Circle + */ +class CircleLogicalNot final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::LOGICAL_NOT>> +{ +public: + loco::Node *x(void) const { return at(0)->node(); } + void x(loco::Node *node) { at(0)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLE_LOGICALNOT_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalOr.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalOr.h new file mode 100644 index 000000000..570be57af --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalOr.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLE_LOGICALOR_H__ +#define __LUCI_IR_CIRCLE_LOGICALOR_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief LOGICAL_OR in Circle + */ +class CircleLogicalOr final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::LOGICAL_OR>> +{ +public: + loco::Node *x(void) const { return at(0)->node(); } + void x(loco::Node *node) { at(0)->node(node); } + + loco::Node *y(void) const { return at(1)->node(); } + void y(loco::Node *node) { at(1)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLE_LOGICALOR_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMaxPool2D.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMaxPool2D.h new file mode 100644 index 000000000..1eb6532ff --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMaxPool2D.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLEMAXPOOL2D_H__ +#define __LUCI_IR_CIRCLEMAXPOOL2D_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFilter.h" +#include "luci/IR/AttrPadding.h" +#include "luci/IR/AttrStride.h" +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief MAX_POOL_2D in Circle + */ +class CircleMaxPool2D final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::MAX_POOL_2D>>, + public LuciNodeMixin<LuciNodeTrait::FusedActFunc> +{ +public: + CircleMaxPool2D() : _padding(Padding::UNDEFINED) { /* empty */} + +public: + loco::Node *value(void) const { return at(0)->node(); } + void value(loco::Node *node) { at(0)->node(node); } + + Padding padding() const { return _padding; } + void padding(Padding padding) { _padding = padding; } + + const Filter *filter(void) const { return &_filter; } + Filter *filter(void) { return &_filter; } + + const Stride *stride(void) const { return &_stride; } + Stride *stride(void) { return &_stride; } + +private: + Padding _padding; + Stride _stride; + Filter _filter; +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEMAXPOOL2D_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMaximum.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMaximum.h new file mode 100644 index 000000000..cf7305e3a --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMaximum.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLEMAXIMUM_H__ +#define __LUCI_IR_CIRCLEMAXIMUM_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief MAXIMUM in Circle + */ +class CircleMaximum final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::MAXIMUM>> +{ +public: + loco::Node *x(void) const { return at(0)->node(); } + void x(loco::Node *node) { at(0)->node(node); } + + loco::Node *y(void) const { return at(1)->node(); } + void y(loco::Node *node) { at(1)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEMAXIMUM_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMean.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMean.h new file mode 100644 index 000000000..6fd791450 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMean.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLEMEAN_H__ +#define __LUCI_IR_CIRCLEMEAN_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief MEAN in Circle + */ +class CircleMean final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::MEAN>> +{ +public: + loco::Node *input(void) const { return at(0)->node(); } + void input(loco::Node *node) { at(0)->node(node); } + + loco::Node *reduction_indices(void) const { return at(1)->node(); } + void reduction_indices(loco::Node *node) { at(1)->node(node); } + +public: + bool keep_dims(void) const { return _keep_dims; } + void keep_dims(bool keep_dims) { _keep_dims = keep_dims; } + +private: + bool _keep_dims = false; +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEMEAN_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMul.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMul.h new file mode 100644 index 000000000..67e897170 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMul.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLEMUL_H__ +#define __LUCI_IR_CIRCLEMUL_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief MUL in Circle + */ +class CircleMul final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::MUL>>, + public LuciNodeMixin<LuciNodeTrait::FusedActFunc> +{ +public: + loco::Node *x(void) const { return at(0)->node(); } + void x(loco::Node *node) { at(0)->node(node); } + + loco::Node *y(void) const { return at(1)->node(); } + void y(loco::Node *node) { at(1)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEMUL_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleOutput.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleOutput.h new file mode 100644 index 000000000..c65317ad1 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleOutput.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLEOUTPUT_H__ +#define __LUCI_IR_CIRCLEOUTPUT_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +#include <loco/IR/GraphOutputIndex.h> + +namespace luci +{ + +/** + * @brief CircleNode for Output of the Graph + * @note This will not be exported as a specific op + */ +class CircleOutput final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLEOUTPUT>> +{ +public: + CircleOutput() = default; + + void index(const loco::GraphOutputIndex &index); + loco::GraphOutputIndex index(void) const; + + bool indexed(void) const { return _index != -1; } + +public: + loco::Node *from(void) const { return at(0)->node(); } + void from(loco::Node *node) { at(0)->node(node); } + +private: + int64_t _index = -1; // Uninitialized +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEOUTPUT_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CirclePack.h b/compiler/luci/lang/include/luci/IR/Nodes/CirclePack.h new file mode 100644 index 000000000..8330b585a --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CirclePack.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLEPACK_H__ +#define __LUCI_IR_CIRCLEPACK_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/VariadicArityNode.h" + +#include <cassert> + +namespace luci +{ + +/** + * @brief PACK in Circle + */ +class CirclePack final : public VariadicArityNode<CircleNodeImpl<CircleOpcode::PACK>> +{ +public: + CirclePack(uint32_t arity) : VariadicArityNode<CircleNodeImpl<CircleOpcode::PACK>>(arity) + { + // TODO Support when arity is 0 + assert(arity >= 1); + } + +public: + uint32_t values_count(void) const { return arity(); } + +public: + Node *values(uint32_t index) const + { + assert(index < values_count()); + return at(index)->node(); + } + void values(uint32_t index, Node *node) + { + assert(index < values_count()); + at(index)->node(node); + } + +public: + int32_t axis(void) const { return _axis; } + void axis(int32_t axis) { _axis = axis; } + +private: + int32_t _axis{0}; +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEPACK_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CirclePad.h b/compiler/luci/lang/include/luci/IR/Nodes/CirclePad.h new file mode 100644 index 000000000..31599bda0 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CirclePad.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLEPAD_H__ +#define __LUCI_IR_CIRCLEPAD_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief PAD in Circle + */ +class CirclePad final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::PAD>> +{ +public: + CirclePad() = default; + +public: + loco::Node *input(void) const { return at(0)->node(); } + void input(loco::Node *node) { at(0)->node(node); } + + loco::Node *paddings(void) const { return at(1)->node(); } + void paddings(loco::Node *node) { at(1)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEPAD_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu.h new file mode 100644 index 000000000..afb2c667a --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLERELU_H__ +#define __LUCI_IR_CIRCLERELU_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief RELU in Circle + */ +class CircleRelu final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::RELU>> +{ +public: + CircleRelu() = default; + +public: + loco::Node *features(void) const { return at(0)->node(); } + void features(loco::Node *node) { at(0)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLERELU_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu6.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu6.h new file mode 100644 index 000000000..b313a5557 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu6.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLERELU6_H__ +#define __LUCI_IR_CIRCLERELU6_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief RELU6 in Circle + */ +class CircleRelu6 final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::RELU6>> +{ +public: + CircleRelu6() = default; + +public: + loco::Node *features(void) const { return at(0)->node(); } + void features(loco::Node *node) { at(0)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLERELU6_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleReshape.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleReshape.h new file mode 100644 index 000000000..a3a2a3f31 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleReshape.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLERESHAPE_H__ +#define __LUCI_IR_CIRCLERESHAPE_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief RESHAPE in Circle + */ +class CircleReshape final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::RESHAPE>> +{ +public: + CircleReshape() = default; + +public: + loco::Node *tensor(void) const { return at(0)->node(); } + void tensor(loco::Node *node) { at(0)->node(node); } + + // TODO Make this input optional. That is, loco system does not emit error + // with this input being null + loco::Node *shape(void) const { return at(1)->node(); } + void shape(loco::Node *node) { at(1)->node(node); } + +public: + class Shape + { + public: + uint32_t rank(void) const { return _shape.size(); } + void rank(uint32_t rank) { _shape.resize(rank); } + + int32_t dim(uint32_t n) const { return _shape.at(n); } + int32_t &dim(uint32_t n) { return _shape.at(n); } + + private: + std::vector<int32_t> _shape; + }; + + const Shape *newShape(void) const { return &_new_shape; } + Shape *newShape(void) { return &_new_shape; } + +private: + Shape _new_shape; +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLERESHAPE_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleRsqrt.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleRsqrt.h new file mode 100644 index 000000000..44d22ef22 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleRsqrt.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLERSQRT_H__ +#define __LUCI_IR_CIRCLERSQRT_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief RSQRT in Circle + */ +class CircleRsqrt final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::RSQRT>> +{ +public: + CircleRsqrt() = default; + +public: + loco::Node *x(void) const { return at(0)->node(); } + void x(loco::Node *node) { at(0)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLERSQRT_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSoftmax.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSoftmax.h new file mode 100644 index 000000000..4ea3c4b0e --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSoftmax.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLESOFTMAX_H__ +#define __LUCI_IR_CIRCLESOFTMAX_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief SOFTMAX in Circle + */ +class CircleSoftmax final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::SOFTMAX>> +{ +public: + loco::Node *logits(void) const { return at(0)->node(); } + void logits(loco::Node *node) { at(0)->node(node); } + +public: + float beta(void) const { return _beta; } + void beta(float beta) { _beta = beta; } + +private: + float _beta; +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLESOFTMAX_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSqrt.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSqrt.h new file mode 100644 index 000000000..bc1f39d90 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSqrt.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLESQRT_H__ +#define __LUCI_IR_CIRCLESQRT_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief SQRT in Circle + */ +class CircleSqrt final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::SQRT>> +{ +public: + CircleSqrt() = default; + +public: + loco::Node *x(void) const { return at(0)->node(); } + void x(loco::Node *node) { at(0)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLESQRT_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSquaredDifference.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSquaredDifference.h new file mode 100644 index 000000000..ff337dfbe --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSquaredDifference.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLESQUAREDIFFERENCE_H__ +#define __LUCI_IR_CIRCLESQUAREDIFFERENCE_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief SQUARED_DIFFERENCE in Circle + */ +class CircleSquaredDifference final + : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::SQUARED_DIFFERENCE>> +{ +public: + CircleSquaredDifference() = default; + +public: + loco::Node *x(void) const { return at(0)->node(); } + void x(loco::Node *node) { at(0)->node(node); } + + loco::Node *y(void) const { return at(1)->node(); } + void y(loco::Node *node) { at(1)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLESQUAREDIFFERENCE_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSub.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSub.h new file mode 100644 index 000000000..08208f942 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSub.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLESUB_H__ +#define __LUCI_IR_CIRCLESUB_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief SUB in Circle + */ +class CircleSub final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::SUB>>, + public LuciNodeMixin<LuciNodeTrait::FusedActFunc> +{ +public: + CircleSub() = default; + +public: + loco::Node *x(void) const { return at(0)->node(); } + void x(loco::Node *node) { at(0)->node(node); } + + loco::Node *y(void) const { return at(1)->node(); } + void y(loco::Node *node) { at(1)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLESUB_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleTranspose.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleTranspose.h new file mode 100644 index 000000000..198b56afd --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleTranspose.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLETRANSPOSE_H__ +#define __LUCI_IR_CIRCLETRANSPOSE_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief TRANSPOSE in Circle + */ +class CircleTranspose final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::TRANSPOSE>> +{ +public: + CircleTranspose() = default; + +public: + /// @brief Get the input node to transpose + loco::Node *a(void) const { return at(0)->node(); } + + /// @brief Set the input node to transpose + void a(loco::Node *node) { at(0)->node(node); } + + loco::Node *perm(void) const { return at(1)->node(); } + void perm(loco::Node *node) { at(1)->node(node); } +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLETRANSPOSE_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleTransposeConv.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleTransposeConv.h new file mode 100644 index 000000000..54a0d010c --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleTransposeConv.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_CIRCLETRANSPOSECONV_H__ +#define __LUCI_IR_CIRCLETRANSPOSECONV_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrPadding.h" +#include "luci/IR/AttrStride.h" +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/LuciNodeMixins.h" + +namespace luci +{ + +/** + * @brief TRANSPOSE_CONV in Circle + * + * @note Argument node function names are from TensorFlow. So referring 'in' and + * 'out' acutally means 'out' and 'in' of the this node. + */ +class CircleTransposeConv final + : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::TRANSPOSE_CONV>> +{ +public: + loco::Node *inputSizes(void) const { return at(0)->node(); } + void inputSizes(Node *node) { at(0)->node(node); } + + loco::Node *filter(void) const { return at(1)->node(); } + void filter(Node *node) { at(1)->node(node); } + + loco::Node *outBackprop(void) const { return at(2)->node(); } + void outBackprop(Node *node) { at(2)->node(node); } + +public: + const Padding &padding(void) const { return _padding; } + void padding(const Padding &padding) { _padding = padding; } + + const Stride *stride(void) const { return &_stride; } + Stride *stride(void) { return &_stride; } + +private: + Padding _padding; + Stride _stride; +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLETRANSPOSECONV_H__ diff --git a/compiler/luci/lang/include/luci/IR/VariadicArityNode.h b/compiler/luci/lang/include/luci/IR/VariadicArityNode.h new file mode 100644 index 000000000..a4814ee48 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/VariadicArityNode.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2020 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 __LUCI_IR_VARIADICARITYNODES_H__ +#define __LUCI_IR_VARIADICARITYNODES_H__ + +#include <loco/IR/Node.h> +#include <loco/IR/Use.h> + +#include <vector> +#include <memory> +#include <cassert> + +namespace luci +{ + +/** + * @brief Nodes with the variadic inputs + */ +template <typename Base> class VariadicArityNode : public Base +{ +public: + VariadicArityNode(uint32_t arity) + { + for (uint32_t n = 0; n < arity; ++n) + { + _args.push_back(std::make_unique<loco::Use>(this)); + } + }; + + virtual ~VariadicArityNode() = default; + +public: + uint32_t arity(void) const final { return _args.size(); } + + loco::Node *arg(uint32_t n) const final + { + assert(n < _args.size()); + return _args.at(n)->node(); + } + + void drop(void) final + { + for (uint32_t n = 0; n < _args.size(); ++n) + { + _args.at(n)->node(nullptr); + } + } + +protected: + // This API allows inherited classes to access "_args" field. + loco::Use *at(uint32_t n) const + { + assert(n < _args.size()); + return _args.at(n).get(); + } + +private: + std::vector<std::unique_ptr<loco::Use>> _args; +}; + +} // namespace luci + +#endif // __LUCI_IR_VARIADICARITYNODES_H__ diff --git a/compiler/luci/lang/src/Check.h b/compiler/luci/lang/src/Check.h new file mode 100644 index 000000000..e05ec904a --- /dev/null +++ b/compiler/luci/lang/src/Check.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 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 __CHECK_H__ +#define __CHECK_H__ + +#include <stdexcept> +#include <cassert> +#include <iostream> + +// TODO Add macro for Release version + +#define LUCI_ASSERT(condition, msg) \ + { \ + if (!(condition)) \ + { \ + std::cerr << "[assert failed] " << (msg) << ". " << std::endl; \ + assert((condition)); \ + } \ + } + +#endif // __CHECK_H__ diff --git a/compiler/luci/lang/src/CircleDialect.cpp b/compiler/luci/lang/src/CircleDialect.cpp new file mode 100644 index 000000000..e1c925de4 --- /dev/null +++ b/compiler/luci/lang/src/CircleDialect.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2020 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 "luci/IR/CircleDialect.h" +#include "luci/IR/Nodes/CircleInput.h" +#include "luci/IR/Nodes/CircleOutput.h" + +#include <loco/IR/Graph.h> +#include <loco/IR/GraphInputIndex.h> +#include <loco/IR/GraphOutputIndex.h> + +#include <cassert> +#include <memory> + +namespace +{ + +struct GiiQueryServiceImpl final : public loco::GraphInputIndexQueryService +{ + bool associated(const loco::Node *node) const final + { + if (auto circleinput = dynamic_cast<const luci::CircleInput *>(node)) + { + return circleinput->indexed(); + } + return false; + } + + loco::GraphOutputIndex index(const loco::Node *node) const final + { + assert(associated(node)); + auto circleinput = dynamic_cast<const luci::CircleInput *>(node); + assert(circleinput != nullptr); + return circleinput->index(); + } +}; + +struct GoiQueryServiceImpl final : public loco::GraphOutputIndexQueryService +{ + bool associated(const loco::Node *node) const final + { + if (auto circleoutput = dynamic_cast<const luci::CircleOutput *>(node)) + { + return circleoutput->indexed(); + } + return false; + } + + loco::GraphOutputIndex index(const loco::Node *node) const final + { + assert(associated(node)); + auto circleoutput = dynamic_cast<const luci::CircleOutput *>(node); + assert(circleoutput != nullptr); + return circleoutput->index(); + } +}; + +} // namespace + +namespace luci +{ + +CircleDialect::CircleDialect() +{ + service<loco::GraphInputIndexQueryService>(std::make_unique<GiiQueryServiceImpl>()); + service<loco::GraphOutputIndexQueryService>(std::make_unique<GoiQueryServiceImpl>()); +} + +loco::Dialect *CircleDialect::get(void) +{ + static CircleDialect d; + return &d; +} + +} // namespace luci diff --git a/compiler/luci/lang/src/CircleDialect.test.cpp b/compiler/luci/lang/src/CircleDialect.test.cpp new file mode 100644 index 000000000..78221f199 --- /dev/null +++ b/compiler/luci/lang/src/CircleDialect.test.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 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 "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleDialectTest, get_P) +{ + auto d = luci::CircleDialect::get(); + + // get() SHOULD return a valid(non-null) pointer + ASSERT_NE(d, nullptr); + // The return value SHOULD be stable across multiple invocations + ASSERT_EQ(d, luci::CircleDialect::get()); +} + +TEST(CircleDialectTest, get_N) +{ + // TBD +} diff --git a/compiler/luci/lang/src/CircleNode.cpp b/compiler/luci/lang/src/CircleNode.cpp new file mode 100644 index 000000000..cc273ba91 --- /dev/null +++ b/compiler/luci/lang/src/CircleNode.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2020 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 "luci/IR/CircleNode.h" +#include "luci/IR/CircleDialect.h" + +namespace luci +{ + +const loco::Dialect *CircleNode::dialect(void) const { return CircleDialect::get(); } + +} // namespace luci diff --git a/compiler/luci/lang/src/CircleNodes.cpp b/compiler/luci/lang/src/CircleNodes.cpp new file mode 100644 index 000000000..76ff7ec5a --- /dev/null +++ b/compiler/luci/lang/src/CircleNodes.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020 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 "luci/IR/CircleNodes.h" + +#include "Check.h" + +#include <loco.h> + +namespace luci +{ + +void set_new_shape(CircleReshape *node, int32_t *base, uint32_t size) +{ + // Check node does not have both of new shape infos + LUCI_ASSERT(node->shape() == nullptr, "node already has shape input"); + LUCI_ASSERT(node->newShape()->rank() == 0, "node already has newShape attribute"); + + const loco::DataType S32 = loco::DataType::S32; + + // Set 2nd input as CircleConst + auto const_shape_node = node->graph()->nodes()->create<CircleConst>(); + const_shape_node->rank(1); + const_shape_node->dim(0) = size; + const_shape_node->dtype(S32); + const_shape_node->size<S32>(size); + for (uint32_t axis = 0; axis < size; ++axis) + const_shape_node->at<S32>(axis) = base[axis]; + node->shape(const_shape_node); + + // Set newShape attribute + node->newShape()->rank(size); + for (uint32_t axis = 0; axis < size; ++axis) + node->newShape()->dim(axis) = base[axis]; +} + +} // namespace luci diff --git a/compiler/luci/lang/src/LuciNodeMixins.cpp b/compiler/luci/lang/src/LuciNodeMixins.cpp new file mode 100644 index 000000000..660cbe1a5 --- /dev/null +++ b/compiler/luci/lang/src/LuciNodeMixins.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2020 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. + */ + +// This is to validate LuciNodeMixins.h +#include "luci/IR/LuciNodeMixins.h" diff --git a/compiler/luci/lang/src/Module.cpp b/compiler/luci/lang/src/Module.cpp new file mode 100644 index 000000000..e52d897a5 --- /dev/null +++ b/compiler/luci/lang/src/Module.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Module.h" + +#include <stdexcept> + +namespace luci +{ + +void Module::add(std::unique_ptr<loco::Graph> &&g) +{ + if (g.get() == nullptr) + throw std::invalid_argument("Module: Graph cannot be null"); + + _graphs.emplace_back(std::move(g)); +} + +loco::Graph *Module::graph(void) const +{ + auto &graph = _graphs.at(0); + return graph.get(); +} + +loco::Graph *Module::graph(size_t idx) const +{ + auto &graph = _graphs.at(idx); + return graph.get(); +} + +std::unique_ptr<Module> make_module(void) { return std::make_unique<Module>(); } + +} // namespace loco diff --git a/compiler/luci/lang/src/Module.test.cpp b/compiler/luci/lang/src/Module.test.cpp new file mode 100644 index 000000000..f60319944 --- /dev/null +++ b/compiler/luci/lang/src/Module.test.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Module.h" + +#include <gtest/gtest.h> + +TEST(ModuleTest, consturctor) +{ + auto gs = luci::make_module(); + + GTEST_SUCCEED(); +} + +TEST(ModuleTest, add) +{ + auto m = luci::make_module(); + auto g = loco::make_graph(); + auto g_ptr = g.get(); + + m->add(std::move(g)); + + ASSERT_EQ(m->graph(), g_ptr); + ASSERT_EQ(m->graph(0), g_ptr); +} + +TEST(ModuleTest, add_more) +{ + auto m = luci::make_module(); + auto g1 = loco::make_graph(); + auto g2 = loco::make_graph(); + auto g3 = loco::make_graph(); + auto g1_ptr = g1.get(); + auto g2_ptr = g2.get(); + auto g3_ptr = g3.get(); + + m->add(std::move(g1)); + m->add(std::move(g2)); + m->add(std::move(g3)); + + ASSERT_EQ(m->size(), 3); + ASSERT_EQ(m->graph(), g1_ptr); + ASSERT_EQ(m->graph(0), g1_ptr); + ASSERT_EQ(m->graph(1), g2_ptr); + ASSERT_EQ(m->graph(2), g3_ptr); +} + +TEST(ModuleTest, add_nullptr_NEG) +{ + auto m = luci::make_module(); + + EXPECT_THROW(m->add(nullptr), std::invalid_argument); +} + +TEST(ModuleTest, graph_index_overflow_NEG) +{ + auto m = luci::make_module(); + + EXPECT_ANY_THROW(m->graph(100)); +} diff --git a/compiler/luci/lang/src/Nodes/CircleAbs.test.cpp b/compiler/luci/lang/src/Nodes/CircleAbs.test.cpp new file mode 100644 index 000000000..847f1500b --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleAbs.test.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleAbs.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleAbsTest, constructor) +{ + luci::CircleAbs abs_node; + + ASSERT_EQ(abs_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(abs_node.opcode(), luci::CircleOpcode::ABS); + + ASSERT_EQ(abs_node.x(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleAdd.test.cpp b/compiler/luci/lang/src/Nodes/CircleAdd.test.cpp new file mode 100644 index 000000000..a7701963d --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleAdd.test.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleAdd.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleAddTest, constructor_P) +{ + luci::CircleAdd add_node; + + ASSERT_EQ(add_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(add_node.opcode(), luci::CircleOpcode::ADD); + + ASSERT_EQ(add_node.x(), nullptr); + ASSERT_EQ(add_node.y(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleArgMax.test.cpp b/compiler/luci/lang/src/Nodes/CircleArgMax.test.cpp new file mode 100644 index 000000000..6b2cff11c --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleArgMax.test.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleArgMax.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleArgMaxTest, constructor_P) +{ + luci::CircleArgMax add_node; + + ASSERT_EQ(add_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(add_node.opcode(), luci::CircleOpcode::ARG_MAX); + + ASSERT_EQ(add_node.input(), nullptr); + ASSERT_EQ(add_node.dimension(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleBatchToSpaceND.test.cpp b/compiler/luci/lang/src/Nodes/CircleBatchToSpaceND.test.cpp new file mode 100644 index 000000000..e995718a1 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleBatchToSpaceND.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleBatchToSpaceND.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleBatchToSpaceNDTest, constructor) +{ + luci::CircleBatchToSpaceND bts_node; + + ASSERT_EQ(bts_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(bts_node.opcode(), luci::CircleOpcode::BATCH_TO_SPACE_ND); + + ASSERT_EQ(bts_node.input(), nullptr); + ASSERT_EQ(bts_node.block_shape(), nullptr); + ASSERT_EQ(bts_node.crops(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleConcatenation.test.cpp b/compiler/luci/lang/src/Nodes/CircleConcatenation.test.cpp new file mode 100644 index 000000000..7167682b2 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleConcatenation.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleConcatenation.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleConcatenationTest, constructor_P) +{ + luci::CircleConcatenation concat_node(3); + + ASSERT_EQ(concat_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(concat_node.opcode(), luci::CircleOpcode::CONCATENATION); + + ASSERT_EQ(concat_node.numValues(), 3); + ASSERT_EQ(concat_node.values(0), nullptr); + ASSERT_EQ(concat_node.values(1), nullptr); + ASSERT_EQ(concat_node.values(2), nullptr); + ASSERT_EQ(concat_node.fusedActivationFunction(), luci::FusedActFunc::UNDEFINED); +} diff --git a/compiler/luci/lang/src/Nodes/CircleConst.cpp b/compiler/luci/lang/src/Nodes/CircleConst.cpp new file mode 100644 index 000000000..1c46884d8 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleConst.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleConst.h" + +#include <cassert> + +namespace luci +{ + +template <loco::DataType DT> uint32_t CircleConst::size(void) const +{ + assert(dtype() == DT); + assert(_data.size() % sizeof(typename loco::DataTypeImpl<DT>::Type) == 0); + return _data.size() / sizeof(typename loco::DataTypeImpl<DT>::Type); +} + +template <loco::DataType DT> void CircleConst::size(uint32_t l) +{ + assert(dtype() == DT); + _data.resize(l * sizeof(typename loco::DataTypeImpl<DT>::Type)); +} + +template <loco::DataType DT> +const typename loco::DataTypeImpl<DT>::Type &CircleConst::at(uint32_t n) const +{ + assert(dtype() == DT); + assert(n < size<DT>()); + return *(reinterpret_cast<const typename loco::DataTypeImpl<DT>::Type *>(_data.data()) + n); +} + +template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &CircleConst::at(uint32_t n) +{ + assert(dtype() == DT); + assert(n < size<DT>()); + return *(reinterpret_cast<typename loco::DataTypeImpl<DT>::Type *>(_data.data()) + n); +} + +template <loco::DataType DT> +const typename loco::DataTypeImpl<DT>::Type &CircleConst::scalar(void) const +{ + assert(dtype() == DT); + return *(reinterpret_cast<const typename loco::DataTypeImpl<DT>::Type *>(_data.data())); +} + +template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &CircleConst::scalar(void) +{ + assert(dtype() == DT); + return *(reinterpret_cast<typename loco::DataTypeImpl<DT>::Type *>(_data.data())); +} + +#define INSTANTIATE(DT) \ + template uint32_t CircleConst::size<DT>(void) const; \ + template void CircleConst::size<DT>(uint32_t); \ + template const typename loco::DataTypeImpl<DT>::Type &CircleConst::at<DT>(uint32_t) const; \ + template typename loco::DataTypeImpl<DT>::Type &CircleConst::at<DT>(uint32_t); \ + template const typename loco::DataTypeImpl<DT>::Type &CircleConst::scalar<DT>(void) const; \ + template typename loco::DataTypeImpl<DT>::Type &CircleConst::scalar<DT>(void); + +INSTANTIATE(loco::DataType::S32); +INSTANTIATE(loco::DataType::FLOAT32); +INSTANTIATE(loco::DataType::U8); + +#undef INSTANTIATE + +} // namespace luci diff --git a/compiler/luci/lang/src/Nodes/CircleConv2D.test.cpp b/compiler/luci/lang/src/Nodes/CircleConv2D.test.cpp new file mode 100644 index 000000000..7931c7eba --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleConv2D.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleConv2D.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleConv2Dest, constructor_P) +{ + luci::CircleConv2D conv2d_node; + + ASSERT_EQ(conv2d_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(conv2d_node.opcode(), luci::CircleOpcode::CONV_2D); + + ASSERT_EQ(conv2d_node.input(), nullptr); + ASSERT_EQ(conv2d_node.filter(), nullptr); + ASSERT_EQ(conv2d_node.bias(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleCos.test.cpp b/compiler/luci/lang/src/Nodes/CircleCos.test.cpp new file mode 100644 index 000000000..34c2cfdf0 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleCos.test.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleCos.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleCosTest, constructor_P) +{ + luci::CircleCos cos_node; + + ASSERT_EQ(cos_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(cos_node.opcode(), luci::CircleOpcode::COS); + + ASSERT_EQ(cos_node.x(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleDepthwiseConv2D.test.cpp b/compiler/luci/lang/src/Nodes/CircleDepthwiseConv2D.test.cpp new file mode 100644 index 000000000..bbc1ea543 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleDepthwiseConv2D.test.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleDepthwiseConv2D.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleDepthwiseConv2DTest, constructor_P) +{ + luci::CircleDepthwiseConv2D dw_conv2d_node; + + ASSERT_EQ(dw_conv2d_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(dw_conv2d_node.opcode(), luci::CircleOpcode::DEPTHWISE_CONV_2D); + + ASSERT_EQ(dw_conv2d_node.input(), nullptr); + ASSERT_EQ(dw_conv2d_node.filter(), nullptr); + ASSERT_EQ(dw_conv2d_node.bias(), nullptr); + ASSERT_EQ(dw_conv2d_node.padding(), luci::Padding::UNDEFINED); + ASSERT_EQ(dw_conv2d_node.stride()->h(), 1); + ASSERT_EQ(dw_conv2d_node.stride()->w(), 1); + ASSERT_EQ(dw_conv2d_node.depthMultiplier(), 0); + ASSERT_EQ(dw_conv2d_node.fusedActivationFunction(), luci::FusedActFunc::UNDEFINED); +} diff --git a/compiler/luci/lang/src/Nodes/CircleDiv.test.cpp b/compiler/luci/lang/src/Nodes/CircleDiv.test.cpp new file mode 100644 index 000000000..e950cc6be --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleDiv.test.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleDiv.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleDivTest, constructor_P) +{ + luci::CircleDiv div_node; + + ASSERT_EQ(div_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(div_node.opcode(), luci::CircleOpcode::DIV); + + ASSERT_EQ(div_node.x(), nullptr); + ASSERT_EQ(div_node.y(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleEqual.test.cpp b/compiler/luci/lang/src/Nodes/CircleEqual.test.cpp new file mode 100644 index 000000000..e2757f094 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleEqual.test.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleEqual.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleEqualTest, constructor_P) +{ + luci::CircleEqual or_node; + + ASSERT_EQ(or_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(or_node.opcode(), luci::CircleOpcode::EQUAL); + + ASSERT_EQ(or_node.x(), nullptr); + ASSERT_EQ(or_node.y(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleExp.test.cpp b/compiler/luci/lang/src/Nodes/CircleExp.test.cpp new file mode 100644 index 000000000..db10d0b03 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleExp.test.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleExp.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleExpTest, constructor) +{ + luci::CircleExp exp_node; + + ASSERT_EQ(exp_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(exp_node.opcode(), luci::CircleOpcode::EXP); + + ASSERT_EQ(exp_node.x(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleFullyConnected.test.cpp b/compiler/luci/lang/src/Nodes/CircleFullyConnected.test.cpp new file mode 100644 index 000000000..994dcd239 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleFullyConnected.test.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleFullyConnected.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleFullyConnectedTest, constructor) +{ + luci::CircleFullyConnected fc_node; + + ASSERT_EQ(fc_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(fc_node.opcode(), luci::CircleOpcode::FULLY_CONNECTED); + + ASSERT_EQ(fc_node.input(), nullptr); + ASSERT_EQ(fc_node.weights(), nullptr); + ASSERT_EQ(fc_node.bias(), nullptr); + ASSERT_EQ(fc_node.fusedActivationFunction(), luci::FusedActFunc::UNDEFINED); +} diff --git a/compiler/luci/lang/src/Nodes/CircleGather.test.cpp b/compiler/luci/lang/src/Nodes/CircleGather.test.cpp new file mode 100644 index 000000000..4eace9a02 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleGather.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleGather.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleGatherTest, constructor) +{ + luci::CircleGather gather_node; + + ASSERT_EQ(gather_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(gather_node.opcode(), luci::CircleOpcode::GATHER); + + ASSERT_EQ(gather_node.input(), nullptr); + ASSERT_EQ(gather_node.positions(), nullptr); + ASSERT_EQ(gather_node.axis(), 0); +} diff --git a/compiler/luci/lang/src/Nodes/CircleInput.cpp b/compiler/luci/lang/src/Nodes/CircleInput.cpp new file mode 100644 index 000000000..dcf54f3b0 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleInput.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleInput.h" + +#include <cassert> +#include <limits> + +namespace luci +{ + +void CircleInput::index(const loco::GraphInputIndex &index) +{ + // CircleInput internally stores "GraphInputIndex" as int64_t + _index = static_cast<int64_t>(index); +} + +loco::GraphInputIndex CircleInput::index(void) const +{ + assert(_index >= std::numeric_limits<loco::GraphInputIndex>::min()); + assert(_index <= std::numeric_limits<loco::GraphInputIndex>::max()); + return static_cast<loco::GraphInputIndex>(_index); +} + +} // namespace luci diff --git a/compiler/luci/lang/src/Nodes/CircleInstanceNorm.test.cpp b/compiler/luci/lang/src/Nodes/CircleInstanceNorm.test.cpp new file mode 100644 index 000000000..b87e81791 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleInstanceNorm.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleInstanceNorm.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleInstanceNormTest, constructor) +{ + luci::CircleInstanceNorm instance_norm; + + ASSERT_EQ(instance_norm.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(instance_norm.opcode(), luci::CircleOpcode::INSTANCE_NORM); + + ASSERT_EQ(instance_norm.input(), nullptr); + ASSERT_EQ(instance_norm.gamma(), nullptr); + ASSERT_EQ(instance_norm.beta(), nullptr); + ASSERT_FLOAT_EQ(instance_norm.epsilon(), 1e-05); + ASSERT_EQ(instance_norm.fusedActivationFunction(), luci::FusedActFunc::UNDEFINED); +} diff --git a/compiler/luci/lang/src/Nodes/CircleLogicalNot.test.cpp b/compiler/luci/lang/src/Nodes/CircleLogicalNot.test.cpp new file mode 100644 index 000000000..360dd4711 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleLogicalNot.test.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleLogicalNot.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleLogicalNotTest, constructor_P) +{ + luci::CircleLogicalNot not_node; + + ASSERT_EQ(not_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(not_node.opcode(), luci::CircleOpcode::LOGICAL_NOT); + + ASSERT_EQ(not_node.x(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleLogicalOr.test.cpp b/compiler/luci/lang/src/Nodes/CircleLogicalOr.test.cpp new file mode 100644 index 000000000..039db4afc --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleLogicalOr.test.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleLogicalOr.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleLogicalOrTest, constructor_P) +{ + luci::CircleLogicalOr or_node; + + ASSERT_EQ(or_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(or_node.opcode(), luci::CircleOpcode::LOGICAL_OR); + + ASSERT_EQ(or_node.x(), nullptr); + ASSERT_EQ(or_node.y(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleMaxPool2D.test.cpp b/compiler/luci/lang/src/Nodes/CircleMaxPool2D.test.cpp new file mode 100644 index 000000000..874ecec0e --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleMaxPool2D.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleMaxPool2D.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleMaxPool2DTest, constructor_P) +{ + luci::CircleMaxPool2D maxpool2d_node; + + ASSERT_EQ(maxpool2d_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(maxpool2d_node.opcode(), luci::CircleOpcode::MAX_POOL_2D); + + ASSERT_EQ(maxpool2d_node.value(), nullptr); + ASSERT_NE(maxpool2d_node.filter(), nullptr); + ASSERT_NE(maxpool2d_node.stride(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleMaximum.test.cpp b/compiler/luci/lang/src/Nodes/CircleMaximum.test.cpp new file mode 100644 index 000000000..efe62f11a --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleMaximum.test.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleMaximum.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleMaximumTest, constructor_P) +{ + luci::CircleMaximum max_node; + + ASSERT_EQ(max_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(max_node.opcode(), luci::CircleOpcode::MAXIMUM); + + ASSERT_EQ(max_node.x(), nullptr); + ASSERT_EQ(max_node.y(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleMul.test.cpp b/compiler/luci/lang/src/Nodes/CircleMul.test.cpp new file mode 100644 index 000000000..f9eca42f9 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleMul.test.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleMul.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleMulTest, constructor_P) +{ + luci::CircleMul mul_node; + + ASSERT_EQ(mul_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(mul_node.opcode(), luci::CircleOpcode::MUL); + + ASSERT_EQ(mul_node.x(), nullptr); + ASSERT_EQ(mul_node.y(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleOutput.cpp b/compiler/luci/lang/src/Nodes/CircleOutput.cpp new file mode 100644 index 000000000..31380456f --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleOutput.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleOutput.h" + +#include <cassert> +#include <limits> + +namespace luci +{ + +void CircleOutput::index(const loco::GraphOutputIndex &index) +{ + // CircleOutput internally stores "GraphOutputIndex" as int64_t + _index = static_cast<int64_t>(index); +} + +loco::GraphOutputIndex CircleOutput::index(void) const +{ + assert(_index >= std::numeric_limits<loco::GraphOutputIndex>::min()); + assert(_index <= std::numeric_limits<loco::GraphOutputIndex>::max()); + return static_cast<loco::GraphOutputIndex>(_index); +} + +} // namespace luci diff --git a/compiler/luci/lang/src/Nodes/CirclePack.test.cpp b/compiler/luci/lang/src/Nodes/CirclePack.test.cpp new file mode 100644 index 000000000..5c9a96f7c --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CirclePack.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CirclePack.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CirclePackTest, constructor) +{ + luci::CirclePack pack_node(3); + + ASSERT_EQ(pack_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(pack_node.opcode(), luci::CircleOpcode::PACK); + + ASSERT_EQ(pack_node.axis(), 0); + ASSERT_EQ(pack_node.values_count(), 3); + ASSERT_EQ(pack_node.values(0), nullptr); + ASSERT_EQ(pack_node.values(1), nullptr); + ASSERT_EQ(pack_node.values(2), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CirclePad.test.cpp b/compiler/luci/lang/src/Nodes/CirclePad.test.cpp new file mode 100644 index 000000000..3a23fa0f0 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CirclePad.test.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CirclePad.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CirclePadTest, constructor_P) +{ + luci::CirclePad pad_node; + + ASSERT_EQ(pad_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(pad_node.opcode(), luci::CircleOpcode::PAD); + + ASSERT_EQ(pad_node.input(), nullptr); + ASSERT_EQ(pad_node.paddings(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleRelu.test.cpp b/compiler/luci/lang/src/Nodes/CircleRelu.test.cpp new file mode 100644 index 000000000..19ea88aa6 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleRelu.test.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleRelu.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleReluTest, constructor_P) +{ + luci::CircleRelu relu_node; + + ASSERT_EQ(relu_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(relu_node.opcode(), luci::CircleOpcode::RELU); + + ASSERT_EQ(relu_node.features(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleRelu6.test.cpp b/compiler/luci/lang/src/Nodes/CircleRelu6.test.cpp new file mode 100644 index 000000000..74bf2e86a --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleRelu6.test.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleRelu6.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleRelu6Test, constructor_P) +{ + luci::CircleRelu6 relu6_node; + + ASSERT_EQ(relu6_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(relu6_node.opcode(), luci::CircleOpcode::RELU6); + + ASSERT_EQ(relu6_node.features(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleReshape.test.cpp b/compiler/luci/lang/src/Nodes/CircleReshape.test.cpp new file mode 100644 index 000000000..7bc2d32a4 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleReshape.test.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleReshape.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleReshapeTest, constructor_P) +{ + luci::CircleReshape reshape; + + ASSERT_EQ(reshape.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(reshape.opcode(), luci::CircleOpcode::RESHAPE); + + ASSERT_EQ(reshape.tensor(), nullptr); + ASSERT_EQ(reshape.shape(), nullptr); + ASSERT_EQ(reshape.newShape()->rank(), 0); +} + +TEST(CircleReshapeTest, alloc_new_shape_P) +{ + luci::CircleReshape reshape; + + reshape.newShape()->rank(2); + ASSERT_EQ(reshape.newShape()->rank(), 2); + + reshape.newShape()->dim(0) = 0; + reshape.newShape()->dim(1) = 1; + + auto &const_reshape = const_cast<const luci::CircleReshape &>(reshape); + ASSERT_EQ(const_reshape.newShape()->dim(0), 0); + ASSERT_EQ(const_reshape.newShape()->dim(1), 1); +} diff --git a/compiler/luci/lang/src/Nodes/CircleRsqrt.test.cpp b/compiler/luci/lang/src/Nodes/CircleRsqrt.test.cpp new file mode 100644 index 000000000..51f6bab36 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleRsqrt.test.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleRsqrt.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleRsqrtTest, constructor) +{ + luci::CircleRsqrt rsqrt_node; + + ASSERT_EQ(rsqrt_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(rsqrt_node.opcode(), luci::CircleOpcode::RSQRT); + + ASSERT_EQ(rsqrt_node.x(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleSoftmax.test.cpp b/compiler/luci/lang/src/Nodes/CircleSoftmax.test.cpp new file mode 100644 index 000000000..7e994490c --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleSoftmax.test.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleSoftmax.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleSoftmaxTest, constructor_P) +{ + luci::CircleSoftmax softmax_node; + + ASSERT_EQ(softmax_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(softmax_node.opcode(), luci::CircleOpcode::SOFTMAX); + + ASSERT_EQ(softmax_node.logits(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleSqrt.test.cpp b/compiler/luci/lang/src/Nodes/CircleSqrt.test.cpp new file mode 100644 index 000000000..6cfb3bc94 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleSqrt.test.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleSqrt.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleSqrtTest, constructor_P) +{ + luci::CircleSqrt sqrt_node; + + ASSERT_EQ(sqrt_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(sqrt_node.opcode(), luci::CircleOpcode::SQRT); + + ASSERT_EQ(sqrt_node.x(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleSquaredDifference.test.cpp b/compiler/luci/lang/src/Nodes/CircleSquaredDifference.test.cpp new file mode 100644 index 000000000..71df189b9 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleSquaredDifference.test.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleSquaredDifference.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleSquaredDifferenceTest, constructor_P) +{ + luci::CircleSquaredDifference sd_node; + + ASSERT_EQ(sd_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(sd_node.opcode(), luci::CircleOpcode::SQUARED_DIFFERENCE); + + ASSERT_EQ(sd_node.x(), nullptr); + ASSERT_EQ(sd_node.y(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleSub.test.cpp b/compiler/luci/lang/src/Nodes/CircleSub.test.cpp new file mode 100644 index 000000000..ebb29446a --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleSub.test.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleSub.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleSubTest, constructor_P) +{ + luci::CircleSub sub_node; + + ASSERT_EQ(sub_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(sub_node.opcode(), luci::CircleOpcode::SUB); + + ASSERT_EQ(sub_node.x(), nullptr); + ASSERT_EQ(sub_node.y(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleTranspose.test.cpp b/compiler/luci/lang/src/Nodes/CircleTranspose.test.cpp new file mode 100644 index 000000000..7233869e6 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleTranspose.test.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleTranspose.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleTransposeTest, constructor_P) +{ + luci::CircleTranspose tr_node; + + ASSERT_EQ(tr_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(tr_node.opcode(), luci::CircleOpcode::TRANSPOSE); + + ASSERT_EQ(tr_node.a(), nullptr); + ASSERT_EQ(tr_node.perm(), nullptr); +} diff --git a/compiler/luci/lang/src/Nodes/CircleTransposeConv.test.cpp b/compiler/luci/lang/src/Nodes/CircleTransposeConv.test.cpp new file mode 100644 index 000000000..9615082d9 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleTransposeConv.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020 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 "luci/IR/Nodes/CircleTransposeConv.h" + +#include "luci/IR/CircleDialect.h" + +#include <gtest/gtest.h> + +TEST(CircleTransposeConvTest, constructor_P) +{ + luci::CircleTransposeConv trc_node; + + ASSERT_EQ(trc_node.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(trc_node.opcode(), luci::CircleOpcode::TRANSPOSE_CONV); + + ASSERT_EQ(trc_node.inputSizes(), nullptr); + ASSERT_EQ(trc_node.filter(), nullptr); + ASSERT_EQ(trc_node.outBackprop(), nullptr); +} diff --git a/compiler/luci/log/CMakeLists.txt b/compiler/luci/log/CMakeLists.txt new file mode 100644 index 000000000..af2e7a768 --- /dev/null +++ b/compiler/luci/log/CMakeLists.txt @@ -0,0 +1,9 @@ +# TODO Find how to test logging framework +file(GLOB_RECURSE SOURCES "src/*.cpp") + +add_library(luci_log SHARED ${SOURCES}) +target_include_directories(luci_log PUBLIC include) +target_link_libraries(luci_log PUBLIC hermes) +target_link_libraries(luci_log PRIVATE hermes_std) +target_link_libraries(luci_log PRIVATE nncc_common) +install(TARGETS luci_log DESTINATION lib) diff --git a/compiler/luci/log/README.md b/compiler/luci/log/README.md new file mode 100644 index 000000000..512bc96d2 --- /dev/null +++ b/compiler/luci/log/README.md @@ -0,0 +1,3 @@ +# luci-log + +_luci-log_ is a logging framework for _luci_ compiler framework. diff --git a/compiler/luci/log/include/luci/Log.h b/compiler/luci/log/include/luci/Log.h new file mode 100644 index 000000000..51299a082 --- /dev/null +++ b/compiler/luci/log/include/luci/Log.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2020 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 __LUCI_LOG_H__ +#define __LUCI_LOG_H__ + +#include <hermes.h> + +namespace luci +{ + +/** + * @brief Logger Implementation + */ +class Logger final : public hermes::Source +{ +public: + Logger(hermes::Context *ctx); + ~Logger(); +}; + +/** + * @brief Logger Configuration + * + * Users are able to turn logging on/off via MOCO_LOG environment variable. + */ +class LoggerConfig final : public hermes::Config +{ +public: + LoggerConfig(); + +public: + void configure(const hermes::Source *, hermes::Source::Setting &) const final; + void configure(const Logger *, hermes::Source::Setting &) const; + +private: + bool _enabled; +}; + +} // namespace luci + +#include "luci/LoggingContext.h" + +/** + * HOW TO USE: + * + * LOGGER(l); + * + * INFO(l) << "Hello, World" << std::endl; + * + */ +#define LOGGER(name) ::luci::Logger name{::luci::LoggingContext::get()}; + +// TODO Support FATAL, ERROR, WARN, and VERBOSE +#define INFO(name) HERMES_INFO(name) + +// WARNING! +// +// THE CURRENT IMPLEMENTATION IS NOT THREAD SAFE. +// + +#endif // __LUCI_LOG_H__ diff --git a/compiler/luci/log/include/luci/LoggingContext.h b/compiler/luci/log/include/luci/LoggingContext.h new file mode 100644 index 000000000..f5091099f --- /dev/null +++ b/compiler/luci/log/include/luci/LoggingContext.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 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 __LUCI_LOGGING_CONTEXT_H__ +#define __LUCI_LOGGING_CONTEXT_H__ + +#include <hermes.h> + +namespace luci +{ + +/** + * @brief Global logging context + */ +struct LoggingContext +{ + static hermes::Context *get(void); +}; + +} // namespace luci + +#endif // __LUCI_LOGGING_CONTEXT_H__ diff --git a/compiler/luci/log/src/Log.cpp b/compiler/luci/log/src/Log.cpp new file mode 100644 index 000000000..7e1634009 --- /dev/null +++ b/compiler/luci/log/src/Log.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2020 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 "luci/Log.h" + +#include <cassert> +#include <cstdlib> +#include <iostream> + +// TODO Extract these lexical conversion routines as a library +namespace +{ + +/** + * @brief Convert C-string as a value of type T + * + * safecast(s, v) returns v if s is nullptr. + */ +template <typename T> T safecast(const char *, const T &); + +template <> bool safecast<bool>(const char *s, const bool &value) +{ + return (s == nullptr) ? value : (std::stoi(s) != 0); +} + +} // namespace + +// +// Logger +// +namespace luci +{ + +Logger::Logger(hermes::Context *ctx) { activate(ctx->sources(), ctx->bus()); } +Logger::~Logger() { deactivate(); } + +} // namespace luci + +// +// LoggerConfig +// +namespace luci +{ + +LoggerConfig::LoggerConfig() +{ + // Turn on logging if LUCI_LOG is set as non-zero value + _enabled = safecast<bool>(std::getenv("LUCI_LOG"), false); +} + +void LoggerConfig::configure(const hermes::Source *source, hermes::Source::Setting &setting) const +{ + // Let's ignore hermes::Sources if that is not a moco logger + if (auto logger = dynamic_cast<const Logger *>(source)) + { + configure(logger, setting); + } +} + +void LoggerConfig::configure(const Logger *, hermes::Source::Setting &setting) const +{ + if (_enabled) + { + // Enable all catagories + setting.accept_all(); + } + else + { + // Disable all catagories + setting.reject_all(); + } +} + +} // namespace luci diff --git a/compiler/luci/log/src/LoggingContext.cpp b/compiler/luci/log/src/LoggingContext.cpp new file mode 100644 index 000000000..8d7997869 --- /dev/null +++ b/compiler/luci/log/src/LoggingContext.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 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 "luci/LoggingContext.h" +#include "luci/Log.h" + +#include <hermes/ConsoleReporter.h> + +#include <memory> + +namespace luci +{ + +hermes::Context *LoggingContext::get(void) +{ + static hermes::Context *ctx = nullptr; + + if (ctx == nullptr) + { + ctx = new hermes::Context; + ctx->sinks()->append(std::make_unique<hermes::ConsoleReporter>()); + ctx->config(std::make_unique<LoggerConfig>()); + } + + return ctx; +} + +} // namespace luci diff --git a/compiler/luci/logex/CMakeLists.txt b/compiler/luci/logex/CMakeLists.txt new file mode 100644 index 000000000..fa2ea123c --- /dev/null +++ b/compiler/luci/logex/CMakeLists.txt @@ -0,0 +1,13 @@ +# TODO Find how to test logging-ex utility +file(GLOB_RECURSE SOURCES "src/*.cpp") + +add_library(luci_logex SHARED ${SOURCES}) +target_include_directories(luci_logex PUBLIC include) +target_link_libraries(luci_logex PUBLIC loco) +target_link_libraries(luci_logex PUBLIC locop) +target_link_libraries(luci_logex PRIVATE luci_log) +target_link_libraries(luci_logex PRIVATE luci_lang) +target_link_libraries(luci_logex PRIVATE hermes_std) +target_link_libraries(luci_logex PRIVATE nncc_common) +target_link_libraries(luci_logex PRIVATE pepper_str) +install(TARGETS luci_logex DESTINATION lib) diff --git a/compiler/luci/logex/README.md b/compiler/luci/logex/README.md new file mode 100644 index 000000000..03b6baf35 --- /dev/null +++ b/compiler/luci/logex/README.md @@ -0,0 +1,3 @@ +# luci-logex + +_luci-logex_ is a extended logging utility for _luci_ compiler framework. diff --git a/compiler/luci/logex/include/luci/FormattedGraph.h b/compiler/luci/logex/include/luci/FormattedGraph.h new file mode 100644 index 000000000..da4af3bfa --- /dev/null +++ b/compiler/luci/logex/include/luci/FormattedGraph.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020 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 __LUCI_FORMATTED_GRAPH_H__ +#define __LUCI_FORMATTED_GRAPH_H__ + +#include <locop/FormattedGraph.h> + +#include <memory> + +namespace luci +{ + +class NodeSummaryBuilder final : public locop::NodeSummaryBuilder +{ +public: + NodeSummaryBuilder(const locop::SymbolTable *tbl) : _tbl{tbl} + { + // DO NOTHING + } + +public: + bool build(const loco::Node *node, locop::NodeSummary &s) const final; + +private: + const locop::SymbolTable *_tbl; +}; + +class NodeSummaryBuilderFactory final : public locop::NodeSummaryBuilderFactory +{ +public: + NodeSummaryBuilderFactory() = default; + +public: + std::unique_ptr<locop::NodeSummaryBuilder> create(const locop::SymbolTable *tlb) const final + { + return std::make_unique<NodeSummaryBuilder>(tlb); + } +}; + +} // namespace luci + +#endif // __LUCI_FORMATTED_GRAPH_H__ diff --git a/compiler/luci/logex/include/luci/LogHelper.h b/compiler/luci/logex/include/luci/LogHelper.h new file mode 100644 index 000000000..37cdd735b --- /dev/null +++ b/compiler/luci/logex/include/luci/LogHelper.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 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 __LUCI_LOG_HELPER_H__ +#define __LUCI_LOG_HELPER_H__ + +#include <locop/FormattedGraph.h> +#include <loco.h> + +#include <memory> + +namespace luci +{ + +using FormattedGraph = locop::FormattedGraphImpl<locop::Formatter::LinearV1>; + +FormattedGraph fmt(loco::Graph *g); + +static inline FormattedGraph fmt(const std::unique_ptr<loco::Graph> &g) { return fmt(g.get()); } + +} // namespace luci + +#endif // __LUCI_LOG_HELPER_H__ diff --git a/compiler/luci/logex/src/FormattedGraph.cpp b/compiler/luci/logex/src/FormattedGraph.cpp new file mode 100644 index 000000000..894ebc151 --- /dev/null +++ b/compiler/luci/logex/src/FormattedGraph.cpp @@ -0,0 +1,606 @@ +/* + * Copyright (c) 2020 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 "luci/FormattedGraph.h" + +#include <luci/IR/CircleDialect.h> +#include <luci/IR/CircleNodes.h> + +#include <pepper/str.h> + +#include <cassert> +#include <sstream> +#include <vector> + +/** + * @brief dump std::vector<int64_t> values to stream + */ +std::ostream &operator<<(std::ostream &os, const std::vector<int64_t> &vi64) +{ + for (auto vi : vi64) + { + os << vi << " "; + } + return os; +} + +// For TF lite +namespace +{ + +const char *to_str(loco::DataType type) +{ + switch (type) + { + case loco::DataType::U8: + return "UINT8"; + case loco::DataType::U16: + return "UINT16"; + case loco::DataType::U32: + return "UINT32"; + case loco::DataType::U64: + return "UINT64"; + + case loco::DataType::S8: + return "INT8"; + case loco::DataType::S16: + return "INT16"; + case loco::DataType::S32: + return "INT32"; + case loco::DataType::S64: + return "INT64"; + + case loco::DataType::FLOAT16: + return "FLOAT16"; + case loco::DataType::FLOAT32: + return "FLOAT32"; + case loco::DataType::FLOAT64: + return "FLOAT64"; + + case loco::DataType::BOOL: + return "BOOL"; + + default: + return "Error"; + } +} + +const char *to_str(luci::FusedActFunc fused) +{ + switch (fused) + { + case luci::FusedActFunc::NONE: + return "NONE"; + case luci::FusedActFunc::RELU: + return "RELU"; + case luci::FusedActFunc::RELU_N1_TO_1: + return "RELU_N1_TO_1"; + case luci::FusedActFunc::RELU6: + return "RELU6"; + default: + return "Error"; + } +} + +const char *to_str(luci::Padding padding) +{ + switch (padding) + { + case luci::Padding::SAME: + return "SAME"; + case luci::Padding::VALID: + return "VALID"; + default: + return "Error"; + } +} + +std::string to_str(const luci::Stride *stride) +{ + return pepper::str(stride->h(), ",", stride->w()); +} + +std::string to_str(const luci::Filter *filter) +{ + return pepper::str(filter->h(), ",", filter->w()); +} + +std::string circle_opname(uint32_t opnum) +{ + static const std::string prefix{"circle."}; + + switch (static_cast<luci::CircleOpcode>(opnum)) + { +#define CIRCLE_NODE(OPCODE, CLASS) \ + case luci::CircleOpcode::OPCODE: \ + return prefix + #OPCODE; +#include <luci/IR/CircleNodes.lst> +#undef CIRCLE_NODE + default: + break; + }; + + return prefix + "Invalid"; +} + +// CircleNodeSummaryBuilder with default implementation +class CircleNodeSummaryBuilderBase : public locop::NodeSummaryBuilder +{ +public: + CircleNodeSummaryBuilderBase(const locop::SymbolTable *tbl) : _tbl{tbl} + { + // DO NOTHING + } + +public: + bool build(const loco::Node *, locop::NodeSummary &s) const final; + +protected: +#define CIRCLE_NODE(OPCODE, CLASS) \ + virtual bool summary(const CLASS *, locop::NodeSummary &s) const \ + { \ + s.comments().append("Emitted by Default CircleNodeSummaryBuilder"); \ + s.state(locop::NodeSummary::State::PartiallyKnown); \ + return true; \ + } +#include <luci/IR/CircleNodes.lst> +#undef CIRCLE_NODE + +protected: + const locop::SymbolTable *tbl(void) const { return _tbl; } + + // Please do not use _tbl directly and use tbl(). + // This will be changed to private in near future. +protected: + const locop::SymbolTable *_tbl; +}; + +class CircleNodeSummaryBuilder final : public CircleNodeSummaryBuilderBase +{ +public: + CircleNodeSummaryBuilder(const locop::SymbolTable *tbl) : CircleNodeSummaryBuilderBase(tbl) + { + // DO NOTHING + } + +private: +#define IMPLEMENT(CLASS) bool summary(const CLASS *, locop::NodeSummary &) const final; + IMPLEMENT(luci::CircleAbs) + IMPLEMENT(luci::CircleAdd) + IMPLEMENT(luci::CircleArgMax) + IMPLEMENT(luci::CircleAveragePool2D) + IMPLEMENT(luci::CircleBatchToSpaceND) + IMPLEMENT(luci::CircleConcatenation) + IMPLEMENT(luci::CircleConst) + IMPLEMENT(luci::CircleConv2D) + IMPLEMENT(luci::CircleCos) + IMPLEMENT(luci::CircleDepthwiseConv2D) + IMPLEMENT(luci::CircleDiv) + IMPLEMENT(luci::CircleExp) + IMPLEMENT(luci::CircleFullyConnected) + IMPLEMENT(luci::CircleLogicalNot) + IMPLEMENT(luci::CircleLogicalOr) + IMPLEMENT(luci::CircleMaximum) + IMPLEMENT(luci::CircleMaxPool2D) + IMPLEMENT(luci::CircleMean) + IMPLEMENT(luci::CircleMul) + IMPLEMENT(luci::CirclePack) + IMPLEMENT(luci::CirclePad) + IMPLEMENT(luci::CircleRelu) + IMPLEMENT(luci::CircleRelu6) + IMPLEMENT(luci::CircleReshape) + IMPLEMENT(luci::CircleRsqrt) + IMPLEMENT(luci::CircleSoftmax) + IMPLEMENT(luci::CircleSqrt) + IMPLEMENT(luci::CircleSquaredDifference) + IMPLEMENT(luci::CircleSub) + IMPLEMENT(luci::CircleTranspose) + IMPLEMENT(luci::CircleTransposeConv) + // Circle Only + IMPLEMENT(luci::CircleInstanceNorm) + // Virtual nodes + IMPLEMENT(luci::CircleInput) + IMPLEMENT(luci::CircleOutput) +#undef IMPLEMENT +}; + +bool CircleNodeSummaryBuilderBase::build(const loco::Node *node, locop::NodeSummary &s) const +{ + if (node->dialect() != luci::CircleDialect::get()) + return false; + +#define CIRCLE_NODE(OPCODE, CLASS) \ + if (dynamic_cast<const CLASS *>(node)) \ + { \ + s.opname(circle_opname(node->opnum())); \ + return summary(dynamic_cast<const CLASS *>(node), s); \ + } +#include <luci/IR/CircleNodes.lst> +#undef CIRCLE_NODE + + return false; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleAbs *node, locop::NodeSummary &s) const +{ + s.args().append("x", tbl()->lookup(node->x())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleAdd *node, locop::NodeSummary &s) const +{ + assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED); + + s.args().append("x", tbl()->lookup(node->x())); + s.args().append("y", tbl()->lookup(node->y())); + s.args().append("fused_activation_function", to_str(node->fusedActivationFunction())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleArgMax *node, locop::NodeSummary &s) const +{ + s.args().append("input", tbl()->lookup(node->input())); + s.args().append("dimension", tbl()->lookup(node->dimension())); + s.args().append("output_type", to_str(node->output_type())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleAveragePool2D *node, + locop::NodeSummary &s) const +{ + assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED); + + s.args().append("value", tbl()->lookup(node->value())); + s.args().append("filter(h,w)", to_str(node->filter())); + s.args().append("stride(h,w)", to_str(node->stride())); + s.args().append("padding", to_str(node->padding())); + s.args().append("fused", to_str(node->fusedActivationFunction())); + + s.state(locop::NodeSummary::State::Complete); + + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleBatchToSpaceND *node, + locop::NodeSummary &s) const +{ + s.args().append("input", tbl()->lookup(node->input())); + s.args().append("block_shape", tbl()->lookup(node->block_shape())); + s.args().append("crops", tbl()->lookup(node->crops())); + + s.state(locop::NodeSummary::State::Complete); + + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleConcatenation *node, + locop::NodeSummary &s) const +{ + assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED); + + for (uint32_t i = 0; i < node->numValues(); ++i) + s.args().append("values", tbl()->lookup(node->values(i))); + s.args().append("axis", pepper::str(node->axis())); + s.args().append("fused", to_str(node->fusedActivationFunction())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleConst *, locop::NodeSummary &s) const +{ + s.state(locop::NodeSummary::State::PartiallyKnown); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleConv2D *node, locop::NodeSummary &s) const +{ + assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED); + assert(node->padding() != luci::Padding::UNDEFINED); + + s.args().append("input", tbl()->lookup(node->input())); + s.args().append("filter", tbl()->lookup(node->filter())); + s.args().append("bias", tbl()->lookup(node->bias())); + + s.args().append("stride(h,w)", to_str(node->stride())); + s.args().append("padding", to_str(node->padding())); + s.args().append("fused", to_str(node->fusedActivationFunction())); + + s.state(locop::NodeSummary::State::Complete); + + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleCos *node, locop::NodeSummary &s) const +{ + s.args().append("x", tbl()->lookup(node->x())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleDepthwiseConv2D *node, + locop::NodeSummary &s) const +{ + assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED); + assert(node->padding() != luci::Padding::UNDEFINED); + + s.args().append("input", tbl()->lookup(node->input())); + s.args().append("filter", tbl()->lookup(node->filter())); + s.args().append("bias", tbl()->lookup(node->bias())); + + s.args().append("stride(h,w)", to_str(node->stride())); + s.args().append("padding", to_str(node->padding())); + s.args().append("depthMultiplier", std::to_string(node->depthMultiplier())); + s.args().append("fused", to_str(node->fusedActivationFunction())); + + s.state(locop::NodeSummary::State::Complete); + + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleDiv *node, locop::NodeSummary &s) const +{ + s.args().append("x", tbl()->lookup(node->x())); + s.args().append("y", tbl()->lookup(node->y())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleExp *node, locop::NodeSummary &s) const +{ + s.args().append("x", tbl()->lookup(node->x())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleFullyConnected *node, + locop::NodeSummary &s) const +{ + assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED); + + s.args().append("input", tbl()->lookup(node->input())); + s.args().append("weights", tbl()->lookup(node->weights())); + s.args().append("bias", tbl()->lookup(node->bias())); + s.args().append("fused", to_str(node->fusedActivationFunction())); + + s.state(locop::NodeSummary::State::Complete); + + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleLogicalNot *node, + locop::NodeSummary &s) const +{ + s.args().append("x", tbl()->lookup(node->x())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleLogicalOr *node, + locop::NodeSummary &s) const +{ + s.args().append("x", tbl()->lookup(node->x())); + s.args().append("y", tbl()->lookup(node->y())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleMaximum *node, locop::NodeSummary &s) const +{ + s.args().append("x", tbl()->lookup(node->x())); + s.args().append("y", tbl()->lookup(node->y())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleMaxPool2D *node, + locop::NodeSummary &s) const +{ + assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED); + + s.args().append("value", tbl()->lookup(node->value())); + s.args().append("filter(h,w)", to_str(node->filter())); + s.args().append("stride(h,w)", to_str(node->stride())); + s.args().append("padding", to_str(node->padding())); + s.args().append("fused", to_str(node->fusedActivationFunction())); + + s.state(locop::NodeSummary::State::Complete); + + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleMean *node, locop::NodeSummary &s) const +{ + s.args().append("input", tbl()->lookup(node->input())); + s.args().append("reduction_indices", tbl()->lookup(node->reduction_indices())); + s.args().append("keep_dims", node->keep_dims() ? "true" : "false"); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleMul *node, locop::NodeSummary &s) const +{ + assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED); + + s.args().append("x", tbl()->lookup(node->x())); + s.args().append("y", tbl()->lookup(node->y())); + s.args().append("fused_activation_function", to_str(node->fusedActivationFunction())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CirclePack *node, locop::NodeSummary &s) const +{ + for (uint32_t i = 0; i < node->values_count(); ++i) + s.args().append("values", tbl()->lookup(node->values(i))); + s.args().append("values_count", pepper::str(node->values_count())); + s.args().append("axis", pepper::str(node->axis())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CirclePad *node, locop::NodeSummary &s) const +{ + s.args().append("input", tbl()->lookup(node->input())); + s.args().append("paddings", tbl()->lookup(node->paddings())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleRelu *node, locop::NodeSummary &s) const +{ + s.args().append("features", tbl()->lookup(node->features())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleRelu6 *node, locop::NodeSummary &s) const +{ + s.args().append("features", tbl()->lookup(node->features())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleReshape *node, locop::NodeSummary &s) const +{ + s.args().append("tensor", tbl()->lookup(node->tensor())); + s.args().append("shape", tbl()->lookup(node->shape())); + // TODO Show newShape info + s.state(locop::NodeSummary::State::PartiallyKnown); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleRsqrt *node, locop::NodeSummary &s) const +{ + s.args().append("x", tbl()->lookup(node->x())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleSoftmax *node, locop::NodeSummary &s) const +{ + s.args().append("logits", tbl()->lookup(node->logits())); + s.args().append("beta", pepper::str(node->beta())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleSqrt *node, locop::NodeSummary &s) const +{ + s.args().append("x", tbl()->lookup(node->x())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleSquaredDifference *node, + locop::NodeSummary &s) const +{ + s.args().append("x", tbl()->lookup(node->x())); + s.args().append("y", tbl()->lookup(node->y())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleSub *node, locop::NodeSummary &s) const +{ + s.args().append("x", tbl()->lookup(node->x())); + s.args().append("y", tbl()->lookup(node->y())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +// TODO TFLTanh + +bool CircleNodeSummaryBuilder::summary(const luci::CircleTranspose *node, + locop::NodeSummary &s) const +{ + s.args().append("a", tbl()->lookup(node->a())); + s.args().append("perm", tbl()->lookup(node->perm())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleTransposeConv *node, + locop::NodeSummary &s) const +{ + assert(node->padding() != luci::Padding::UNDEFINED); + + s.args().append("inputSizes", tbl()->lookup(node->inputSizes())); + s.args().append("filter", tbl()->lookup(node->filter())); + s.args().append("outBackprop", tbl()->lookup(node->outBackprop())); + + s.args().append("stride(h,w)", to_str(node->stride())); + s.args().append("padding", to_str(node->padding())); + + s.state(locop::NodeSummary::State::Complete); + + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleInput *, locop::NodeSummary &s) const +{ + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleOutput *node, locop::NodeSummary &s) const +{ + s.args().append("from", tbl()->lookup(node->from())); + + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleInstanceNorm *node, + locop::NodeSummary &s) const +{ + auto fused = node->fusedActivationFunction(); + assert(fused != luci::FusedActFunc::UNDEFINED); + + s.args().append("input", tbl()->lookup(node->input())); + s.args().append("gamma", tbl()->lookup(node->gamma())); + s.args().append("beta", tbl()->lookup(node->beta())); + s.args().append("epsilon", pepper::str(node->epsilon())); + s.args().append("fused_activation_function", to_str(fused)); + + s.state(locop::NodeSummary::State::Complete); + + return true; +} + +} // namespace + +namespace luci +{ + +bool NodeSummaryBuilder::build(const loco::Node *node, locop::NodeSummary &s) const +{ + if (locop::CanonicalNodeSummaryBuilder(_tbl).build(node, s)) + { + return true; + } + + if (CircleNodeSummaryBuilder(_tbl).build(node, s)) + { + return true; + } + + return false; +} + +} // namespace exo diff --git a/compiler/luci/logex/src/LogHelper.cpp b/compiler/luci/logex/src/LogHelper.cpp new file mode 100644 index 000000000..caf659906 --- /dev/null +++ b/compiler/luci/logex/src/LogHelper.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020 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 "luci/LogHelper.h" +#include "luci/FormattedGraph.h" + +namespace luci +{ + +FormattedGraph fmt(loco::Graph *g) +{ + auto node_summary_builder = std::make_unique<NodeSummaryBuilderFactory>(); + return std::move(locop::fmt<locop::LinearV1>(g).with(std::move(node_summary_builder))); +} + +} // namespace luci diff --git a/compiler/luci/pass/CMakeLists.txt b/compiler/luci/pass/CMakeLists.txt new file mode 100644 index 000000000..93130ce60 --- /dev/null +++ b/compiler/luci/pass/CMakeLists.txt @@ -0,0 +1,29 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +#file(GLOB_RECURSE TESTS "src/*.test.cpp") +#list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(luci_pass SHARED ${SOURCES}) +target_include_directories(luci_pass PRIVATE src) +target_include_directories(luci_pass PUBLIC include) +target_link_libraries(luci_pass PUBLIC loco) +target_link_libraries(luci_pass PUBLIC logo_core) +target_link_libraries(luci_pass PRIVATE logo) +target_link_libraries(luci_pass PRIVATE luci_lang) +target_link_libraries(luci_pass PRIVATE luci_log) +target_link_libraries(luci_pass PRIVATE luci_service) +target_link_libraries(luci_pass PRIVATE luci_logex) +target_link_libraries(luci_pass PRIVATE nncc_common) +target_link_libraries(luci_pass PRIVATE oops) +install(TARGETS luci_pass DESTINATION lib) + +# TODO enable for tests +#if(NOT ENABLE_TEST) +# return() +#endif(NOT ENABLE_TEST) +# +#nnas_find_package(GTest REQUIRED) +# +#GTest_AddTest(luci_pass_test ${TESTS}) +#target_include_directories(luci_pass_test PRIVATE src) +#target_link_libraries(luci_pass_test luci_pass) +#target_link_libraries(luci_pass_test oops) diff --git a/compiler/luci/pass/README.md b/compiler/luci/pass/README.md new file mode 100644 index 000000000..9b6cdebd3 --- /dev/null +++ b/compiler/luci/pass/README.md @@ -0,0 +1,3 @@ +# luci-pass + +_luci-pass_ provides Circle Dialect transformation passes diff --git a/compiler/luci/pass/include/luci/CircleOptimizer.h b/compiler/luci/pass/include/luci/CircleOptimizer.h new file mode 100644 index 000000000..a969cca85 --- /dev/null +++ b/compiler/luci/pass/include/luci/CircleOptimizer.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 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 __LUCI_CIRCLE_OPTIMIZER_H__ +#define __LUCI_CIRCLE_OPTIMIZER_H__ + +#include <loco.h> + +#include <string> +#include <vector> + +namespace luci +{ + +class CircleOptimizer final +{ +public: + struct Options + { + enum Algorithm + { + FuseInstanceNorm, + }; + + virtual void enable(Algorithm) = 0; + virtual bool query(Algorithm) = 0; + }; + +public: + // TODO maybe caller can provide Options as ctor parameters + Options *options(void); + +public: + void optimize(loco::Graph *) const; + +private: + std::unique_ptr<Options> _options; +}; + +} // namespace luci + +#endif // __LUCI_CIRCLE_OPTIMIZER_H__ diff --git a/compiler/luci/pass/include/luci/Pass/FuseInstanceNormPass.h b/compiler/luci/pass/include/luci/Pass/FuseInstanceNormPass.h new file mode 100644 index 000000000..800a5f789 --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/FuseInstanceNormPass.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020 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 __LUCI_FUSE_INSTANCE_NORM_PASS_H__ +#define __LUCI_FUSE_INSTANCE_NORM_PASS_H__ + +#include <logo/Pass.h> + +namespace luci +{ + +/** + * @brief Class to fuse certain pattern of subgraph into CircleInstanceNorm + * with auxiliary nodes + * + * For detailed subgraph pattern to be fused, please check its implementation. + */ +struct FuseInstanceNormPass final : public logo::Pass +{ + const char *name(void) const final { return "luci::FuseInstanceNormPass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci + +#endif // __LUCI_FUSE_INSTANCE_NORM_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/ShapeInferencePass.h b/compiler/luci/pass/include/luci/Pass/ShapeInferencePass.h new file mode 100644 index 000000000..86bb2ab42 --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/ShapeInferencePass.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 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 __LUCI_SHAPE_INFERENCE_PASS_H__ +#define __LUCI_SHAPE_INFERENCE_PASS_H__ + +#include <loco.h> + +#include <logo/Pass.h> + +namespace luci +{ + +/** + * @brief Pass to infer shape of nodes + */ +class ShapeInferencePass : public logo::Pass +{ +public: + virtual const char *name(void) const { return "luci::ShapeInferencePass"; } + +public: + bool run(loco::Graph *graph); +}; + +} // namespace luci + +#endif //__LUCI_SHAPE_INFERENCE_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/TypeInferencePass.h b/compiler/luci/pass/include/luci/Pass/TypeInferencePass.h new file mode 100644 index 000000000..c607ac63f --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/TypeInferencePass.h @@ -0,0 +1,42 @@ + +/* + * Copyright (c) 2020 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 __LUCI_TYPE_INFERENCE_PASS_H__ +#define __LUCI_TYPE_INFERENCE_PASS_H__ + +#include <loco.h> + +#include <logo/Pass.h> + +namespace luci +{ + +/** + * @brief Pass to infer type of nodes + */ +class TypeInferencePass : public logo::Pass +{ +public: + virtual const char *name(void) const { return "luci::TypeInferencePass"; } + +public: + bool run(loco::Graph *graph); +}; + +} // namespace luci + +#endif //__LUCI_TYPE_INFERENCE_PASS_H__ diff --git a/compiler/luci/pass/src/CircleOptimizer.cpp b/compiler/luci/pass/src/CircleOptimizer.cpp new file mode 100644 index 000000000..dcb05a0b5 --- /dev/null +++ b/compiler/luci/pass/src/CircleOptimizer.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2020 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 "luci/CircleOptimizer.h" + +#include "luci/Pass/FuseInstanceNormPass.h" +// TODO add more passes + +#include "luci/Pass/ShapeInferencePass.h" +#include "luci/Pass/TypeInferencePass.h" + +// logo passes +#include <logo/RemoveDeadNodePass.h> + +#include "ProgressReporter.h" + +#include <logo/Phase.h> + +#include <memory> + +namespace +{ + +using namespace luci; + +class OptimizeOptionsImpl : public luci::CircleOptimizer::Options +{ +public: + void enable(Algorithm) final; + bool query(Algorithm) final; + +private: + std::vector<Algorithm> _algorithms; +}; + +void OptimizeOptionsImpl::enable(Algorithm algo) { _algorithms.push_back(algo); } + +bool OptimizeOptionsImpl::query(Algorithm algo) +{ + std::vector<Algorithm>::iterator it = std::find(_algorithms.begin(), _algorithms.end(), algo); + if (it == _algorithms.end()) + return false; + + return true; +} + +} // namespace + +namespace luci +{ + +CircleOptimizer::Options *CircleOptimizer::options(void) +{ + if (_options == nullptr) + { + _options = std::make_unique<OptimizeOptionsImpl>(); + } + + return _options.get(); +} + +void CircleOptimizer::optimize(loco::Graph *g) const +{ + logo::Phase phase; + + /* TRANSFORM DECLARATION BEGIN */ + if (_options->query(Options::Algorithm::FuseInstanceNorm)) + { + phase.emplace_back(std::make_unique<FuseInstanceNormPass>()); + } + // Shape inference is needed for added nodes doing above transformations + phase.emplace_back(std::make_unique<luci::ShapeInferencePass>()); + phase.emplace_back(std::make_unique<luci::TypeInferencePass>()); + phase.emplace_back(std::make_unique<logo::RemoveDeadNodePass>()); + /* TRANSFORM DECLARATION END */ + + ProgressReporter prog(g, logo::PhaseStrategy::Saturate); + logo::PhaseRunner<logo::PhaseStrategy::Saturate> phase_runner{g}; + phase_runner.attach(&prog); + phase_runner.run(phase); +} + +} // namespace luci diff --git a/compiler/luci/pass/src/FuseInstanceNormPass.cpp b/compiler/luci/pass/src/FuseInstanceNormPass.cpp new file mode 100644 index 000000000..180b5bbef --- /dev/null +++ b/compiler/luci/pass/src/FuseInstanceNormPass.cpp @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2020 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 "luci/Pass/FuseInstanceNormPass.h" + +#include <luci/IR/CircleNodes.h> + +#include <loco/Service/ShapeInference.h> + +#include <cassert> +#include <set> + +// Helper to find commutative node's arguments +namespace +{ + +/** + * INTRODUCTION + * Binary operation f(x,y) is 'commutative' when + * f(x,y) == f(y,x) holds for all x, y. + * For examples, ADD, MUL and SQUARED_DIFFERENCE are commutative. + * These helpers make it easy to find commutative arguemnts of commtative node. + * + * HOW TO USE + * COMM_NODE *node; + * ARG_TYPE_1 *arg1; + * ARG_TYPE_2 *arg2; + * + * bool ok = fill(&arg1, &arg2).with_commutative_args_of(node); + * + * Result + * If 'node's commutative argument types are actually {ARG_TYPE_1, ARG_TYPE_2} + * (as a set), 'arg1' and 'arg2' set as actual 'node's arguemnts with matching + * type, and return value 'ok' is true. + * Otherwise, 'arg1' and 'arg2' not changed, 'ok' is false. + */ + +template <class ARG_TYPE_1, class ARG_TYPE_2> class NodeFiller final +{ +public: + NodeFiller(ARG_TYPE_1 **arg_1, ARG_TYPE_2 **arg_2) : _arg_1(arg_1), _arg_2(arg_2) + { + // DO NOTHING + } + + /** + * @return true When 'node's argument types are 'ARG_TYPE_1' and 'ARG_TYPE_2' + * In such case, it assign '_arg_1' and '_arg_2' to actual arguments + * + * @return false When 'node's argument types are NOT matched with 'ARG_TYPE_*' + * In such case, it does not amend '_arg_1' and '_arg_2' + * + * @require COMM_NODE has member x() and y() + */ + template <class COMM_NODE> bool with_commutative_args_of(const COMM_NODE *node); + +private: + ARG_TYPE_1 **_arg_1; + ARG_TYPE_2 **_arg_2; +}; + +template <class ARG_TYPE_1, class ARG_TYPE_2> +inline NodeFiller<ARG_TYPE_1, ARG_TYPE_2> fill(ARG_TYPE_1 **arg_1, ARG_TYPE_2 **arg_2) +{ + return NodeFiller<ARG_TYPE_1, ARG_TYPE_2>{arg_1, arg_2}; +} + +template <class ARG_TYPE_1, class ARG_TYPE_2> +template <class COMM_NODE> +bool NodeFiller<ARG_TYPE_1, ARG_TYPE_2>::with_commutative_args_of(const COMM_NODE *node) +{ + // Case 1) X == ARG_TYPE_1 / Y == ARG_TYPE_2 + { + auto x = dynamic_cast<ARG_TYPE_1 *>(node->x()); + auto y = dynamic_cast<ARG_TYPE_2 *>(node->y()); + + if (x && y) + { + *_arg_1 = x; + *_arg_2 = y; + return true; + } + } + + // Case 2) X == ARG_TYPE_2 / Y == ARG_TYPE_1 + { + auto x = dynamic_cast<ARG_TYPE_2 *>(node->x()); + auto y = dynamic_cast<ARG_TYPE_1 *>(node->y()); + + if (x && y) + { + *_arg_1 = y; + *_arg_2 = x; + return true; + } + } + + return false; +} + +} // namespace + +// Helper to check detail +namespace +{ + +/// @return true When node has shape of '1 x .. x 1 x depth' +bool is_1D_with_dummy_dim(luci::CircleConst *node, uint32_t depth) +{ + auto rank = node->rank(); + uint32_t axis; + for (axis = 0; axis < rank - 1; ++axis) + { + if (node->dim(axis).value() != 1) + return false; + } + return node->dim(axis).value() == depth; +} + +bool is_instance_mean(luci::CircleMean *mean) +{ + // + // CHECK 1) input is rank 4 + // + auto input = mean->input(); + if (not loco::shape_known(input)) + return false; + auto input_shape = loco::shape_get(input).as<loco::TensorShape>(); + if (input_shape.rank() != 4) + return false; + + // + // CHECK 2) 'reduction indices' is CircleConst of value [1,2], that is HW of NHWC + // + // TODO Support equivalent case, like [-3,-2] + // TODO Support non-Const case? + // TODO What if input is NCHW format in Circle? + auto red_indices = dynamic_cast<luci::CircleConst *>(mean->reduction_indices()); + if (not red_indices) + return false; + if (red_indices->rank() != 1) + return false; + std::set<int32_t> red_indices_set; + { + // TODO Currently only support S32, support other types + assert(red_indices->dtype() == loco::DataType::S32); + for (uint32_t i = 0; i < red_indices->dim(0).value(); ++i) + red_indices_set.insert(red_indices->at<loco::DataType::S32>(i)); + } + if (red_indices_set.size() != 2) + return false; + if (red_indices_set.find(1) == red_indices_set.end()) + return false; + if (red_indices_set.find(2) == red_indices_set.end()) + return false; + + // + // CHECK 3) keep_dims == true (?) + // + // We only have case of 'keep_dims == true' so far, but it might be okay with 'keep_dims == false' + // TODO Check this fact, and if true, return true regardless of keep_dims + return mean->keep_dims(); +} + +} // namespace + +// Helper to fuse Instance Norm +namespace +{ + +/** + * SUBGRAPH PATTERN + * + * - Below diagram shows Instance Norm pattern to fuse. + * - Execution dependency order is top to the bottom. + * - Node name is matched with variable name of InstanceNormPattern class. + * - Usually, first word of node name (variable name) is node type. For e.g. + * variable 'mean_as_variance' is pointer to TFLMean. + * - (Item in parenthesis) means actually exist, but not having a name and + * not a variable of InstanceNormPattern class. + * + * TODO support other semantically same patterns for instance norm + * + * [In] + * | + * V + * +----------- ifm -----+ (reduction indicies) + * | | | | + * | | V V + * | | mean_of_ifm ----------------+ + * | V | | + * | sqdiff <--+ (reduction indicies) | + * | | | | + * | V | | + * | mean_as_variance <---+ const_as_epsilon | + * | | | | + * | V | | + * | add_as_variance <--------+ | + * | | | + * | V | + * | rsqrt const_as_gamma | + * | | | | + * | V | | + * | mul_gamma <--+ | + * | | | | + * V V V | + * mul_as_scaled_ifm mul_as_scaled_mean <-------------+ + * | | + * | const_as_beta | + * | | V + * | +------> sub + * V | + * add_as_terminal <----------+ + * | + * V + * [Out] + */ +class InstanceNormPattern final +{ +public: + InstanceNormPattern(luci::CircleAdd *candidate) + { + assert(candidate); + add_as_terminal = candidate; + } + +public: + bool matched(); + bool matched() const { return _matched; } + +public: + // Context + loco::Node *ifm = nullptr; + luci::CircleMean *mean_of_ifm = nullptr; + luci::CircleSquaredDifference *sqdiff = nullptr; + luci::CircleMean *mean_as_variance = nullptr; + luci::CircleConst *const_as_epsilon = nullptr; + luci::CircleAdd *add_as_variance = nullptr; + luci::CircleRsqrt *rsqrt = nullptr; + luci::CircleConst *const_as_gamma = nullptr; + luci::CircleMul *mul_gamma = nullptr; + luci::CircleMul *mul_as_scaled_ifm = nullptr; + luci::CircleMul *mul_as_scaled_mean = nullptr; + luci::CircleConst *const_as_beta = nullptr; + luci::CircleSub *sub = nullptr; + luci::CircleAdd *add_as_terminal = nullptr; + +private: + bool _matched = false; +}; + +bool InstanceNormPattern::matched() +{ + if (_matched) + return true; + +#define CHECK_OR_FALSE(condition) \ + if (not(condition)) \ + return false; + + // Check order is DFS + + CHECK_OR_FALSE(fill(&mul_as_scaled_ifm, &sub).with_commutative_args_of(add_as_terminal)); + CHECK_OR_FALSE(fill(&ifm, &mul_gamma).with_commutative_args_of(mul_as_scaled_ifm)); + + CHECK_OR_FALSE(loco::shape_known(ifm)); + auto ifm_shape = loco::shape_get(ifm); + CHECK_OR_FALSE(ifm_shape.domain() == loco::Domain::Tensor); + auto ifm_tensor_shape = ifm_shape.as<loco::TensorShape>(); + CHECK_OR_FALSE(ifm_tensor_shape.rank() == 4); + uint32_t ifm_channel_depth = ifm_tensor_shape.dim(3).value(); + + CHECK_OR_FALSE(fill(&rsqrt, &const_as_gamma).with_commutative_args_of(mul_gamma)); + CHECK_OR_FALSE(is_1D_with_dummy_dim(const_as_gamma, ifm_channel_depth)); + + add_as_variance = dynamic_cast<luci::CircleAdd *>(rsqrt->x()); + CHECK_OR_FALSE(add_as_variance); + + CHECK_OR_FALSE( + fill(&mean_as_variance, &const_as_epsilon).with_commutative_args_of(add_as_variance)); + + CHECK_OR_FALSE(const_as_epsilon->dtype() == loco::DataType::FLOAT32); + // TODO Support regarding broadcast + CHECK_OR_FALSE(const_as_epsilon->size<loco::DataType::FLOAT32>() == 1); + + CHECK_OR_FALSE(is_instance_mean(mean_as_variance)); + sqdiff = dynamic_cast<luci::CircleSquaredDifference *>(mean_as_variance->input()); + CHECK_OR_FALSE(sqdiff); + + loco::Node *ifm_should_be = nullptr; + CHECK_OR_FALSE(fill(&ifm_should_be, &mean_of_ifm).with_commutative_args_of(sqdiff)); + CHECK_OR_FALSE(ifm == ifm_should_be); + CHECK_OR_FALSE(is_instance_mean(mean_of_ifm)); + CHECK_OR_FALSE(ifm == mean_of_ifm->input()); + + const_as_beta = dynamic_cast<luci::CircleConst *>(sub->x()); + CHECK_OR_FALSE(const_as_beta); + CHECK_OR_FALSE(is_1D_with_dummy_dim(const_as_beta, ifm_channel_depth)); + + mul_as_scaled_mean = dynamic_cast<luci::CircleMul *>(sub->y()); + CHECK_OR_FALSE(mul_as_scaled_mean); + + luci::CircleMul *mul_gamma_should_be = nullptr; + luci::CircleMean *mean_of_ifm_should_be = nullptr; + CHECK_OR_FALSE(fill(&mul_gamma_should_be, &mean_of_ifm_should_be) + .with_commutative_args_of(mul_as_scaled_mean)); + CHECK_OR_FALSE(mul_gamma == mul_gamma_should_be); + CHECK_OR_FALSE(mean_of_ifm == mean_of_ifm_should_be); +#undef CHECK_OR_FALSE + _matched = true; + return true; +} + +/** + * Instance norm pattern would be fused like following diagram: + * + * [In] --------------------------- CircleInstanceNorm --- [Out] + * / / + * const_as_gamma --- TFLReshape --- / + * / + * const_as_beta ---- TFLReshape --- + * + * Note + * - 'const_as_gamma' and 'const_as_beta' are from original graph + * - Value of 'const_as_epsilon' would be copied to CircleInstanceNorm's attribute + * - TFLReshape is added as CircleInstanceNorm only accept 1D tensor + * - 'CircleConst --- TFLReshape' is expected to be fused in constant folding for Reshape + */ +void fuse_instance_norm(const InstanceNormPattern &p) +{ + assert(p.matched()); + + auto graph = p.add_as_terminal->graph(); + + // Make reshape for gamma & beta + auto reshape_gamma = graph->nodes()->create<luci::CircleReshape>(); + auto reshape_beta = graph->nodes()->create<luci::CircleReshape>(); + { + auto ifm_shape = loco::shape_get(p.ifm).as<loco::TensorShape>(); + uint32_t ifm_channel_depth = ifm_shape.dim(3).value(); + + int32_t new_shape[1] = {static_cast<int32_t>(ifm_channel_depth)}; + + reshape_gamma->tensor(p.const_as_gamma); + reshape_beta->tensor(p.const_as_beta); + + luci::set_new_shape(reshape_gamma, new_shape, 1); + luci::set_new_shape(reshape_beta, new_shape, 1); + } + + // Make Instance Norm to replace + auto instance_norm = graph->nodes()->create<luci::CircleInstanceNorm>(); + instance_norm->input(p.ifm); + instance_norm->gamma(reshape_gamma); + instance_norm->beta(reshape_beta); + float epsilon = p.const_as_epsilon->at<loco::DataType::FLOAT32>(0); + instance_norm->epsilon(epsilon); + instance_norm->fusedActivationFunction(p.add_as_terminal->fusedActivationFunction()); + + replace(p.add_as_terminal).with(instance_norm); +} + +} // namespace + +namespace luci +{ + +bool FuseInstanceNormPass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + auto add = dynamic_cast<luci::CircleAdd *>(node); + if (not add) + continue; + + InstanceNormPattern pattern(add); + if (not pattern.matched()) + continue; + + fuse_instance_norm(pattern); + changed = true; + } + + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/ProgressReporter.cpp b/compiler/luci/pass/src/ProgressReporter.cpp new file mode 100644 index 000000000..dcf47aba6 --- /dev/null +++ b/compiler/luci/pass/src/ProgressReporter.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2020 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 "ProgressReporter.h" + +#include <luci/Log.h> +#include <luci/LogHelper.h> + +#include <logo/Phase.h> +#include <logo/Pass.h> + +#include <cassert> + +namespace +{ + +char to_char(bool b) { return b ? 'Y' : 'N'; } + +const char *to_str(logo::PhaseStrategy s) +{ + switch (s) + { + case logo::PhaseStrategy::Saturate: + return "Saturate"; + case logo::PhaseStrategy::Restart: + return "Restart"; + } + assert(false); + return ""; +} + +} // namespace + +namespace luci +{ + +void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseBegin> *) +{ + LOGGER(prime); + + INFO(prime) << "=============================================================="; + INFO(prime) << "PhaseRunner<" << to_str(strategy()) << ">"; + INFO(prime) << "Initial graph"; + INFO(prime) << luci::fmt(graph()); +} + +void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseEnd> *) +{ + LOGGER(prime); + + INFO(prime) << "PhaseRunner<" << to_str(strategy()) << "> - done"; +} + +void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassBegin> *info) +{ + LOGGER(prime); + + INFO(prime) << "--------------------------------------------------------------"; + INFO(prime) << "Before " << logo::pass_name(info->pass()); +} + +void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassEnd> *info) +{ + LOGGER(prime); + + INFO(prime) << "After " << logo::pass_name(info->pass()) + << " (changed: " << to_char(info->changed()) << ")"; + INFO(prime) << luci::fmt(graph()); +} + +} // namespace luci diff --git a/compiler/luci/pass/src/ProgressReporter.h b/compiler/luci/pass/src/ProgressReporter.h new file mode 100644 index 000000000..bd2ba9849 --- /dev/null +++ b/compiler/luci/pass/src/ProgressReporter.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020 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 __LUCI_PROGRESSREPORTER_H__ +#define __LUCI_PROGRESSREPORTER_H__ + +#include <logo/Phase.h> + +#include <loco.h> + +namespace luci +{ + +class ProgressReporter : public logo::PhaseEventListener +{ +public: + ProgressReporter(loco::Graph *graph, logo::PhaseStrategy strategy) + : _graph{graph}, _strategy{strategy} + { + // DO NOTHING + } + +public: + void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseBegin> *) override; + void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseEnd> *) override; + void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassBegin> *) override; + void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassEnd> *) override; + +public: + loco::Graph *graph(void) const { return _graph; } + logo::PhaseStrategy strategy(void) const { return _strategy; } + +private: + loco::Graph *_graph; + logo::PhaseStrategy _strategy; +}; + +} // namespace luci + +#endif // __LUCI_PROGRESSREPORTER_H__ diff --git a/compiler/luci/pass/src/ShapeInferencePass.cpp b/compiler/luci/pass/src/ShapeInferencePass.cpp new file mode 100644 index 000000000..f681b3d5f --- /dev/null +++ b/compiler/luci/pass/src/ShapeInferencePass.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 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 "luci/Pass/ShapeInferencePass.h" + +#include <luci/IR/CircleDialect.h> +#include <luci/Service/CircleShapeInferenceRule.h> + +#include <loco.h> +#include <loco/IR/CanonicalDialect.h> +#include <loco/Service/CanonicalShapeInferenceRule.h> +#include <loco/Service/ShapeInference.h> +#include <loco/Service/MultiDialectShapeInferenceRule.h> + +namespace luci +{ + +bool ShapeInferencePass::run(loco::Graph *g) +{ + loco::CanonicalShapeInferenceRule canonical_rule; + luci::CircleShapeInferenceRule circle_rule; + + loco::MultiDialectShapeInferenceRule rules; + + rules.bind(loco::CanonicalDialect::get(), &canonical_rule) + .bind(luci::CircleDialect::get(), &circle_rule); + + return loco::apply(&rules).to(g); +} + +} // namespace luci diff --git a/compiler/luci/pass/src/TypeInferencePass.cpp b/compiler/luci/pass/src/TypeInferencePass.cpp new file mode 100644 index 000000000..2c7b3a897 --- /dev/null +++ b/compiler/luci/pass/src/TypeInferencePass.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020 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 "luci/Pass/TypeInferencePass.h" + +#include <luci/IR/CircleDialect.h> +#include <luci/Service/CircleTypeInferenceRule.h> + +#include <loco.h> +#include <loco/IR/CanonicalDialect.h> +#include <loco/Service/TypeInference.h> + +namespace luci +{ + +bool TypeInferencePass::run(loco::Graph *g) +{ + loco::CanonicalTypeInferenceRule canonical_rule; + luci::CircleTypeInferenceRule circle_rule; + + loco::MultiDialectTypeInferenceRule rules; + + rules.bind(loco::CanonicalDialect::get(), &canonical_rule) + .bind(luci::CircleDialect::get(), &circle_rule); + + return loco::apply(&rules).to(g); +} + +} // namespace luci diff --git a/compiler/luci/requires.cmake b/compiler/luci/requires.cmake new file mode 100644 index 000000000..e88dabd24 --- /dev/null +++ b/compiler/luci/requires.cmake @@ -0,0 +1,9 @@ +require("loco") +require("locop") +require("logo-core") +require("mio-circle") +require("oops") +require("hermes") +require("hermes-std") +require("tflchef") +require("tflite2circle") diff --git a/compiler/luci/service/CMakeLists.txt b/compiler/luci/service/CMakeLists.txt new file mode 100644 index 000000000..9f50c9c4f --- /dev/null +++ b/compiler/luci/service/CMakeLists.txt @@ -0,0 +1,25 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(luci_service SHARED ${SOURCES}) +target_include_directories(luci_service PRIVATE src) +target_include_directories(luci_service PUBLIC include) +target_link_libraries(luci_service PUBLIC luci_lang) +target_link_libraries(luci_service PUBLIC mio_circle) +target_link_libraries(luci_service PUBLIC logo_core) +target_link_libraries(luci_service PRIVATE luci_log) +target_link_libraries(luci_service PRIVATE nncc_common) +target_link_libraries(luci_service PRIVATE oops) +install(TARGETS luci_service DESTINATION lib) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(luci_service_test ${TESTS}) +target_include_directories(luci_service_test PRIVATE src) +target_link_libraries(luci_service_test luci_service) +target_link_libraries(luci_service_test oops) diff --git a/compiler/luci/service/README.md b/compiler/luci/service/README.md new file mode 100644 index 000000000..ac3583145 --- /dev/null +++ b/compiler/luci/service/README.md @@ -0,0 +1,3 @@ +# luci-service + +_luci-service_ provides Circle Dialect Services diff --git a/compiler/luci/service/include/luci/Service/CircleShapeInference.h b/compiler/luci/service/include/luci/Service/CircleShapeInference.h new file mode 100644 index 000000000..fb934c2cf --- /dev/null +++ b/compiler/luci/service/include/luci/Service/CircleShapeInference.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 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 __LUCI_CIRCLE_SHAPE_INFERENCE_H__ +#define __LUCI_CIRCLE_SHAPE_INFERENCE_H__ + +#include "ShapeDescription.h" + +#include <loco/IR/Nodes.h> + +namespace luci +{ + +/** + * @brief Get the shape of each node as a node annotation + * + * HOW TO USE + * + * ShapeInference::get(g->nodes()->at(..)); + */ +struct ShapeInference +{ + static ShapeDescription get(loco::Node *node); +}; + +} // namespace luci + +#endif // __LUCI_CIRCLE_SHAPE_INFERENCE_H__ diff --git a/compiler/luci/service/include/luci/Service/CircleShapeInferenceRule.h b/compiler/luci/service/include/luci/Service/CircleShapeInferenceRule.h new file mode 100644 index 000000000..3f63c9633 --- /dev/null +++ b/compiler/luci/service/include/luci/Service/CircleShapeInferenceRule.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020 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 __LUCI_CIRCLE_SHAPE_INFERENCE_RULE_H__ +#define __LUCI_CIRCLE_SHAPE_INFERENCE_RULE_H__ + +#include <loco/Service/ShapeInference.h> + +namespace luci +{ + +struct CircleShapeInferenceRule final : public loco::ShapeInferenceRule +{ + bool recognize(const loco::Dialect *) const final; + bool infer(const loco::Node *, loco::NodeShape &) const final; +}; + +} // namespace luci + +#endif // __LUCI_CIRCLE_SHAPE_INFERENCE_RULE_H__ diff --git a/compiler/luci/service/include/luci/Service/CircleTypeInference.h b/compiler/luci/service/include/luci/Service/CircleTypeInference.h new file mode 100644 index 000000000..ea7a3c5ed --- /dev/null +++ b/compiler/luci/service/include/luci/Service/CircleTypeInference.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020 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 __LUCI_CIRCLE_TYPE_INFERENCE_H__ +#define __LUCI_CIRCLE_TYPE_INFERENCE_H__ + +#include <loco/IR/Nodes.h> + +#include <mio/circle/schema_generated.h> + +namespace luci +{ + +/** + * @brief Get the type of each node as NodeAnnotation + * + * HOW TO USE + * + * TypeInference::get(g->nodes()->at(0)); + * TypeInference::get(g->nodes()->at(...)); + */ +struct TypeInference +{ + static circle::TensorType get(loco::Node *node); +}; + +} // namespace luci + +#endif // __LUCI_CIRCLE_TYPE_INFERENCE_H__ diff --git a/compiler/luci/service/include/luci/Service/CircleTypeInferenceRule.h b/compiler/luci/service/include/luci/Service/CircleTypeInferenceRule.h new file mode 100644 index 000000000..3b21081ef --- /dev/null +++ b/compiler/luci/service/include/luci/Service/CircleTypeInferenceRule.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 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 __LUCI_CIRCLE_TYPE_INFERENCE_RULE_H__ +#define __LUCI_CIRCLE_TYPE_INFERENCE_RULE_H__ + +#include <loco/Service/TypeInference.h> + +namespace luci +{ + +/** + * @brief Type Inference Rule for CircleDialect + */ +struct CircleTypeInferenceRule final : public loco::TypeInferenceRule +{ + bool recognize(const loco::Dialect *) const final; + bool infer(const loco::Node *, loco::DataType &) const final; +}; + +} // namespace luci + +#endif // __LUCI_CIRCLE_TYPE_INFERENCE_RULE_H__ diff --git a/compiler/luci/service/include/luci/Service/ShapeDescription.h b/compiler/luci/service/include/luci/Service/ShapeDescription.h new file mode 100644 index 000000000..949cce535 --- /dev/null +++ b/compiler/luci/service/include/luci/Service/ShapeDescription.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020 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 __LUCI_SHAPE_DESCRIPTION_H__ +#define __LUCI_SHAPE_DESCRIPTION_H__ + +#include <loco/IR/PermutingCodec.h> +#include <loco/IR/NodeShape.h> + +#include <cstdint> +#include <vector> + +namespace luci +{ + +struct ShapeDescription +{ + std::vector<int32_t> _dims; + bool _rank_known; +}; + +// TODO remove these when CircleDialect is fully functioal +ShapeDescription to_shape_description(const loco::TensorShape &shape); +ShapeDescription to_shape_description(const loco::FeatureShape &shape); +ShapeDescription to_shape_description(const loco::FilterShape &shape); +ShapeDescription to_shape_description(const loco::BiasShape &shape); +ShapeDescription to_shape_description(const loco::MatrixShape &shape); +ShapeDescription to_shape_description(const loco::NodeShape &shape); + +template <typename Permutation> inline bool isNHWC(Permutation *perm); + +template <> inline bool isNHWC(loco::Permutation<loco::Domain::Feature> *perm) +{ + return perm->axis(loco::FeatureAxis::Count) == 0 && perm->axis(loco::FeatureAxis::Height) == 1 && + perm->axis(loco::FeatureAxis::Width) == 2 && perm->axis(loco::FeatureAxis::Depth) == 3; +} + +template <> inline bool isNHWC(loco::Permutation<loco::Domain::Filter> *perm) +{ + return perm->axis(loco::FilterAxis::Count) == 0 && perm->axis(loco::FilterAxis::Height) == 1 && + perm->axis(loco::FilterAxis::Width) == 2 && perm->axis(loco::FilterAxis::Depth) == 3; +} + +} // namespace luci + +#endif // __LUCI_SHAPE_DESCRIPTION_H__ diff --git a/compiler/luci/service/include/luci/Service/Validate.h b/compiler/luci/service/include/luci/Service/Validate.h new file mode 100644 index 000000000..4b80d1d16 --- /dev/null +++ b/compiler/luci/service/include/luci/Service/Validate.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020 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 __LUCI_SERVICE_VALIDATE_H__ +#define __LUCI_SERVICE_VALIDATE_H__ + +#include <loco.h> + +namespace luci +{ + +bool validate(loco::Graph *); + +} // namespace luci + +#endif // __LUCI_SERVICE_VALIDATE_H__ diff --git a/compiler/luci/service/src/Check.h b/compiler/luci/service/src/Check.h new file mode 100644 index 000000000..e05ec904a --- /dev/null +++ b/compiler/luci/service/src/Check.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 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 __CHECK_H__ +#define __CHECK_H__ + +#include <stdexcept> +#include <cassert> +#include <iostream> + +// TODO Add macro for Release version + +#define LUCI_ASSERT(condition, msg) \ + { \ + if (!(condition)) \ + { \ + std::cerr << "[assert failed] " << (msg) << ". " << std::endl; \ + assert((condition)); \ + } \ + } + +#endif // __CHECK_H__ diff --git a/compiler/luci/service/src/CircleShapeInference.cpp b/compiler/luci/service/src/CircleShapeInference.cpp new file mode 100644 index 000000000..fdcfa76bc --- /dev/null +++ b/compiler/luci/service/src/CircleShapeInference.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 "luci/Service/CircleShapeInference.h" +#include "luci/Service/ShapeDescription.h" + +#include <loco.h> +#include <loco/Service/ShapeInference.h> + +#include <cassert> + +namespace luci +{ + +ShapeDescription ShapeInference::get(loco::Node *node) +{ + // TODO Adjust indentation level + { + assert(loco::shape_known(node)); + return to_shape_description(loco::shape_get(node)); + } +} + +} // namespace luci diff --git a/compiler/luci/service/src/CircleShapeInferenceRule.cpp b/compiler/luci/service/src/CircleShapeInferenceRule.cpp new file mode 100644 index 000000000..c8e872b1e --- /dev/null +++ b/compiler/luci/service/src/CircleShapeInferenceRule.cpp @@ -0,0 +1,907 @@ +/* + * Copyright (c) 2020 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 "luci/Service/CircleShapeInferenceRule.h" +#include "Check.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/IR/CircleDialect.h> +#include <luci/IR/CircleNodeVisitor.h> +#include <luci/Log.h> + +#include <oops/InternalExn.h> + +#include <algorithm> +#include <cassert> +#include <stdexcept> + +namespace +{ + +// Call this for CircleAvgPool2D and CircleMaxPool2D only +template <class Pool2DType> loco::NodeShape infer_pool_2d_shape(const Pool2DType *node) +{ + LUCI_ASSERT(loco::shape_known(node->value()), "Shape must be known"); + + auto ifm_shape = loco::shape_get(node->value()).template as<loco::TensorShape>(); + assert(ifm_shape.rank() == 4); + + uint32_t input_height = ifm_shape.dim(1).value(); + uint32_t input_width = ifm_shape.dim(2).value(); + uint32_t stride_height = node->stride()->h(); + uint32_t stride_width = node->stride()->w(); + uint32_t window_height = node->filter()->h(); + uint32_t window_width = node->filter()->w(); + uint32_t dilation_height = 1; // dilation for CircleAvgPool2D and CircleMaxPool2D is 1 + uint32_t dilation_width = 1; + uint32_t effective_window_height = dilation_height * (window_height - 1) + 1; + uint32_t effective_window_width = dilation_width * (window_width - 1) + 1; + + uint32_t output_height = 0; + uint32_t output_width = 0; + + if (node->padding() == luci::Padding::VALID) + { + output_height = (input_height + stride_height - effective_window_height) / stride_height; + output_width = (input_width + stride_width - effective_window_width) / stride_width; + } + else if (node->padding() == luci::Padding::SAME) + { + output_height = (input_height + stride_height - 1) / stride_height; + output_width = (input_width + stride_width - 1) / stride_width; + } + else + LUCI_ASSERT(false, "Wrong padding type"); + + loco::TensorShape ofm_shape; + ofm_shape.rank(4); + ofm_shape.dim(0) = ifm_shape.dim(0); + ofm_shape.dim(1) = output_height; + ofm_shape.dim(2) = output_width; + ofm_shape.dim(3) = ifm_shape.dim(3); + + return loco::NodeShape{ofm_shape}; +} + +/** + * @brief Create a higher-rank TensorShape following NumPy broadcasting semantics + * + * HOW TO USE: + * + * auto expanded_tensor_shape = expand(tensor_shape).to(N); + */ +class TensorShapeExpander +{ +public: + TensorShapeExpander(const loco::TensorShape &shape) : _shape{shape} + { + // DO NOTHING + } + +public: + loco::TensorShape to(uint32_t output_rank) + { + auto const &input_shape = _shape; + uint32_t const input_rank = input_shape.rank(); + + assert(input_rank <= output_rank && "Cannot shrink rank"); + uint32_t const axis_shift = output_rank - input_rank; + + loco::TensorShape output_shape; + + output_shape.rank(output_rank); + for (uint32_t axis = 0; axis < output_rank; ++axis) + { + output_shape.dim(axis) = (axis < axis_shift) ? 1 : input_shape.dim(axis - axis_shift); + } + + return output_shape; + } + +private: + const loco::TensorShape _shape; +}; + +/** + * @breif Expand shape x and y to same rank by align right and filling with 1 + */ +void expand_rank(loco::TensorShape &x, loco::TensorShape &y) +{ + auto x_rank = x.rank(); + auto y_rank = y.rank(); + + if (x_rank == y_rank) + return; + + TensorShapeExpander x_exp(x); + TensorShapeExpander y_exp(y); + + auto xy_rank = std::max(x_rank, y_rank); + + x = x_rank > y_rank ? x : x_exp.to(xy_rank); + y = y_rank > x_rank ? y : y_exp.to(xy_rank); +} + +/** + * @breif Returns shape of expanded dimension of input x and y having same rank + */ +loco::TensorShape expand_dimension(const loco::TensorShape &x, const loco::TensorShape &y) +{ + assert(x.rank() == y.rank()); + + auto rank = x.rank(); + + loco::TensorShape output_shape; + + output_shape.rank(rank); + for (uint32_t axis = 0; axis < rank; ++axis) + { + assert(x.dim(axis).known() && y.dim(axis).known()); + + auto x_dim = x.dim(axis).value(); + auto y_dim = y.dim(axis).value(); + + // each dimension of x and y should be same or one must be 1 if different + if (!((x_dim == y_dim) || (x_dim == 1 || y_dim == 1))) + INTERNAL_EXN("Cannot produce expand_dimension of two shapes"); + + output_shape.dim(axis) = std::max(x_dim, y_dim); + } + + return output_shape; +} + +loco::TensorShape broadcast_shape(const loco::TensorShape &x, const loco::TensorShape &y) +{ + auto x_match = x; + auto y_match = y; + + expand_rank(x_match, y_match); + + auto output_shape = expand_dimension(x_match, y_match); + + return output_shape; +} + +/** + * @brief Class to infer the shape of CircleNode + * + * @note All CircleNode's inputs and outputs are always loco::Domain::Tensor + */ +class ShapeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::NodeShape> +{ +public: + loco::NodeShape visit(const luci::CircleAbs *node) final + { + auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>(); + return loco::NodeShape{x_shape}; + } + + loco::NodeShape visit(const luci::CircleAdd *node) final + { + auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>(); + auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>(); + + auto output_shape = broadcast_shape(x_shape, y_shape); + + return loco::NodeShape{output_shape}; + } + + loco::NodeShape visit(const luci::CircleArgMax *node) final + { + auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto dimension_shape = loco::shape_get(node->dimension()).as<loco::TensorShape>(); + + int64_t select_axis = 0; + { + LUCI_ASSERT(node->dimension(), "2nd input dimension() should not be nullptr"); + + // Only support node's shape() is CircleConst with S32/S64 + // Support S32 for now. + auto const_shape_node = dynamic_cast<luci::CircleConst *>(node->dimension()); + LUCI_ASSERT(const_shape_node, "Only support CircleConst for shape of CircleArgMax"); + LUCI_ASSERT(const_shape_node->dtype() == loco::DataType::S32, + "Only support int32 CircleConst for CircleArgMax"); + + if (const_shape_node->rank() > 1) + INTERNAL_EXN_V("Only support rank 0/1 CircleConst", + oops::to_uint32(const_shape_node->rank())); + + select_axis = const_shape_node->scalar<loco::DataType::S32>(); + } + assert(select_axis < input_shape.rank()); + assert(select_axis >= 0); // TODO support minus of this breaks + + // NOTE select_axis is removed + loco::TensorShape shape_output; + uint32_t rank = input_shape.rank(); + uint32_t shrink = static_cast<uint32_t>(select_axis); + assert(rank > 0); + shape_output.rank(rank - 1); + for (uint32_t r = 0, d = 0; r < rank; ++r) + { + if (r == shrink) + continue; + shape_output.dim(d++) = input_shape.dim(r); + } + return loco::NodeShape{shape_output}; + } + + loco::NodeShape visit(const luci::CircleAveragePool2D *node) final + { + return infer_pool_2d_shape(node); + } + + loco::NodeShape visit(const luci::CircleBatchToSpaceND *node) final + { + const loco::DataType S32 = loco::DataType::S32; + + auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + // Support only input rank is 3 and 4 + assert(input_shape.rank() == 3 || input_shape.rank() == 4); + + // Only support block_shape() with S32 type CircleConst for now + auto const_block_shape = dynamic_cast<luci::CircleConst *>(node->block_shape()); + LUCI_ASSERT(const_block_shape, "Only support CircleConst for block_shape"); + LUCI_ASSERT(const_block_shape->dtype() == loco::DataType::S32, + "Only support int32 block_shape"); + + // Only support crops() with S32 type CircleConst for now + auto const_crops = dynamic_cast<luci::CircleConst *>(node->crops()); + LUCI_ASSERT(const_crops, "Only support CircleConst for crops"); + LUCI_ASSERT(const_crops->dtype() == loco::DataType::S32, "Only support int32 crops"); + + auto const_block_shape_shape = loco::shape_get(const_block_shape).as<loco::TensorShape>(); + auto const_crops_shape = loco::shape_get(const_crops).as<loco::TensorShape>(); + assert(const_block_shape_shape.rank() == 1); + assert(const_crops_shape.rank() == 2); + + int32_t input_spatial_dim = input_shape.rank() - 2; + assert(const_block_shape_shape.dim(0) == input_spatial_dim); + assert(const_crops_shape.dim(0) == input_spatial_dim); + assert(const_crops_shape.dim(1) == 2); + + loco::TensorShape shape_output; + + shape_output.rank(input_shape.rank()); + + int32_t output_batch_size = input_shape.dim(0).value(); + for (int32_t dim = 0; dim < input_spatial_dim; ++dim) + { + int dim_size = input_shape.dim(dim + 1).value() * const_block_shape->at<S32>(dim); + dim_size -= const_crops->at<S32>(dim * 2); + dim_size -= const_crops->at<S32>(dim * 2 + 1); + shape_output.dim(dim + 1) = dim_size; + + assert(output_batch_size % const_block_shape->at<S32>(dim) == 0); + output_batch_size = output_batch_size / const_block_shape->at<S32>(dim); + } + shape_output.dim(0) = output_batch_size; + shape_output.dim(input_shape.rank() - 1) = input_shape.dim(input_shape.rank() - 1); + + return loco::NodeShape{shape_output}; + } + + loco::NodeShape visit(const luci::CircleConcatenation *node) final + { + // TODO Support when CircleConcatenation has 0 input + assert(node->numValues() > 0); + + auto first_shape = loco::shape_get(node->values(0)).as<loco::TensorShape>(); + auto axis = node->axis(); + if (axis < 0) + axis += first_shape.rank(); + + assert(0 <= axis); + assert(first_shape.rank() > static_cast<uint32_t>(axis)); + + loco::TensorShape output_shape; + + output_shape.rank(first_shape.rank()); + for (uint32_t i = 0; i < output_shape.rank(); ++i) + output_shape.dim(i) = first_shape.dim(i); + + for (uint32_t i = 1; i < node->numValues(); ++i) + { + auto input_shape = loco::shape_get(node->values(i)).as<loco::TensorShape>(); + + for (uint32_t j = 0; j < output_shape.rank(); ++j) + { + if (j == static_cast<uint32_t>(axis)) + output_shape.dim(j) = output_shape.dim(j).value() + input_shape.dim(j).value(); + else + assert(output_shape.dim(j) == input_shape.dim(j)); + } + } + + return loco::NodeShape{output_shape}; + } + + loco::NodeShape visit(const luci::CircleConst *node) final + { + loco::TensorShape shape; + + shape.rank(node->rank()); + for (uint32_t axis = 0; axis < node->rank(); axis++) + shape.dim(axis) = node->dim(axis); + + return loco::NodeShape{shape}; + } + + loco::NodeShape visit(const luci::CircleConv2D *node) final + { + LOGGER(l); + + auto ifm_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); // in NHWC + auto ker_shape = loco::shape_get(node->filter()).as<loco::TensorShape>(); // in OHWI + + INFO(l) << "[luci] CircleConv2D ShapeInf ifm(" << ifm_shape.rank() << ") ker(" + << ker_shape.rank() << ")" << std::endl; + + assert(ifm_shape.rank() == 4); + assert(ker_shape.rank() == 4); + assert(ifm_shape.dim(3) == ker_shape.dim(3)); + + uint32_t input_height = ifm_shape.dim(1).value(); + uint32_t input_width = ifm_shape.dim(2).value(); + uint32_t stride_height = node->stride()->h(); + uint32_t stride_width = node->stride()->w(); + uint32_t ker_height = ker_shape.dim(1).value(); + uint32_t ker_width = ker_shape.dim(2).value(); + uint32_t dilation_height = 1; + uint32_t dilation_width = 1; + uint32_t effective_ker_height = dilation_height * (ker_height - 1) + 1; + uint32_t effective_ker_width = dilation_width * (ker_width - 1) + 1; + + uint32_t output_height = 0; + uint32_t output_width = 0; + + if (node->padding() == luci::Padding::VALID) + { + output_height = (input_height + stride_height - effective_ker_height) / stride_height; + output_width = (input_width + stride_width - effective_ker_width) / stride_width; + } + else if (node->padding() == luci::Padding::SAME) + { + output_height = (input_height + stride_height - 1) / stride_height; + output_width = (input_width + stride_width - 1) / stride_width; + } + else + LUCI_ASSERT(false, "Wrong padding type"); + + loco::TensorShape ofm_shape; + ofm_shape.rank(4); + ofm_shape.dim(0) = ifm_shape.dim(0); + ofm_shape.dim(1) = output_height; + ofm_shape.dim(2) = output_width; + ofm_shape.dim(3) = ker_shape.dim(0); + + return loco::NodeShape{ofm_shape}; + } + + loco::NodeShape visit(const luci::CircleCos *node) final + { + auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>(); + + return loco::NodeShape{x_shape}; + } + + loco::NodeShape visit(const luci::CircleDepthwiseConv2D *node) final + { + auto ifm_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); // in NHWC + auto ker_shape = loco::shape_get(node->filter()).as<loco::TensorShape>(); // in 1 H W CM + + assert(ifm_shape.rank() == 4); + assert(ker_shape.rank() == 4); + assert(ker_shape.dim(0).value() == 1); + + uint32_t input_height = ifm_shape.dim(1).value(); + uint32_t input_width = ifm_shape.dim(2).value(); + uint32_t stride_height = node->stride()->h(); + uint32_t stride_width = node->stride()->w(); + uint32_t ker_height = ker_shape.dim(1).value(); + uint32_t ker_width = ker_shape.dim(2).value(); + uint32_t dilation_height = 1; + uint32_t dilation_width = 1; + uint32_t effective_ker_height = dilation_height * (ker_height - 1) + 1; + uint32_t effective_ker_width = dilation_width * (ker_width - 1) + 1; + + uint32_t output_height = 0; + uint32_t output_width = 0; + + if (node->padding() == luci::Padding::VALID) + { + output_height = (input_height + stride_height - effective_ker_height) / stride_height; + output_width = (input_width + stride_width - effective_ker_width) / stride_width; + } + else if (node->padding() == luci::Padding::SAME) + { + output_height = (input_height + stride_height - 1) / stride_height; + output_width = (input_width + stride_width - 1) / stride_width; + } + else + LUCI_ASSERT(false, "Wrong padding type"); + + loco::TensorShape ofm_shape; + ofm_shape.rank(4); + ofm_shape.dim(0) = ifm_shape.dim(0); + ofm_shape.dim(1) = output_height; + ofm_shape.dim(2) = output_width; + ofm_shape.dim(3) = ker_shape.dim(3); + + return loco::NodeShape{ofm_shape}; + } + + loco::NodeShape visit(const luci::CircleDiv *node) final + { + auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>(); + auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>(); + + auto output_shape = broadcast_shape(x_shape, y_shape); + + return loco::NodeShape{output_shape}; + } + + loco::NodeShape visit(const luci::CircleEqual *node) final + { + const auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>(); + const auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>(); + loco::TensorShape output_shape = broadcast_shape(x_shape, y_shape); + return loco::NodeShape{output_shape}; + } + + loco::NodeShape visit(const luci::CircleExp *node) final + { + auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>(); + return loco::NodeShape{x_shape}; + } + + loco::NodeShape visit(const luci::CircleFullyConnected *node) final + { + auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto weights_shape = loco::shape_get(node->weights()).as<loco::TensorShape>(); + + // Checking shape capability for fully connected layer + // Input: a tensor of at least rank 2 [D1, D2, ... Dn] + // Weight: [# of units, K] + // Output: [D1 * D2 * ... * Dn / K, # of units] + LUCI_ASSERT(input_shape.rank() >= 2, "Input rank should be at least 2"); + LUCI_ASSERT(weights_shape.rank() == 2, "Incompatible weights rank for fully connected"); + + uint32_t input_size = 1; + for (uint32_t i = 0; i < input_shape.rank(); i++) + { + input_size = input_size * input_shape.dim(i).value(); + } + const uint32_t batch_size = input_size / weights_shape.dim(1).value(); + loco::TensorShape out_shape; + out_shape.rank(2); + out_shape.dim(0) = batch_size; + out_shape.dim(1) = weights_shape.dim(0); + + return loco::NodeShape{out_shape}; + } + + loco::NodeShape visit(const luci::CircleLogicalNot *node) final + { + const auto input_shape = loco::shape_get(node->x()).as<loco::TensorShape>(); + return loco::NodeShape{input_shape}; + } + + loco::NodeShape visit(const luci::CircleLogicalOr *node) final + { + const auto input_shape = loco::shape_get(node->x()).as<loco::TensorShape>(); + return loco::NodeShape{input_shape}; + } + + loco::NodeShape visit(const luci::CircleMaximum *node) final + { + auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>(); + auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>(); + + auto output_shape = broadcast_shape(x_shape, y_shape); + + return loco::NodeShape{output_shape}; + } + + loco::NodeShape visit(const luci::CircleMaxPool2D *node) final + { + return infer_pool_2d_shape(node); + } + + loco::NodeShape visit(const luci::CircleMean *node) final + { + const loco::DataType S32 = loco::DataType::S32; + + auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto reduction_indices = dynamic_cast<luci::CircleConst *>(node->reduction_indices()); + + { // Exceptions + // TODO support non-const case + LUCI_ASSERT(reduction_indices, "Only support constant reduction_indices"); + // TODO support other data type + LUCI_ASSERT(reduction_indices->dtype() == S32, "Only support int 32"); + } + + std::vector<int32_t> reduction_values; + + for (uint32_t i = 0; i < reduction_indices->size<S32>(); ++i) + { + int32_t axis = reduction_indices->at<S32>(i); + if (axis < 0) + axis += input_shape.rank(); + if (not(0 <= axis and axis < static_cast<int32_t>(input_shape.rank()))) + INTERNAL_EXN_V("Invalid reduction axis for MEAN", oops::to_uint32(axis)); + reduction_values.push_back(axis); + } + + loco::TensorShape output_shape; + + if (node->keep_dims()) + { + output_shape.rank(input_shape.rank()); + for (uint32_t i = 0; i < input_shape.rank(); ++i) + output_shape.dim(i) = input_shape.dim(i); + for (uint32_t i = 0; i < reduction_values.size(); ++i) + output_shape.dim(reduction_values.at(i)) = 1; + } + else + { + std::vector<bool> check_reduce(input_shape.rank(), false); + for (uint32_t i = 0; i < reduction_values.size(); ++i) + check_reduce.at(reduction_values.at(i)) = true; + + uint32_t reduce_cnt = 0; + for (uint32_t i = 0; i < check_reduce.size(); ++i) + if (check_reduce.at(i)) + ++reduce_cnt; + + output_shape.rank(input_shape.rank() - reduce_cnt); + for (uint32_t i = 0, j = 0; i < check_reduce.size(); ++i) + if (check_reduce.at(i) == false) + output_shape.dim(j++) = i; + } + + return loco::NodeShape{output_shape}; + } + + loco::NodeShape visit(const luci::CircleMul *node) final + { + auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>(); + auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>(); + + auto output_shape = broadcast_shape(x_shape, y_shape); + + return loco::NodeShape{output_shape}; + } + + loco::NodeShape visit(const luci::CirclePack *node) final + { + LUCI_ASSERT(node->values_count() > 0, "Only support one or more inputs"); + + auto first_shape = loco::shape_get(node->values(0)).as<loco::TensorShape>(); + // Make sure all inputs have the same shape. + for (uint32_t i = 1; i < node->values_count(); ++i) + { + auto in_shape = loco::shape_get(node->values(i)).as<loco::TensorShape>(); + LUCI_ASSERT(loco::NodeShape{first_shape} == loco::NodeShape{in_shape}, + "All inputs must have the same shape"); + } + + // Checking shape capability for pack layer + // Input: tensors [D1, D2, ... Dn] + // Axis: K + // Output: [D1, D2, ... , D_K-1, n, D_K+1, ... Dn] + auto axis = node->axis(); + if (axis < 0) + axis += first_shape.rank() + 1; + + LUCI_ASSERT(0 <= axis, "Axis is out of range"); + LUCI_ASSERT(static_cast<uint32_t>(axis) <= first_shape.rank(), "Axis is out of range"); + + loco::TensorShape output_shape; + output_shape.rank(first_shape.rank() + 1); + + uint32_t j = 0; + for (uint32_t i = 0; i < output_shape.rank(); ++i) + { + if (i == static_cast<uint32_t>(axis)) + { + output_shape.dim(i) = node->values_count(); + } + else + { + output_shape.dim(i) = first_shape.dim(j++); + } + } + + return loco::NodeShape{output_shape}; + } + + loco::NodeShape visit(const luci::CirclePad *node) final + { + const loco::DataType S32 = loco::DataType::S32; + + auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto paddings = dynamic_cast<luci::CircleConst *>(node->paddings()); + + // TODO support non-const case + LUCI_ASSERT(paddings, "Only support constant reduction_indices"); + // TODO support other data type + LUCI_ASSERT(paddings->dtype() == S32, "Only support int 32 for now"); + LUCI_ASSERT(paddings->rank() == 2, "paddings should be rank 2") + + int32_t n = paddings->dim(0).value(); + int32_t v = paddings->dim(1).value(); + + LUCI_ASSERT(v == 2, "paddings should be [n, 2]"); + LUCI_ASSERT(n == int32_t(input_shape.rank()), + "paddings [n, 2] should have same value of input rank"); + + loco::TensorShape output_shape; + + output_shape.rank(input_shape.rank()); + for (int32_t ni = 0; ni < n; ++ni) + { + int32_t idx = ni * 2; + int value = input_shape.dim(ni).value(); + value += paddings->at<S32>(idx + 0); // left + value += paddings->at<S32>(idx + 1); // right + output_shape.dim(ni) = value; + } + + return loco::NodeShape{output_shape}; + } + + loco::NodeShape visit(const luci::CircleRelu *node) final + { + auto input_shape = loco::shape_get(node->features()).as<loco::TensorShape>(); + + return loco::NodeShape{input_shape}; + } + + loco::NodeShape visit(const luci::CircleRelu6 *node) final + { + auto input_shape = loco::shape_get(node->features()).as<loco::TensorShape>(); + + return loco::NodeShape{input_shape}; + } + + /** + * @note CircleReshape has new shape info in two places: 2nd input and attribute. + * This shape inference forces both to exist, and match each other. + * When this condition satisfied, it return the inferred shape + * + * TODO Change this policy when not appropriate + */ + loco::NodeShape visit(const luci::CircleReshape *node) final + { + const loco::DataType S32 = loco::DataType::S32; + + loco::TensorShape shape_by_input; + { + LUCI_ASSERT(node->shape(), "2nd input shape() should not be nullptr"); + + // Only support node's shape() is CircleConst with S32 + // TODO support other node with other types + auto const_shape_node = dynamic_cast<luci::CircleConst *>(node->shape()); + LUCI_ASSERT(const_shape_node, "Only support CircleConst for shape of CircleReshape"); + LUCI_ASSERT(const_shape_node->dtype() == S32, "Only support int32 CircleConst"); + + if (const_shape_node->rank() != 1) + INTERNAL_EXN_V("Only support rank 1 CircleConst", + oops::to_uint32(const_shape_node->rank())); + + shape_by_input.rank(const_shape_node->dim(0).value()); + + for (uint32_t axis = 0; axis < shape_by_input.rank(); ++axis) + { + shape_by_input.dim(axis) = const_shape_node->at<S32>(axis); + } + } + + loco::TensorShape shape_by_attr; + { + shape_by_attr.rank(node->newShape()->rank()); + + for (uint32_t axis = 0; axis < shape_by_attr.rank(); ++axis) + { + shape_by_attr.dim(axis) = node->newShape()->dim(axis); + } + } + + LUCI_ASSERT(shape_by_input == shape_by_attr, + "Warning: Two new shape information mismatched for CircleReshape"); + + loco::TensorShape output_shape = shape_by_input; + + // One of the dimensions can have special value -1, meaning its actual value should be inferred. + const auto input_shape = loco::shape_get(node->tensor()).as<loco::TensorShape>(); + const uint32_t input_element_count = loco::element_count(&input_shape); + uint32_t output_element_count = 1; + uint32_t unknown_dim_index = UINT32_MAX; + for (uint32_t dim_index = 0; dim_index < output_shape.rank(); ++dim_index) + { + const uint32_t dim_value = output_shape.dim(dim_index).value(); + if (static_cast<int>(dim_value) == -1) + { + LUCI_ASSERT(unknown_dim_index == UINT32_MAX, "More than one unknown dimension"); + unknown_dim_index = dim_index; + } + else + { + output_element_count *= dim_value; + } + } + if (unknown_dim_index != UINT32_MAX) + { + output_shape.dim(unknown_dim_index) = input_element_count / output_element_count; + } + + return loco::NodeShape{output_shape}; + } + + loco::NodeShape visit(const luci::CircleRsqrt *node) final + { + auto input_shape = loco::shape_get(node->x()).as<loco::TensorShape>(); + + return loco::NodeShape{input_shape}; + } + + loco::NodeShape visit(const luci::CircleSoftmax *node) final + { + auto input_shape = loco::shape_get(node->logits()).as<loco::TensorShape>(); + + return loco::NodeShape{input_shape}; + } + + loco::NodeShape visit(const luci::CircleSqrt *node) final + { + auto input_shape = loco::shape_get(node->x()).as<loco::TensorShape>(); + + return loco::NodeShape{input_shape}; + } + + loco::NodeShape visit(const luci::CircleSquaredDifference *node) final + { + auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>(); + auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>(); + + auto output_shape = broadcast_shape(x_shape, y_shape); + + return loco::NodeShape{output_shape}; + } + + loco::NodeShape visit(const luci::CircleSub *node) final + { + auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>(); + auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>(); + + auto output_shape = broadcast_shape(x_shape, y_shape); + + return loco::NodeShape{output_shape}; + } + + // TODO CircleTanh + + /// @brief Returns output shape of transpose. Use loco::ConstGen and luci::CircleConst for ConstT. + template <class ConstT> + loco::TensorShape output_shape_of_transpose(loco::TensorShape input_shape, + const ConstT *perm_node) + { + loco::TensorShape output_shape; + output_shape.rank(input_shape.rank()); + + assert(perm_node->dtype() == loco::DataType::S32); + assert(input_shape.rank() == perm_node->template size<loco::DataType::S32>()); + + for (uint32_t out_axis = 0; out_axis < output_shape.rank(); out_axis++) + { + auto in_axis = perm_node->template at<loco::DataType::S32>(out_axis); + output_shape.dim(out_axis) = input_shape.dim(in_axis); + } + + return output_shape; + } + + loco::NodeShape visit(const luci::CircleTranspose *node) final + { + auto input_shape = loco::shape_get(node->a()).as<loco::TensorShape>(); + + auto canon_perm = dynamic_cast<loco::ConstGen *>(node->perm()); + auto circle_perm = dynamic_cast<luci::CircleConst *>(node->perm()); + + if (canon_perm) + { + return loco::NodeShape{output_shape_of_transpose(input_shape, canon_perm)}; + } + else if (circle_perm) + { + return loco::NodeShape{output_shape_of_transpose(input_shape, circle_perm)}; + } + else + INTERNAL_EXN("perm of CircleTranspose should be either ConstGen or CircleConst"); + } + + loco::NodeShape visit(const luci::CircleTransposeConv *node) final + { + // TransposeConv's output shape is written in its 'inputSizes' argument + auto input_sizes_const = dynamic_cast<luci::CircleConst *>(node->inputSizes()); + LUCI_ASSERT(input_sizes_const, + "Only support when CircleTransposeConv's inputSizes is CircleConst") + LUCI_ASSERT(input_sizes_const->dtype() == loco::DataType::S32, "Only support S32 dtype") + LUCI_ASSERT(input_sizes_const->rank() == 1 && input_sizes_const->dim(0).value() == 4, + "Only support rank 1 with 4 entries") + + loco::TensorShape shape; + + shape.rank(4); + for (uint32_t axis = 0; axis < 4; ++axis) + shape.dim(axis) = input_sizes_const->at<loco::DataType::S32>(axis); + + return loco::NodeShape{shape}; + } + + // Circle Only + loco::NodeShape visit(const luci::CircleInstanceNorm *node) final + { + auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + + return loco::NodeShape{input_shape}; + } + + // Virtual + loco::NodeShape visit(const luci::CircleInput *node) final + { + loco::TensorShape shape; + + shape.rank(node->rank()); + for (uint32_t axis = 0; axis < node->rank(); axis++) + shape.dim(axis) = node->dim(axis); + + return loco::NodeShape{shape}; + } + + loco::NodeShape visit(const luci::CircleOutput *node) final + { + auto from_shape = loco::shape_get(node->from()).as<loco::TensorShape>(); + + return loco::NodeShape{from_shape}; + } +}; + +} // namespace + +namespace luci +{ + +bool CircleShapeInferenceRule::recognize(const loco::Dialect *d) const +{ + return CircleDialect::get() == d; +} + +bool CircleShapeInferenceRule::infer(const loco::Node *node, loco::NodeShape &shape) const +{ + assert(node->dialect() == CircleDialect::get()); + assert(dynamic_cast<const CircleNode *>(node) != nullptr); + + ShapeInferenceAlgorithm alg; + shape = dynamic_cast<const CircleNode *>(node)->accept(&alg); + + return true; +} + +} // namespace luci diff --git a/compiler/luci/service/src/CircleShapeInferenceRule.test.cpp b/compiler/luci/service/src/CircleShapeInferenceRule.test.cpp new file mode 100644 index 000000000..0374251a0 --- /dev/null +++ b/compiler/luci/service/src/CircleShapeInferenceRule.test.cpp @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2020 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 "TestGraph.h" +#include "luci/Service/CircleShapeInferenceRule.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/IR/CircleDialect.h> + +#include <loco.h> +#include <loco/IR/CanonicalDialect.h> +#include <loco/Service/ShapeInference.h> +#include <loco/Service/CanonicalShapeInferenceRule.h> +#include <loco/Service/MultiDialectShapeInferenceRule.h> + +#include <gtest/gtest.h> + +#include <memory> + +namespace +{ + +bool shape_pass(loco::Graph *g) +{ + loco::CanonicalShapeInferenceRule canonical_rule; + luci::CircleShapeInferenceRule circle_rule; + loco::MultiDialectShapeInferenceRule rules; + + rules.bind(loco::CanonicalDialect::get(), &canonical_rule) + .bind(luci::CircleDialect::get(), &circle_rule); + + return loco::apply(&rules).to(g); +} + +} // namespace + +TEST(CircleShapeInferenceRuleTest, minimal_with_CircleRelu) +{ + // Create a simple network + luci::test::TestGraph graph; + auto tfl_node = graph.append<luci::CircleRelu>(graph.pull); + graph.complete(tfl_node); + + // set shape + { + graph.pull->rank(2); + graph.pull->dim(0) = 3; + graph.pull->dim(1) = 4; + } + + // pre-check + ASSERT_FALSE(loco::shape_known(tfl_node)); + + // shape inference + luci::CircleShapeInferenceRule tfl_rule; + loco::CanonicalShapeInferenceRule canonical_rule; + loco::MultiDialectShapeInferenceRule rules; + + rules.bind(loco::CanonicalDialect::get(), &canonical_rule) + .bind(luci::CircleDialect::get(), &tfl_rule); + + loco::apply(&rules).to(graph.g.get()); + + // Verify + { + ASSERT_TRUE(loco::shape_known(tfl_node)); + ASSERT_EQ(loco::shape_get(tfl_node).domain(), loco::Domain::Tensor); + + auto shape = loco::shape_get(tfl_node).as<loco::TensorShape>(); + ASSERT_EQ(shape.rank(), 2); + ASSERT_EQ(shape.dim(0), 3); + ASSERT_EQ(shape.dim(1), 4); + } +} + +// based on the case shown in +// https://www.corvil.com/kb/what-is-the-difference-between-same-and-valid-padding-in-tf-nn-max-pool-of-tensorflow +TEST(CircleShapeInferenceRuleTest, avgpool2d_valid) +{ + luci::test::TestGraph graph; + auto tfl_node = graph.append<luci::CircleAveragePool2D>(graph.pull); + graph.complete(); + + auto pull = graph.pull; + { + pull->shape({1, 4, 3, 1}); + } + // setting CircleAveragePool2D + { + tfl_node->filter()->h(2); + tfl_node->filter()->w(2); + tfl_node->stride()->h(2); + tfl_node->stride()->w(2); + tfl_node->fusedActivationFunction(luci::FusedActFunc::NONE); + tfl_node->padding(luci::Padding::VALID); + } + ASSERT_FALSE(loco::shape_known(tfl_node)); + + // shape inference + luci::CircleShapeInferenceRule tfl_rule; + loco::CanonicalShapeInferenceRule canonical_rule; + loco::MultiDialectShapeInferenceRule rules; + + rules.bind(loco::CanonicalDialect::get(), &canonical_rule) + .bind(luci::CircleDialect::get(), &tfl_rule); + + loco::apply(&rules).to(graph.g.get()); + + // Verify + { + ASSERT_TRUE(loco::shape_known(tfl_node)); + ASSERT_EQ(loco::shape_get(tfl_node).domain(), loco::Domain::Tensor); + + auto shape = loco::shape_get(tfl_node).as<loco::TensorShape>(); + ASSERT_EQ(shape.rank(), 4); + ASSERT_EQ(shape.dim(0).value(), 1); + ASSERT_EQ(shape.dim(1).value(), 2); + ASSERT_EQ(shape.dim(2).value(), 1); + ASSERT_EQ(shape.dim(3).value(), 1); + } +} + +TEST(CircleShapeInferenceRuleTest, avgpool2d_same) +{ + luci::test::TestGraph graph; + auto tfl_node = graph.append<luci::CircleAveragePool2D>(graph.pull); + graph.complete(); + + auto pull = graph.pull; + { + pull->shape({1, 4, 3, 1}); + } + + // setting CircleAveragePool2D + { + tfl_node->filter()->h(2); + tfl_node->filter()->w(2); + tfl_node->stride()->h(2); + tfl_node->stride()->w(2); + tfl_node->fusedActivationFunction(luci::FusedActFunc::NONE); + tfl_node->padding(luci::Padding::SAME); + } + + ASSERT_FALSE(loco::shape_known(tfl_node)); + + // shape inference + shape_pass(graph.g.get()); + + // Verify + { + ASSERT_TRUE(loco::shape_known(tfl_node)); + ASSERT_EQ(loco::shape_get(tfl_node).domain(), loco::Domain::Tensor); + + auto shape = loco::shape_get(tfl_node).as<loco::TensorShape>(); + ASSERT_EQ(shape.rank(), 4); + ASSERT_EQ(shape.dim(0).value(), 1); + ASSERT_EQ(shape.dim(1).value(), 2); + ASSERT_EQ(shape.dim(2).value(), 2); + ASSERT_EQ(shape.dim(3).value(), 1); + } +} + +/** + * @note Function to test: Shape inference of two different input shapes + * + * Rank expansion to higher input side + * x(2,1,5) + y(3,5) --> x(2,1,5) + y(1,3,5) + * Do output shape inference like numpy + * x(2,1,5) + y(1,3,5) --> output(2,3,5) + * For each axis, dim value should be same OR one of them should be 1 + */ +TEST(CircleShapeInferenceRuleTest, TFAdd_shapeinf_different) +{ + auto g = loco::make_graph(); + + auto x_node = g->nodes()->create<loco::Pull>(); + { + x_node->rank(3); + x_node->dim(0) = 2; + x_node->dim(1) = 1; + x_node->dim(2) = 5; + } + auto y_node = g->nodes()->create<loco::Pull>(); + { + y_node->rank(2); + y_node->dim(0) = 3; + y_node->dim(1) = 5; + } + auto tfl_node = g->nodes()->create<luci::CircleAdd>(); + { + tfl_node->x(x_node); + tfl_node->y(y_node); + } + auto push_node = g->nodes()->create<loco::Push>(); + { + push_node->from(tfl_node); + } + + auto x_input = g->inputs()->create(); + { + x_input->name("x"); + loco::link(x_input, x_node); + } + auto y_input = g->inputs()->create(); + { + y_input->name("y"); + loco::link(y_input, y_node); + } + auto output = g->outputs()->create(); + { + output->name("output"); + loco::link(output, push_node); + } + + // pre-check + ASSERT_FALSE(loco::shape_known(tfl_node)); + + // shape inference + while (shape_pass(g.get()) == true) + ; + + // Verify + { + ASSERT_TRUE(loco::shape_known(tfl_node)); + ASSERT_EQ(loco::shape_get(tfl_node).domain(), loco::Domain::Tensor); + + auto shape = loco::shape_get(tfl_node).as<loco::TensorShape>(); + ASSERT_EQ(shape.rank(), 3); + ASSERT_EQ(shape.dim(0), 2); + ASSERT_EQ(shape.dim(1), 3); + ASSERT_EQ(shape.dim(2), 5); + } +} + +TEST(CircleShapeInferenceRuleTest, CircleTranspose_simple) +{ + luci::test::ExampleGraph<luci::test::ExampleGraphType::CircleTranspose> g; + + g.pull->rank(3); + g.pull->dim(0) = 3; + g.pull->dim(1) = 8; + g.pull->dim(2) = 1; + + g.const_perm->dtype(loco::DataType::S32); + g.const_perm->rank(1); + g.const_perm->dim(0) = 3; + g.const_perm->size<loco::DataType::S32>(3); + g.const_perm->at<loco::DataType::S32>(0) = 1; + g.const_perm->at<loco::DataType::S32>(1) = 2; + g.const_perm->at<loco::DataType::S32>(2) = 0; + + // pre-check + ASSERT_FALSE(loco::shape_known(g.transpose_node)); + + // shape inference + while (shape_pass(g.graph()) == true) + ; + + // Verify + { + ASSERT_TRUE(loco::shape_known(g.transpose_node)); + + auto shape = loco::shape_get(g.transpose_node).as<loco::TensorShape>(); + ASSERT_EQ(shape.rank(), 3); + ASSERT_EQ(shape.dim(0), 8); + ASSERT_EQ(shape.dim(1), 1); + ASSERT_EQ(shape.dim(2), 3); + } +} diff --git a/compiler/luci/service/src/CircleTypeInference.cpp b/compiler/luci/service/src/CircleTypeInference.cpp new file mode 100644 index 000000000..669906159 --- /dev/null +++ b/compiler/luci/service/src/CircleTypeInference.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2020 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 "luci/Service/CircleTypeInference.h" +#include "luci/Service/CircleTypeInferenceRule.h" + +#include <luci/IR/CircleDialect.h> + +#include <loco/IR/CanonicalNode.h> +#include <loco/IR/CanonicalNodeVisitor.h> +#include <loco/IR/CanonicalDialect.h> +#include <loco/Service/TypeInference.h> +#include <mio/circle/schema_generated.h> +#include <oops/InternalExn.h> + +#include <memory> +#include <stdexcept> +#include <type_traits> + +namespace +{ + +circle::TensorType translateLocoTypeToCircle(loco::DataType dtype) +{ + switch (dtype) + { + case loco::DataType::U8: + return circle::TensorType_UINT8; + // case loco::DataType::U16: unsupported + // case loco::DataType::U32: unsupported + // case loco::DataType::U64: unsupported + case loco::DataType::S8: + return circle::TensorType_INT8; + case loco::DataType::S16: + return circle::TensorType_INT16; + case loco::DataType::S32: + return circle::TensorType_INT32; + case loco::DataType::S64: + return circle::TensorType_INT64; + case loco::DataType::FLOAT16: + return circle::TensorType_FLOAT16; + case loco::DataType::FLOAT32: + return circle::TensorType_FLOAT32; + // case loco::DataType::FLOAT64: unsupported + case loco::DataType::BOOL: + return circle::TensorType_BOOL; + default: + break; + } + + INTERNAL_EXN_V("Invalid loco dtype", oops::to_uint32(dtype)); +} + +} // namespace + +namespace luci +{ + +circle::TensorType TypeInference::get(loco::Node *node) +{ + assert(loco::dtype_known(node)); + return translateLocoTypeToCircle(loco::dtype_get(node)); +} + +} // namespace luci diff --git a/compiler/luci/service/src/CircleTypeInferenceRule.cpp b/compiler/luci/service/src/CircleTypeInferenceRule.cpp new file mode 100644 index 000000000..21a28c1b6 --- /dev/null +++ b/compiler/luci/service/src/CircleTypeInferenceRule.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2020 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 "luci/Service/CircleTypeInferenceRule.h" + +#include <luci/IR/CircleDialect.h> +#include <luci/IR/CircleNodeVisitor.h> +#include <luci/IR/CircleNodes.h> + +#include <cassert> + +namespace +{ + +struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::DataType> +{ + // TODO Given a tensor x of complex numbers, Abs operation returns a tensor of type float32 or + // float64. + loco::DataType visit(const luci::CircleAbs *node) final { return loco::dtype_get(node->x()); } + + loco::DataType visit(const luci::CircleAdd *node) final { return loco::dtype_get(node->x()); } + + loco::DataType visit(const luci::CircleArgMax *node) final { return node->output_type(); } + + loco::DataType visit(const luci::CircleAveragePool2D *node) final + { + return loco::dtype_get(node->value()); + } + + loco::DataType visit(const luci::CircleBatchToSpaceND *node) final + { + return loco::dtype_get(node->input()); + } + + loco::DataType visit(const luci::CircleConcatenation *node) final + { + // TODO Support when CircleConcatenation has 0 input + assert(node->numValues() > 0); + + for (uint32_t i = 1; i < node->numValues(); ++i) + assert(loco::dtype_get(node->values(i - 1)) == loco::dtype_get(node->values(i))); + + return loco::dtype_get(node->values(0)); + } + + loco::DataType visit(const luci::CircleConst *node) final { return node->dtype(); } + + loco::DataType visit(const luci::CircleConv2D *node) final + { + return loco::dtype_get(node->input()); + } + + loco::DataType visit(const luci::CircleCos *node) final { return loco::dtype_get(node->x()); } + + loco::DataType visit(const luci::CircleDepthwiseConv2D *node) final + { + return loco::dtype_get(node->input()); + } + + loco::DataType visit(const luci::CircleDiv *node) final { return loco::dtype_get(node->x()); } + + loco::DataType visit(const luci::CircleEqual *) final { return loco::DataType::BOOL; } + + loco::DataType visit(const luci::CircleExp *node) final { return loco::dtype_get(node->x()); } + + loco::DataType visit(const luci::CircleFullyConnected *node) final + { + return loco::dtype_get(node->input()); + } + + loco::DataType visit(const luci::CircleLogicalNot *node) final + { + return loco::dtype_get(node->x()); + } + + loco::DataType visit(const luci::CircleLogicalOr *node) final + { + return loco::dtype_get(node->x()); + } + + loco::DataType visit(const luci::CircleMaximum *node) final { return loco::dtype_get(node->x()); } + + loco::DataType visit(const luci::CircleMaxPool2D *node) final + { + return loco::dtype_get(node->value()); + } + + loco::DataType visit(const luci::CircleMean *node) final + { + return loco::dtype_get(node->input()); + } + + loco::DataType visit(const luci::CirclePack *node) final + { + // Only support CirclePack with one or more inputs + assert(node->values_count() > 0); + + auto first_value_type = loco::dtype_get(node->values(0)); + for (uint32_t i = 1; i < node->values_count(); ++i) + assert(first_value_type == loco::dtype_get(node->values(i))); + + return first_value_type; + } + + loco::DataType visit(const luci::CirclePad *node) final { return loco::dtype_get(node->input()); } + + loco::DataType visit(const luci::CircleMul *node) final { return loco::dtype_get(node->x()); } + + loco::DataType visit(const luci::CircleRelu *node) final + { + return loco::dtype_get(node->features()); + } + + loco::DataType visit(const luci::CircleRelu6 *node) final + { + return loco::dtype_get(node->features()); + } + + loco::DataType visit(const luci::CircleReshape *node) final + { + return loco::dtype_get(node->tensor()); + } + + loco::DataType visit(const luci::CircleRsqrt *node) final { return loco::dtype_get(node->x()); } + + loco::DataType visit(const luci::CircleSoftmax *node) final + { + return loco::dtype_get(node->logits()); + } + + loco::DataType visit(const luci::CircleSqrt *node) final { return loco::dtype_get(node->x()); } + + loco::DataType visit(const luci::CircleSquaredDifference *node) final + { + return loco::dtype_get(node->x()); + } + + loco::DataType visit(const luci::CircleSub *node) final { return loco::dtype_get(node->x()); } + + // TODO CircleTanh + + loco::DataType visit(const luci::CircleTranspose *node) final + { + return loco::dtype_get(node->a()); + } + + loco::DataType visit(const luci::CircleTransposeConv *node) final + { + return loco::dtype_get(node->outBackprop()); + } + + // Circle Only + loco::DataType visit(const luci::CircleInstanceNorm *node) final + { + return loco::dtype_get(node->input()); + } + + // Virtual + loco::DataType visit(const luci::CircleInput *node) final { return node->dtype(); } + + loco::DataType visit(const luci::CircleOutput *node) final + { + return loco::dtype_get(node->from()); + } +}; + +} // namespace + +namespace luci +{ + +bool CircleTypeInferenceRule::recognize(const loco::Dialect *d) const +{ + return CircleDialect::get() == d; +} + +bool CircleTypeInferenceRule::infer(const loco::Node *node, loco::DataType &dtype) const +{ + assert(node->dialect() == CircleDialect::get()); + + TypeInferenceAlgorithm alg; + + dtype = dynamic_cast<const CircleNode *>(node)->accept(&alg); + assert(dtype != loco::DataType::Unknown); + + return true; +} + +} // namespace luci diff --git a/compiler/luci/service/src/CircleTypeInferenceRule.test.cpp b/compiler/luci/service/src/CircleTypeInferenceRule.test.cpp new file mode 100644 index 000000000..29f45173e --- /dev/null +++ b/compiler/luci/service/src/CircleTypeInferenceRule.test.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2020 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 "TestGraph.h" +#include <luci/Service/CircleTypeInferenceRule.h> + +#include <luci/IR/CircleNodes.h> +#include <luci/IR/CircleDialect.h> + +#include <loco.h> +#include <loco/IR/CanonicalDialect.h> +#include <loco/Service/TypeInference.h> + +#include <gtest/gtest.h> + +#include <memory> + +TEST(CircleTypeInferenceRuleTest, minimal_with_CircleRelu) +{ + // Create a simple network + luci::test::TestGraph graph; + auto tfl_node = graph.append<luci::CircleRelu>(graph.pull); + graph.complete(tfl_node); + + graph.pull->dtype(loco::DataType::S32); + + // pre-check + ASSERT_FALSE(loco::dtype_known(tfl_node)); + + // type inference + luci::CircleTypeInferenceRule tfl_rule; + loco::CanonicalTypeInferenceRule canon_rule; + loco::MultiDialectTypeInferenceRule rules; + + rules.bind(loco::CanonicalDialect::get(), &canon_rule); + rules.bind(luci::CircleDialect::get(), &tfl_rule); + + loco::apply(&rules).to(graph.g.get()); + + // Verify + ASSERT_TRUE(loco::dtype_known(tfl_node)); + auto type = loco::dtype_get(tfl_node); + ASSERT_EQ(type, loco::DataType::S32); +} diff --git a/compiler/luci/service/src/GraphBlock.h b/compiler/luci/service/src/GraphBlock.h new file mode 100644 index 000000000..2a455888a --- /dev/null +++ b/compiler/luci/service/src/GraphBlock.h @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2020 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_BLOCK_H__ +#define __GRAPH_BLOCK_H__ + +#include <loco.h> +#include <loco/Service/ShapeInference.h> + +#include <oops/InternalExn.h> + +#include <functional> + +// TODO Change all Canonical nodes to Circle nodes + +namespace luci +{ + +/// @brief feature layout of TFlite/Circle file +enum class FeatureLayout +{ + NHWC, +}; + +/// @brief Creates a loco::FeatureEncode with T layout (NHWC for tflite) and add it to graph. +template <FeatureLayout T> loco::FeatureEncode *make_feature_encode(loco::Node *input_for_encode); + +/// @brief Creates a loco::FeatureDecode with T layout (NHWC for tflite) and add it to graph. +template <FeatureLayout T> loco::FeatureDecode *make_feature_decode(loco::Node *input_for_decode); + +enum class FilterLayout +{ + OHWI, // a.k.a., NHWC, Tensorflow Lite uses this layout for filter + HWIO, // a.k.a., HWCN, Tensorflow uses this layout for filter +}; + +/// @brief Create a loco::FilterEncode of given layout +template <FilterLayout T> loco::FilterEncode *make_filter_encode(loco::Node *input_for_encode); + +/// @brief Create a loco::FilterDecode of given layout +template <FilterLayout T> loco::FilterDecode *make_filter_decode(loco::Node *input_for_decode); + +enum class DepthwiseFilterLayout +{ + HWCM, +}; + +/// @brief Create a loco::DepthwiseFilterDecode of given layout +template <DepthwiseFilterLayout T> +loco::DepthwiseFilterDecode *make_dw_filter_decode(loco::Node *input_for_decode); + +enum class MatrixLayout +{ + HW, + WH +}; + +/// @brief Create a loco::MatrixEncode of given layout +template <MatrixLayout T> loco::MatrixEncode *make_matrix_encode(loco::Node *input_for_encode); + +/// @brief Create a loco::MatrixDecode of given layout +template <MatrixLayout T> loco::MatrixDecode *make_matrix_decode(loco::Node *input_for_decode); + +} // luci + +// +// DomainConverter +// + +/** + * Some canonical nodes can have input of various loco::Domain, e.g., loco::Domain::Tensor, + * loco::Domain::Feature, etc. However, TFL node accepts only loco::Domain::Tensor. + * So, When converting such canonical node to TFL node and input(s) of a canonical node are not + * loco::Domain::Tensor, additional nodes need to be inserted. + * + * The following two classes helps this insertion. + * + * For example, in case of loco::Relu conversion, + * + * Before: + * + * A (output: feature) -- loco::ReLU --- B (input:feature) + * + * After: + * + * A -- loco::FeatureDecode -- locoex::TFLRelu -- loco::FeatureEncode --- B + * + * loco::ReLU (dead node) + */ + +namespace luci +{ + +/** + * @brief Handles input(s) while converting a canonical node to TFL node(s). + * This class informs DomainConverter how to handle inputs of a specific canonical node. + */ +template <class CanonicalT, class TFLT> class InputHandler +{ +public: + /** + * @brief Assign origin's inputs to replacer's inputs. + * (This is called when origin belongs in Tensor domain.) + */ + virtual void handover(CanonicalT *origin, TFLT *replacer) = 0; + + /** + * @brief Returns the list of inputs that needs to have FeatureDecode as its input. + * (This is called when origin belongs in Feature domain.) + */ + virtual std::vector<loco::Node *> getInputsToConvert(CanonicalT *origin) = 0; + + /// @brief Set the inputs of replacer to new_inputs + virtual void set(TFLT *replacer, std::vector<loco::Node *> &new_inputs) = 0; + + /// @brief Set the inputs to nullptr + virtual void nullify(CanonicalT *origin) = 0; +}; + +/** + * @brief Class to handle domain conversion while converting a canonical node to TFL node(s) + */ +template <class CanonicalT, class TFLT> class DomainConverter +{ +public: + template <FeatureLayout FeatureLayoutT> + TFLT *convert(CanonicalT *origin, InputHandler<CanonicalT, TFLT> &input_handler); +}; + +/** + * @brief Performs domain conversion + * + * 1. if origin belong to loco::Domain::Tensor, and replace origin to a TFL node. + * 2. if origin belong to loco::Domain::Feature, insert loco::FeatureDecode for input(s) and + * insert loco::FeatureEncode for output. Then replace origin to a TFL node. + * + * @return new TFL node; nullptr if shape of origin cannot be known + */ +template <class CanonicalT, class TFLT> +template <FeatureLayout FeatureLayoutT> +TFLT *DomainConverter<CanonicalT, TFLT>::convert(CanonicalT *origin, + InputHandler<CanonicalT, TFLT> &input_handler) +{ + static_assert(FeatureLayoutT == FeatureLayout::NHWC, "Feature layout should be NHWC"); + + if (!loco::shape_known(origin)) + { + return nullptr; + } + + auto tfl_node = origin->graph()->nodes()->template create<TFLT>(); + + // when the input is Tensor, just replace canonical node to TFL node. + if (loco::shape_get(origin).domain() == loco::Domain::Tensor) + { + input_handler.handover(origin, tfl_node); + + loco::replace(origin).with(tfl_node); + input_handler.nullify(origin); + + return tfl_node; + } + else if (loco::shape_get(origin).domain() == loco::Domain::Feature) + { + std::vector<loco::Node *> feature_decodes; + + for (auto input : input_handler.getInputsToConvert(origin)) + { + auto dec = make_feature_decode<FeatureLayoutT>(input); + feature_decodes.emplace_back(dec); + } + + input_handler.set(tfl_node, feature_decodes); + + auto enc = make_feature_encode<FeatureLayoutT>(tfl_node); + + loco::replace(origin).with(enc); + input_handler.nullify(origin); + + return tfl_node; + } + else + INTERNAL_EXN_V("Unsupported loco::Domain", oops::to_uint32(loco::shape_get(origin).domain())); +} + +} // namespace luci + +#endif //__GRAPH_BLOCK_H__ diff --git a/compiler/luci/service/src/GraphBlock.test.cpp b/compiler/luci/service/src/GraphBlock.test.cpp new file mode 100644 index 000000000..1da8c18fa --- /dev/null +++ b/compiler/luci/service/src/GraphBlock.test.cpp @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2020 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 "GraphBlock.h" + +#include "Check.h" + +#include <loco.h> + +#include <memory> + +// TODO Change all Canonical nodes to Circle nodes + +namespace +{ + +template <luci::FeatureLayout T> loco::Permutation<loco::Domain::Feature> perm(); + +template <> loco::Permutation<loco::Domain::Feature> perm<luci::FeatureLayout::NHWC>() +{ + // Make NHWC permutation for encoder and decoder + loco::Permutation<loco::Domain::Feature> NHWC; + + NHWC.axis(loco::FeatureAxis::Count) = 0; + NHWC.axis(loco::FeatureAxis::Height) = 1; + NHWC.axis(loco::FeatureAxis::Width) = 2; + NHWC.axis(loco::FeatureAxis::Depth) = 3; + + return NHWC; +} + +template <luci::FilterLayout T> loco::Permutation<loco::Domain::Filter> perm(); + +template <> loco::Permutation<loco::Domain::Filter> perm<luci::FilterLayout::HWIO>() +{ + loco::Permutation<loco::Domain::Filter> HWIO; // a.k.a., HWCN + + HWIO.axis(loco::FilterAxis::Height) = 0; + HWIO.axis(loco::FilterAxis::Width) = 1; + HWIO.axis(loco::FilterAxis::Depth) = 2; + HWIO.axis(loco::FilterAxis::Count) = 3; + + return HWIO; +} + +template <> loco::Permutation<loco::Domain::Filter> perm<luci::FilterLayout::OHWI>() +{ + + // Make NHWC permutation for encoder and decoder + loco::Permutation<loco::Domain::Filter> OHWI; // a.k.a., NHWC + + OHWI.axis(loco::FilterAxis::Count) = 0; + OHWI.axis(loco::FilterAxis::Height) = 1; + OHWI.axis(loco::FilterAxis::Width) = 2; + OHWI.axis(loco::FilterAxis::Depth) = 3; + + return OHWI; +} + +template <luci::DepthwiseFilterLayout T> loco::Permutation<loco::Domain::DepthwiseFilter> perm(); + +template <> +loco::Permutation<loco::Domain::DepthwiseFilter> perm<luci::DepthwiseFilterLayout::HWCM>() +{ + loco::Permutation<loco::Domain::DepthwiseFilter> HWCM; + + HWCM.axis(loco::DepthwiseFilterAxis::Height) = 0; + HWCM.axis(loco::DepthwiseFilterAxis::Width) = 1; + HWCM.axis(loco::DepthwiseFilterAxis::Depth) = 2; + HWCM.axis(loco::DepthwiseFilterAxis::Multiplier) = 3; + + return HWCM; +} + +template <luci::MatrixLayout T> loco::Permutation<loco::Domain::Matrix> perm(); + +template <> loco::Permutation<loco::Domain::Matrix> perm<luci::MatrixLayout::HW>() +{ + loco::Permutation<loco::Domain::Matrix> HW; + + HW.axis(loco::MatrixAxis::Height) = 0; + HW.axis(loco::MatrixAxis::Width) = 1; + + return HW; +} + +template <> loco::Permutation<loco::Domain::Matrix> perm<luci::MatrixLayout::WH>() +{ + loco::Permutation<loco::Domain::Matrix> WH; + + WH.axis(loco::MatrixAxis::Height) = 1; + WH.axis(loco::MatrixAxis::Width) = 0; + + return WH; +} + +} // namespace + +namespace luci +{ + +template <FeatureLayout T> loco::FeatureEncode *make_feature_encode(loco::Node *input_for_encode) +{ + LUCI_ASSERT(input_for_encode != nullptr, "input should not be nullptr"); + loco::Graph *g = input_for_encode->graph(); + + auto encoder = std::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>(); + + encoder->perm(perm<T>()); + + auto enc = g->nodes()->create<loco::FeatureEncode>(); + enc->input(input_for_encode); + enc->encoder(std::move(encoder)); + + return enc; +} + +template <FeatureLayout T> loco::FeatureDecode *make_feature_decode(loco::Node *input_for_decode) +{ + LUCI_ASSERT(input_for_decode != nullptr, "input should not be nullptr"); + loco::Graph *g = input_for_decode->graph(); + + auto decoder = std::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>(); + + decoder->perm(perm<T>()); + + auto dec = g->nodes()->create<loco::FeatureDecode>(); + dec->input(input_for_decode); + dec->decoder(std::move(decoder)); + + return dec; +} + +template <FilterLayout T> loco::FilterEncode *make_filter_encode(loco::Node *input_for_encode) +{ + LUCI_ASSERT(input_for_encode != nullptr, "filter should not be nullptr"); + loco::Graph *g = input_for_encode->graph(); + + auto encoder = std::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>(); + + encoder->perm(perm<T>()); + + auto enc = g->nodes()->create<loco::FilterEncode>(); + enc->input(input_for_encode); + enc->encoder(std::move(encoder)); + + return enc; +} + +template <FilterLayout T> loco::FilterDecode *make_filter_decode(loco::Node *input_for_decode) +{ + LUCI_ASSERT(input_for_decode != nullptr, "filter should not be nullptr"); + loco::Graph *g = input_for_decode->graph(); + + auto decoder = std::make_unique<loco::PermutingDecoder<loco::Domain::Filter>>(); + + decoder->perm(perm<T>()); + + auto dec = g->nodes()->create<loco::FilterDecode>(); + dec->input(input_for_decode); + dec->decoder(std::move(decoder)); + + return dec; +} + +template <DepthwiseFilterLayout T> +loco::DepthwiseFilterDecode *make_dw_filter_decode(loco::Node *input_for_decode) +{ + LUCI_ASSERT(input_for_decode != nullptr, "filter should not be nullptr"); + loco::Graph *g = input_for_decode->graph(); + + auto decoder = std::make_unique<loco::PermutingDecoder<loco::Domain::DepthwiseFilter>>(); + + decoder->perm(perm<T>()); + + auto dec = g->nodes()->create<loco::DepthwiseFilterDecode>(); + dec->input(input_for_decode); + dec->decoder(std::move(decoder)); + + return dec; +} + +template <MatrixLayout T> loco::MatrixEncode *make_matrix_encode(loco::Node *input_for_encode) +{ + LUCI_ASSERT(input_for_encode != nullptr, "input should not be nullptr"); + loco::Graph *g = input_for_encode->graph(); + + auto encoder = std::make_unique<loco::PermutingEncoder<loco::Domain::Matrix>>(); + + encoder->perm(perm<T>()); + + auto enc = g->nodes()->create<loco::MatrixEncode>(); + enc->input(input_for_encode); + enc->encoder(std::move(encoder)); + + return enc; +} + +template <MatrixLayout T> loco::MatrixDecode *make_matrix_decode(loco::Node *input_for_decode) +{ + LUCI_ASSERT(input_for_decode != nullptr, "input should not be nullptr"); + loco::Graph *g = input_for_decode->graph(); + + auto decoder = std::make_unique<loco::PermutingDecoder<loco::Domain::Matrix>>(); + + decoder->perm(perm<T>()); + + auto dec = g->nodes()->create<loco::MatrixDecode>(); + dec->input(input_for_decode); + dec->decoder(std::move(decoder)); + + return dec; +} + +// template instantiation +template loco::FeatureEncode * +make_feature_encode<FeatureLayout::NHWC>(loco::Node *input_for_encode); + +template loco::FeatureDecode * +make_feature_decode<FeatureLayout::NHWC>(loco::Node *input_for_encode); + +template loco::FilterEncode *make_filter_encode<FilterLayout::HWIO>(loco::Node *input_for_encode); +template loco::FilterDecode *make_filter_decode<FilterLayout::OHWI>(loco::Node *input_for_decode); + +template loco::DepthwiseFilterDecode * +make_dw_filter_decode<DepthwiseFilterLayout::HWCM>(loco::Node *input_for_decode); + +template loco::MatrixEncode *make_matrix_encode<MatrixLayout::HW>(loco::Node *input_for_encode); +template loco::MatrixEncode *make_matrix_encode<MatrixLayout::WH>(loco::Node *input_for_encode); +template loco::MatrixDecode *make_matrix_decode<MatrixLayout::HW>(loco::Node *input_for_decode); +template loco::MatrixDecode *make_matrix_decode<MatrixLayout::WH>(loco::Node *input_for_decode); + +} // namespace luci diff --git a/compiler/luci/service/src/ShapeDescription.cpp b/compiler/luci/service/src/ShapeDescription.cpp new file mode 100644 index 000000000..cbc302f70 --- /dev/null +++ b/compiler/luci/service/src/ShapeDescription.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2020 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 "luci/Service/ShapeDescription.h" + +#include <oops/InternalExn.h> + +#include <cassert> + +namespace luci +{ + +ShapeDescription to_shape_description(const loco::TensorShape &shape) +{ + ShapeDescription res; + + res._rank_known = true; + + res._dims.resize(shape.rank()); + for (uint32_t axis = 0; axis < shape.rank(); ++axis) + { + // All the dimensions SHOULD be known + assert(shape.dim(axis).known()); + res._dims.at(axis) = shape.dim(axis).value(); + } + + return res; +} + +ShapeDescription to_shape_description(const loco::FeatureShape &shape) +{ + ShapeDescription res; + + res._rank_known = true; + + // T/F Lite encodes a feature map as a NHWC tensor + res._dims.resize(4); + res._dims.at(0) = shape.count().value(); + res._dims.at(1) = shape.height().value(); + res._dims.at(2) = shape.width().value(); + res._dims.at(3) = shape.depth().value(); + + return res; +} + +ShapeDescription to_shape_description(const loco::FilterShape &shape) +{ + ShapeDescription res; + + res._rank_known = true; + + // T/F Lite encodes a convolution filter as a NHWC tensor + res._dims.resize(4); + res._dims.at(0) = shape.count().value(); + res._dims.at(1) = shape.height().value(); + res._dims.at(2) = shape.width().value(); + res._dims.at(3) = shape.depth().value(); + + return res; +} + +ShapeDescription to_shape_description(const loco::DepthwiseFilterShape &shape) +{ + ShapeDescription res; + + res._rank_known = true; + + // T/F Lite encodes a depthwise convolution filter as a [1, H, W, C*M] tensor + res._dims.resize(4); + res._dims.at(0) = 1; + res._dims.at(1) = shape.height().value(); + res._dims.at(2) = shape.width().value(); + res._dims.at(3) = shape.depth().value() * shape.multiplier().value(); + + return res; +} + +ShapeDescription to_shape_description(const loco::BiasShape &shape) +{ + ShapeDescription res; + + res._rank_known = true; + + res._dims.resize(1); + res._dims.at(0) = shape.length().value(); + + return res; +} + +ShapeDescription to_shape_description(const loco::MatrixShape &shape) +{ + ShapeDescription res; + + res._rank_known = true; + + res._dims.resize(2); + res._dims.at(0) = shape.height().value(); + res._dims.at(1) = shape.width().value(); + + return res; +} + +ShapeDescription to_shape_description(const loco::NodeShape &shape) +{ + switch (shape.domain()) + { + case loco::Domain::Tensor: + return to_shape_description(shape.as<loco::TensorShape>()); + case loco::Domain::Feature: + return to_shape_description(shape.as<loco::FeatureShape>()); + case loco::Domain::Filter: + return to_shape_description(shape.as<loco::FilterShape>()); + case loco::Domain::DepthwiseFilter: + return to_shape_description(shape.as<loco::DepthwiseFilterShape>()); + case loco::Domain::Bias: + return to_shape_description(shape.as<loco::BiasShape>()); + case loco::Domain::Matrix: + return to_shape_description(shape.as<loco::MatrixShape>()); + default: + break; + } + + INTERNAL_EXN_V("Unsupported loco domain", oops::to_uint32(shape.domain())); +} + +} // namespace luci diff --git a/compiler/luci/service/src/TestGraph.h b/compiler/luci/service/src/TestGraph.h new file mode 100644 index 000000000..73562040f --- /dev/null +++ b/compiler/luci/service/src/TestGraph.h @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2020 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 __TEST_GRAPH_H__ +#define __TEST_GRAPH_H__ + +#include <luci/IR/CircleNodes.h> +#include "GraphBlock.h" + +#include <loco.h> + +#include <cassert> +#include <memory> + +// TODO Change all Canonical nodes to Circle nodes + +namespace luci +{ +namespace test +{ + +class TestGraph +{ +public: + std::unique_ptr<loco::Graph> g; + loco::Pull *pull; + loco::Push *push; + + TestGraph() // creates Pull and Push + { + g = loco::make_graph(); + + pull = g->nodes()->create<loco::Pull>(); + + push = g->nodes()->create<loco::Push>(); + + auto input = g->inputs()->create(); + { + input->name("input"); + loco::link(input, pull); + } + auto output = g->outputs()->create(); + { + output->name("output"); + loco::link(output, push); + } + + _next_input = pull; + } + + loco::Graph *graph() { return g.get(); } + + /// @brief Creates node with NO arg and appends it to graph + template <class T> T *append() + { + auto node = g->nodes()->create<T>(); + _next_input = node; + + return node; + } + + /// @brief Creates op T (arity=1) with arg1 as an input and appends it to graph + template <class T> T *append(loco::Node *arg1) + { + auto node = g->nodes()->create<T>(); + setInput(node, arg1); + _next_input = node; + + return node; + } + + /// @brief Creates op T (arity=2) with arg1, arg2 as inputs and appends it to graph + template <class T> T *append(loco::Node *arg1, loco::Node *arg2) + { + auto node = g->nodes()->create<T>(); + setInput(node, arg1, arg2); + _next_input = node; + + return node; + } + + /// @brief Creates op T (arity=3) with arg1, arg2, arg3 as inputs and appends it to graph + template <class T> T *append(loco::Node *arg1, loco::Node *arg2, loco::Node *arg3) + { + auto node = g->nodes()->create<T>(); + setInput(node, arg1, arg2, arg3); + _next_input = node; + + return node; + } + + // push will get the last appended node + void complete() { push->from(_next_input); } + + void complete(loco::Node *last_node) { push->from(last_node); } + +private: + // arity 1 + void setInput(loco::Node *node, loco::Node *) { assert(false && "NYI"); }; + + void setInput(loco::AvgPool2D *node, loco::Node *input) { node->ifm(input); } + void setInput(loco::BiasDecode *node, loco::Node *input) { node->input(input); }; + void setInput(loco::BiasEncode *node, loco::Node *input) { node->input(input); }; + void setInput(loco::FeatureDecode *node, loco::Node *input) { node->input(input); }; + void setInput(loco::FeatureEncode *node, loco::Node *input) { node->input(input); }; + void setInput(loco::MaxPool2D *node, loco::Node *input) { node->ifm(input); } + void setInput(loco::Push *node, loco::Node *input) { node->from(input); }; + void setInput(loco::ReLU *node, loco::Node *input) { node->input(input); }; + void setInput(loco::ReLU6 *node, loco::Node *input) { node->input(input); }; + void setInput(loco::Tanh *node, loco::Node *input) { node->input(input); }; + void setInput(loco::TensorTranspose *node, loco::Node *input) { node->input(input); }; + + void setInput(luci::CircleAveragePool2D *node, loco::Node *input) { node->value(input); }; + void setInput(luci::CircleMaxPool2D *node, loco::Node *input) { node->value(input); }; + void setInput(luci::CircleRelu *node, loco::Node *input) { node->features(input); }; + void setInput(luci::CircleRelu6 *node, loco::Node *input) { node->features(input); }; + + // arity 2 + void setInput(loco::Node *node, loco::Node *, loco::Node *) { assert(false && "NYI"); }; + + void setInput(loco::Conv2D *node, loco::Node *input, loco::Node *filter) + { + node->ifm(input); + node->ker(filter); + } + + void setInput(loco::EltwiseAdd *node, loco::Node *arg1, loco::Node *arg2) + { + node->lhs(arg1); + node->rhs(arg2); + }; + + void setInput(loco::FeatureBiasAdd *node, loco::Node *arg1, loco::Node *arg2) + { + node->value(arg1); + node->bias(arg2); + }; + + void setInput(luci::CircleAdd *node, loco::Node *arg1, loco::Node *arg2) + { + node->x(arg1); + node->y(arg2); + }; + + void setInput(luci::CircleMul *node, loco::Node *arg1, loco::Node *arg2) + { + node->x(arg1); + node->y(arg2); + }; + + void setInput(luci::CircleSub *node, loco::Node *arg1, loco::Node *arg2) + { + node->x(arg1); + node->y(arg2); + }; + + void setInput(luci::CircleTranspose *node, loco::Node *arg1, loco::Node *arg2) + { + node->a(arg1); + node->perm(arg2); + }; + + // arity 3 + void setInput(loco::Node *node, loco::Node *, loco::Node *, loco::Node *) + { + assert(false && "NYI"); + }; + + void setInput(luci::CircleConv2D *node, loco::Node *input, loco::Node *filter, loco::Node *bias) + { + node->input(input); + node->filter(filter); + node->bias(bias); + } + +private: + loco::Node *_next_input; +}; + +enum class ExampleGraphType +{ + FeatureBiasAdd, + ConstGen_ReLU, + FilterEncode_FilterDecode, + Transpose, + + CircleTranspose, +}; + +template <ExampleGraphType T> class ExampleGraph; + +/** + * @brief Class to create the following: + * + * Pull - FeatureEncoder - FeatureBiasAdd - FeatureDecode - Push + * | + * ConstGen - BiasEncode --+ + */ +template <> class ExampleGraph<ExampleGraphType::FeatureBiasAdd> : public TestGraph +{ +public: + loco::FeatureEncode *fea_enc = nullptr; + loco::ConstGen *constgen = nullptr; + loco::BiasEncode *bias_enc = nullptr; + loco::FeatureBiasAdd *fea_bias_add = nullptr; + loco::FeatureDecode *fea_dec = nullptr; + +public: + ExampleGraph() + { + fea_enc = luci::make_feature_encode<luci::FeatureLayout::NHWC>(pull); + constgen = append<loco::ConstGen>(); + bias_enc = append<loco::BiasEncode>(constgen); + fea_bias_add = append<loco::FeatureBiasAdd>(fea_enc, bias_enc); + fea_dec = luci::make_feature_decode<luci::FeatureLayout::NHWC>(fea_bias_add); + complete(fea_dec); + } +}; + +/** + * @brief Class to creates the following: + * + * ConstGen -- ReLU -- Push + */ +template <> class ExampleGraph<ExampleGraphType::ConstGen_ReLU> : public TestGraph +{ +public: + loco::ConstGen *constgen = nullptr; + loco::ReLU *relu = nullptr; + +public: + ExampleGraph() + { + constgen = append<loco::ConstGen>(); + relu = append<loco::ReLU>(constgen); + complete(relu); + } +}; + +/** + * @brief Class to creates the following: + * + * Pull -- Transpose -- Push + */ +template <> class ExampleGraph<ExampleGraphType::Transpose> : public TestGraph +{ +public: + loco::TensorTranspose *transpose = nullptr; + +public: + ExampleGraph() + { + transpose = append<loco::TensorTranspose>(pull); + complete(transpose); + } +}; + +/** + * @brief Class to creates the following: + * + * Pull -- FilterEncode -- FilterDecode -- Push + */ +template <> class ExampleGraph<ExampleGraphType::FilterEncode_FilterDecode> : public TestGraph +{ +public: + loco::FilterEncode *filterEncode = nullptr; + loco::FilterDecode *filterDecode = nullptr; + +public: + ExampleGraph() + { + filterEncode = luci::make_filter_encode<luci::FilterLayout::HWIO>(pull); // from Tensorflow + filterDecode = + luci::make_filter_decode<luci::FilterLayout::OHWI>(filterEncode); // to Tensorflow Lite + complete(filterDecode); + } +}; + +/** + * @brief Class to create the following: + * + * Pull -- CircleTranspose -- Push + */ +template <> class ExampleGraph<ExampleGraphType::CircleTranspose> : public TestGraph +{ +public: + loco::ConstGen *const_perm = nullptr; + luci::CircleTranspose *transpose_node = nullptr; + +public: + ExampleGraph() + { + const_perm = append<loco::ConstGen>(); + transpose_node = append<luci::CircleTranspose>(pull, const_perm); + complete(transpose_node); + } +}; + +} // namespace test +} // namespace luci + +#endif // __TEST_GRAPH_H__ diff --git a/compiler/luci/service/src/Validate.cpp b/compiler/luci/service/src/Validate.cpp new file mode 100644 index 000000000..65b82c2b4 --- /dev/null +++ b/compiler/luci/service/src/Validate.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2020 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 "luci/Service/Validate.h" + +#include <luci/IR/Nodes/CircleOutput.h> +#include <luci/Log.h> + +#include <loco/IR/NodeShape.h> +#include <loco/Service/ShapeInference.h> +#include <loco/Service/TypeInference.h> + +#include <cassert> +#include <vector> + +namespace +{ + +/** + * @brief returns a node that is CircleOutput with index is out_index in nodes + */ +luci::CircleOutput *find_node(std::vector<loco::Node *> nodes, loco::GraphOutputIndex out_index) +{ + for (auto node : nodes) + { + auto circle_output = dynamic_cast<luci::CircleOutput *>(node); + if (circle_output != nullptr) + { + if (circle_output->indexed() && circle_output->index() == out_index) + return circle_output; + } + } + return nullptr; +} + +bool validate_shape_type(loco::Graph *g) +{ + LOGGER(l); + + auto output_nodes = loco::output_nodes(g); + + auto count = g->outputs()->size(); + for (uint32_t out = 0; out < count; ++out) + { + auto graph_out = g->outputs()->at(out); + auto out_index = graph_out->index(); + + auto circle_output = find_node(output_nodes, out_index); + assert(circle_output != nullptr); + assert(circle_output->from() != nullptr); + auto circle_node = dynamic_cast<luci::CircleNode *>(circle_output->from()); + assert(circle_node != nullptr); + assert(loco::shape_known(circle_node)); + + // check if output node shape is same as graph output shape + auto co_shape = loco::shape_get(circle_node); + auto go_tensor_shape = graph_out->shape(); + assert(go_tensor_shape); + auto go_shape = loco::NodeShape(*go_tensor_shape); + if (!(co_shape == go_shape)) + { + INFO(l) << "Shape for #" << out_index << " not same " << std::endl; + return false; + } + + // check if data type match + assert(loco::dtype_known(circle_node)); + if (graph_out->dtype() != loco::dtype_get(circle_node)) + { + INFO(l) << "Type for #" << out_index << " not same " << std::endl; + return false; + } + } + + return true; +} + +} // namespace + +namespace luci +{ + +bool validate(loco::Graph *g) +{ + if (!loco::valid(g)) + return false; + + if (!validate_shape_type(g)) + return false; + + // TODO add more validation + + return true; +} + +} // namespace luci diff --git a/compiler/luci/tester/CMakeLists.txt b/compiler/luci/tester/CMakeLists.txt new file mode 100644 index 000000000..bcb47183e --- /dev/null +++ b/compiler/luci/tester/CMakeLists.txt @@ -0,0 +1,22 @@ +set(SRCS_READ_TESTER + src/ReadTester.cpp + src/Model.cpp + ) + +add_executable(luci_readtester "${SRCS_READ_TESTER}") +target_link_libraries(luci_readtester PRIVATE luci_import) +target_link_libraries(luci_readtester PRIVATE luci_service) +target_link_libraries(luci_readtester PRIVATE luci_pass) +target_link_libraries(luci_readtester PRIVATE oops) + +set(SRCS_WRITE_TESTER + src/WriteTester.cpp + src/Model.cpp + ) + +add_executable(luci_writetester "${SRCS_WRITE_TESTER}") +target_link_libraries(luci_writetester PRIVATE luci_import) +target_link_libraries(luci_writetester PRIVATE luci_service) +target_link_libraries(luci_writetester PRIVATE luci_pass) +target_link_libraries(luci_writetester PRIVATE luci_export) +target_link_libraries(luci_writetester PRIVATE oops) diff --git a/compiler/luci/tester/src/Model.cpp b/compiler/luci/tester/src/Model.cpp new file mode 100644 index 000000000..b02c19161 --- /dev/null +++ b/compiler/luci/tester/src/Model.cpp @@ -0,0 +1,62 @@ +#include "Model.h" + +#include <fstream> +#include <vector> + +#include <fcntl.h> +#include <unistd.h> +#include <sys/stat.h> + +namespace +{ + +class FileModel final : public luci::Model +{ +public: + explicit FileModel(const std::string &filename) : _filename(filename) {} + +public: + FileModel(const FileModel &) = delete; + FileModel(FileModel &&) = delete; + +public: + const ::circle::Model *model(void) override + { + std::ifstream file(_filename, std::ios::binary | std::ios::in); + if (!file.good()) + return nullptr; + + file.unsetf(std::ios::skipws); + + std::streampos fileSize; + file.seekg(0, std::ios::end); + fileSize = file.tellg(); + file.seekg(0, std::ios::beg); + + // reserve capacity + _data.reserve(fileSize); + + // read the data + file.read(_data.data(), fileSize); + if (file.fail()) + return nullptr; + + return ::circle::GetModel(_data.data()); + } + +private: + const std::string _filename; + std::vector<char> _data; +}; + +} // namespace + +namespace luci +{ + +std::unique_ptr<Model> load_model(const std::string &path) +{ + return std::make_unique<FileModel>(path); +} + +} // namespace luci diff --git a/compiler/luci/tester/src/Model.h b/compiler/luci/tester/src/Model.h new file mode 100644 index 000000000..e40faf33e --- /dev/null +++ b/compiler/luci/tester/src/Model.h @@ -0,0 +1,27 @@ +#ifndef __TESTER_MODEL_H__ +#define __TESTER_MODEL_H__ + +#include <mio/circle/schema_generated.h> + +#include <memory> + +namespace luci +{ + +struct Model +{ + virtual ~Model() = default; + + virtual const ::circle::Model *model(void) = 0; +}; + +/** + * @brief Load Circle model (as a raw Model) from a given path + * + * @note May return a nullptr + */ +std::unique_ptr<Model> load_model(const std::string &path); + +} // namespace luci + +#endif // __TESTER_MODEL_H__ diff --git a/compiler/luci/tester/src/ReadTester.cpp b/compiler/luci/tester/src/ReadTester.cpp new file mode 100644 index 000000000..c105d6ce3 --- /dev/null +++ b/compiler/luci/tester/src/ReadTester.cpp @@ -0,0 +1,92 @@ +#include "Model.h" + +#include <luci/Importer.h> +#include <luci/Service/Validate.h> +#include <luci/Pass/ShapeInferencePass.h> +#include <luci/Pass/TypeInferencePass.h> + +#include <iostream> +#include <map> +#include <string> + +namespace +{ + +void show_help_message(const char *progname, std::ostream &os) +{ + os << "USAGE: " << progname << " circlefile" << std::endl << std::endl; +} + +void show_error_message(const char *progname, std::ostream &os, const std::string &msg) +{ + os << "ERROR: " << msg << std::endl; + os << std::endl; + + show_help_message(progname, os); +} + +} // namespace + +/* + * @brief ReadTest main + * + * Give one Circle file as an argument + * + * This will use luci_import to read the file and get loco graph + * In luci_import, LUCI_LOG environment will be checked and will + * dump graph to console if set. + * i.e. "LUCI_LOG=1 luci_readtester mymodel.circle" + */ +int main(int argc, char **argv) +{ + if (argc != 2) + { + show_error_message(argv[0], std::cerr, "Circle file is not specified"); + return 255; + } + + std::string input_path = argv[1]; + + std::cout << "[INFO] Circle is '" << input_path << "'" << std::endl; + + // Load model from the file + std::unique_ptr<luci::Model> model = luci::load_model(input_path); + if (model == nullptr) + { + std::cerr << "ERROR: Failed to load '" << input_path << "'" << std::endl; + return 255; + } + + const circle::Model *input_model = model->model(); + if (input_model == nullptr) + { + std::cerr << "ERROR: Failed to read '" << input_path << "'" << std::endl; + return 255; + } + + luci::Importer importer; + auto module = importer.importModule(input_model); + assert(module->size() > 0); + + for (size_t g = 0; g < module->size(); ++g) + { + auto graph = module->graph(g); + if (graph == nullptr) + return 255; + + { + luci::ShapeInferencePass pass; + while (pass.run(graph) == true) + ; + } + { + luci::TypeInferencePass pass; + while (pass.run(graph) == true) + ; + } + + if (!luci::validate(graph)) + return 255; + } + return 0; +} diff --git a/compiler/luci/tester/src/WriteTester.cpp b/compiler/luci/tester/src/WriteTester.cpp new file mode 100644 index 000000000..80019d1b1 --- /dev/null +++ b/compiler/luci/tester/src/WriteTester.cpp @@ -0,0 +1,142 @@ +#include "Model.h" + +#include <luci/Importer.h> +#include <luci/Pass/ShapeInferencePass.h> +#include <luci/Pass/TypeInferencePass.h> +#include <luci/Service/Validate.h> +#include <luci/CircleExporter.h> +#include <oops/InternalExn.h> + +#include <fstream> +#include <iostream> +#include <map> +#include <string> + +namespace +{ + +void show_help_message(const char *progname, std::ostream &os) +{ + os << "USAGE: " << progname << " circlefile_in circlefile_out" << std::endl << std::endl; +} + +void show_error_message(const char *progname, std::ostream &os, const std::string &msg) +{ + os << "ERROR: " << msg << std::endl; + os << std::endl; + + show_help_message(progname, os); +} + +struct CircleExpContract : public luci::CircleExporter::Contract +{ +public: + CircleExpContract(loco::Graph *graph, const std::string &filename) + : _graph(graph), _filepath(filename) + { + // NOTHING TO DO + } + CircleExpContract(luci::Module *module, const std::string &filename) + : _module(module), _filepath(filename) + { + // NOTHING TO DO + } + virtual ~CircleExpContract() = default; + +public: + loco::Graph *graph(void) const final { return _graph; } + + luci::Module *module(void) const final { return _module; } + +public: + bool store(const char *ptr, const size_t size) const final; + +private: + loco::Graph *_graph; + luci::Module *_module; + const std::string _filepath; +}; + +bool CircleExpContract::store(const char *ptr, const size_t size) const +{ + if (!ptr) + INTERNAL_EXN("Graph was not serialized by FlatBuffer for some reason"); + + std::ofstream fs(_filepath.c_str(), std::ofstream::binary); + fs.write(ptr, size); + + return fs.good(); +} + +} // namespace + +/* + * @brief WriteTester main + * + * Give two Circle file as an argument + * + * This will use luci_import to read the first file and get loco graph + * With the graph, this will use luci_export to write to the second file + * Like ReadTester, LUCI_LOG=1 environment variable is available to dump the graph + */ +int main(int argc, char **argv) +{ + if (argc != 3) + { + show_error_message(argv[0], std::cerr, "In/Out Circle file path is not specified"); + return 255; + } + + std::string input_path = argv[1]; + std::string output_path = argv[2]; + + std::cout << "[INFO] Circle from '" << input_path << "' to '" << output_path << "'" << std::endl; + + // Load model from the file + std::unique_ptr<luci::Model> model = luci::load_model(input_path); + if (model == nullptr) + { + std::cerr << "ERROR: Failed to load '" << input_path << "'" << std::endl; + return 255; + } + + const circle::Model *input_model = model->model(); + if (input_model == nullptr) + { + std::cerr << "ERROR: Failed to read '" << input_path << "'" << std::endl; + return 255; + } + + // Import from input Circle file + luci::Importer importer; + auto module = importer.importModule(input_model); + assert(module->size() > 0); + + for (size_t g = 0; g < module->size(); ++g) + { + auto graph = module->graph(g); + if (graph == nullptr) + return 255; + + { + luci::ShapeInferencePass pass; + while (pass.run(graph) == true) + ; + } + { + luci::TypeInferencePass pass; + while (pass.run(graph) == true) + ; + } + + if (!luci::validate(graph)) + return 255; + } + + // Export to output Circle file + luci::CircleExporter exporter; + + CircleExpContract contract(module.get(), output_path); + + return exporter.invoke(&contract) ? 0 : 255; +} diff --git a/compiler/luci/tests/.gitignore b/compiler/luci/tests/.gitignore new file mode 100644 index 000000000..8dbfa9012 --- /dev/null +++ b/compiler/luci/tests/.gitignore @@ -0,0 +1 @@ +/test.local.lst diff --git a/compiler/luci/tests/CMakeLists.txt b/compiler/luci/tests/CMakeLists.txt new file mode 100644 index 000000000..4e5639047 --- /dev/null +++ b/compiler/luci/tests/CMakeLists.txt @@ -0,0 +1,97 @@ +# TODO use local test.recipe files for small networks +file(GLOB RECIPES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*/test.recipe") + +foreach(RECIPE IN ITEMS ${RECIPES}) + get_filename_component(RECIPE_PREFIX ${RECIPE} DIRECTORY) + + set(RECIPE_SOURCE_FILE "${RECIPE_PREFIX}.recipe") + set(RECIPE_OUTPUT_FILE "${RECIPE_PREFIX}.tflite") + set(CIRCLE_OUTPUT_FILE "${RECIPE_PREFIX}.circle") + + # Copy .recipe + add_custom_command(OUTPUT "${RECIPE_SOURCE_FILE}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${CMAKE_CURRENT_SOURCE_DIR}/${RECIPE}" "${RECIPE_SOURCE_FILE}" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${RECIPE}" + COMMENT "Generating ${RECIPE_SOURCE_FILE}") + + # Generate .tflite + add_custom_command(OUTPUT "${RECIPE_OUTPUT_FILE}" + COMMAND tflchef-file "${RECIPE_SOURCE_FILE}" "${RECIPE_OUTPUT_FILE}" + DEPENDS tflchef-file "${RECIPE_SOURCE_FILE}" + COMMENT "Generating ${RECIPE_OUTPUT_FILE}") + + # Generate .circle + add_custom_command(OUTPUT "${CIRCLE_OUTPUT_FILE}" + COMMAND tflite2circle "${RECIPE_OUTPUT_FILE}" "${CIRCLE_OUTPUT_FILE}" + DEPENDS tflite2circle "${RECIPE_OUTPUT_FILE}" + COMMENT "Generating ${CIRCLE_OUTPUT_FILE}") + + list(APPEND TESTFILES "${CIRCLE_OUTPUT_FILE}") +endforeach(RECIPE) + +# Generate from res/TensorFlowLiteRecipes +nncc_find_resource(TensorFlowLiteRecipes) +set(TENSORFLOWLITERECIPES_DIR "${TensorFlowLiteRecipes_DIR}") + +file(GLOB RECIPES RELATIVE ${TENSORFLOWLITERECIPES_DIR} "${TENSORFLOWLITERECIPES_DIR}/*/test.recipe") + +foreach(RECIPE IN ITEMS ${RECIPES}) + get_filename_component(RECIPE_PREFIX ${RECIPE} DIRECTORY) + + set(RECIPE_SOURCE_FILE "${RECIPE_PREFIX}.recipe") + set(RECIPE_OUTPUT_FILE "${RECIPE_PREFIX}.tflite") + set(CIRCLE_OUTPUT_FILE "${RECIPE_PREFIX}.circle") + + # Copy .recipe + add_custom_command(OUTPUT "${RECIPE_SOURCE_FILE}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${TENSORFLOWLITERECIPES_DIR}/${RECIPE}" "${RECIPE_SOURCE_FILE}" + DEPENDS "${TENSORFLOWLITERECIPES_DIR}/${RECIPE}" + COMMENT "Generating ${RECIPE_SOURCE_FILE}") + + # Generate .tflite + add_custom_command(OUTPUT "${RECIPE_OUTPUT_FILE}" + COMMAND tflchef-file "${RECIPE_SOURCE_FILE}" "${RECIPE_OUTPUT_FILE}" + DEPENDS tflchef-file "${RECIPE_SOURCE_FILE}" + COMMENT "Generating ${RECIPE_OUTPUT_FILE}") + + # Generate .circle + add_custom_command(OUTPUT "${CIRCLE_OUTPUT_FILE}" + COMMAND tflite2circle "${RECIPE_OUTPUT_FILE}" "${CIRCLE_OUTPUT_FILE}" + DEPENDS tflite2circle "${RECIPE_OUTPUT_FILE}" + COMMENT "Generating ${CIRCLE_OUTPUT_FILE}") + + list(APPEND TESTFILES "${CIRCLE_OUTPUT_FILE}") +endforeach(RECIPE) + +# Add a dummy target to create a target-level dependency. +# TODO Find a way to create dependency between CTest tests (added below) and generated testfiles. +add_custom_target(luci_testfiles ALL DEPENDS ${TESTFILES}) + +macro(addread NAME) + list(APPEND DAILY_READ_TESTS ${NAME}) +endmacro(addread) + +macro(addwrite NAME) + list(APPEND DAILY_WRITE_TESTS ${NAME}) +endmacro(addwrite) + +# Read "test.lst" +include("test.lst") +# Read "test.local.lst" if exists +include("test.local.lst" OPTIONAL) + +add_test(NAME luci_unit_readtest + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/readverify.sh" + "${CMAKE_CURRENT_BINARY_DIR}" + "$<TARGET_FILE:luci_readtester>" + ${DAILY_READ_TESTS} +) + +add_test(NAME luci_unit_writetest + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/writeverify.sh" + "${CMAKE_CURRENT_BINARY_DIR}" + "$<TARGET_FILE:luci_writetester>" + ${DAILY_WRITE_TESTS} +) diff --git a/compiler/luci/tests/readverify.sh b/compiler/luci/tests/readverify.sh new file mode 100755 index 000000000..3403e9c19 --- /dev/null +++ b/compiler/luci/tests/readverify.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +# This script verifies the basic behavior of luci frontend +# +# HOW TO USE +# +# ./readverify.sh <path/to/luci_readtester> <TEST 1> <TEST 2> ... +VERIFY_SOURCE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +WORKDIR="$1"; shift +VERIFY_BINARY_PATH="$1"; shift + +TESTED=() +PASSED=() +FAILED=() + +for TESTCASE in "$@"; do + TESTED+=("${TESTCASE}") + + TESTCASE_FILE="${WORKDIR}/${TESTCASE}" + + PASSED_TAG="${TESTCASE_FILE}.passed" + rm -f "${PASSED_TAG}" + + cat > "${TESTCASE_FILE}.log" <( + exec 2>&1 + set -ex + + "${VERIFY_BINARY_PATH}" "${TESTCASE_FILE}.circle" + + if [[ $? -eq 0 ]]; then + touch "${PASSED_TAG}" + fi + ) + + if [[ -f "${PASSED_TAG}" ]]; then + PASSED+=("${TESTCASE}") + else + FAILED+=("${TESTCASE}") + fi +done + +if [[ ${#TESTED[@]} -ne ${#PASSED[@]} ]]; then + echo "FAILED" + for TEST in "${FAILED[@]}" + do + echo "- ${TEST}" + done + exit 255 +fi + +echo "PASSED" +exit 0 diff --git a/compiler/luci/tests/test.lst b/compiler/luci/tests/test.lst new file mode 100644 index 000000000..08cbd6b1a --- /dev/null +++ b/compiler/luci/tests/test.lst @@ -0,0 +1,91 @@ +addread(Add_000) +addread(Add_U8_000) +addread(ArgMax_000) +addread(ArgMax_001) +addread(ArgMax_002) +addread(ArgMax_003) +addread(ArgMax_U8_000) +addread(ArgMax_U8_001) +addread(ArgMax_U8_002) +addread(ArgMax_U8_003) +addread(BatchToSpaceND_000) +addread(Concatenation_000) +addread(Concatenation_U8_000) +addread(Conv2D_000) +addread(Conv2D_U8_000) +addread(Conv2D_002) +addread(Cos_000) +addread(DepthwiseConv2D_000) +addread(DepthwiseConv2D_U8_000) +addread(Div_000) +addread(Equal_000) +addread(Exp_000) +addread(FullyConnected_000) +addread(FullyConnected_001) +addread(FullyConnected_U8_000) +addread(LogicalNot_000) +addread(LogicalOr_000) +addread(MaxPool2D_000) +addread(MaxPool2D_U8_000) +addread(Mean_000) +addread(Mul_000) +addread(Mul_U8_000) +addread(Pack_000) +addread(Pack_U8_000) +addread(Pad_000) +addread(ReLU_000) +addread(Reshape_000) +addread(Reshape_001) +addread(Reshape_U8_000) +addread(Rsqrt_000) +addread(Softmax_000) +addread(Softmax_U8_000) +addread(Sub_000) +addread(Sub_U8_000) +addread(Transpose_000) + +addwrite(Add_000) +addwrite(Add_U8_000) +addwrite(ArgMax_000) +addwrite(ArgMax_001) +addwrite(ArgMax_002) +addwrite(ArgMax_003) +addwrite(ArgMax_U8_000) +addwrite(ArgMax_U8_001) +addwrite(ArgMax_U8_002) +addwrite(ArgMax_U8_003) +addwrite(BatchToSpaceND_000) +addwrite(Concatenation_000) +addwrite(Concatenation_U8_000) +addwrite(Conv2D_000) +addwrite(Conv2D_U8_000) +addwrite(Conv2D_002) +addwrite(Cos_000) +addwrite(DepthwiseConv2D_000) +addwrite(DepthwiseConv2D_U8_000) +addwrite(Div_000) +addwrite(Equal_000) +addwrite(Exp_000) +addwrite(FullyConnected_000) +addwrite(FullyConnected_001) +addwrite(FullyConnected_U8_000) +addwrite(LogicalNot_000) +addwrite(LogicalOr_000) +addwrite(MaxPool2D_000) +addwrite(MaxPool2D_U8_000) +addwrite(Mean_000) +addwrite(Mul_000) +addwrite(Mul_U8_000) +addwrite(Pack_000) +addwrite(Pack_U8_000) +addwrite(Pad_000) +addwrite(ReLU_000) +addwrite(Reshape_000) +addwrite(Reshape_001) +addwrite(Reshape_U8_000) +addwrite(Rsqrt_000) +addwrite(Softmax_000) +addwrite(Softmax_U8_000) +addwrite(Sub_000) +addwrite(Sub_U8_000) +addwrite(Transpose_000) diff --git a/compiler/luci/tests/writeverify.sh b/compiler/luci/tests/writeverify.sh new file mode 100755 index 000000000..6980bac44 --- /dev/null +++ b/compiler/luci/tests/writeverify.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +# This script verifies the basic behavior of luci frontend +# +# HOW TO USE +# +# ./writeverify.sh <path/to/luci_writetester> <TEST 1> <TEST 2> ... +VERIFY_SOURCE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +WORKDIR="$1"; shift +VERIFY_BINARY_PATH="$1"; shift + +TESTED=() +PASSED=() +FAILED=() + +for TESTCASE in "$@"; do + TESTED+=("${TESTCASE}") + + TESTCASE_FILE="${WORKDIR}/${TESTCASE}" + + PASSED_TAG="${TESTCASE_FILE}_w.passed" + rm -f "${PASSED_TAG}" + + cat > "${TESTCASE_FILE}_w.log" <( + exec 2>&1 + set -ex + + "${VERIFY_BINARY_PATH}" "${TESTCASE_FILE}.circle" "${TESTCASE_FILE}_w.circle" + + if [[ $? -eq 0 ]]; then + touch "${PASSED_TAG}" + fi + ) + + if [[ -f "${PASSED_TAG}" ]]; then + PASSED+=("${TESTCASE}") + else + FAILED+=("${TESTCASE}") + fi +done + +if [[ ${#TESTED[@]} -ne ${#PASSED[@]} ]]; then + echo "FAILED" + for TEST in "${FAILED[@]}" + do + echo "- ${TEST}" + done + exit 255 +fi + +echo "PASSED" +exit 0 |