diff options
author | Chunseok Lee <chunseok.lee@samsung.com> | 2020-07-30 11:32:26 +0900 |
---|---|---|
committer | Chunseok Lee <chunseok.lee@samsung.com> | 2020-07-30 11:32:26 +0900 |
commit | 05e0ec30a632339a8533082476f27bda31ccde16 (patch) | |
tree | 5f220ac83084fe133ffb08a6a17e99f9bb36ec1c /compiler/circlechef | |
parent | e2ef8438a24f7c56a0744eb579a6e293ee2fbf8e (diff) | |
download | nnfw-05e0ec30a632339a8533082476f27bda31ccde16.tar.gz nnfw-05e0ec30a632339a8533082476f27bda31ccde16.tar.bz2 nnfw-05e0ec30a632339a8533082476f27bda31ccde16.zip |
Imported Upstream version 1.7.0upstream/1.7.0
Diffstat (limited to 'compiler/circlechef')
54 files changed, 3276 insertions, 0 deletions
diff --git a/compiler/circlechef/CMakeLists.txt b/compiler/circlechef/CMakeLists.txt new file mode 100644 index 000000000..cba7d0a4e --- /dev/null +++ b/compiler/circlechef/CMakeLists.txt @@ -0,0 +1,21 @@ +nnas_find_package(Protobuf QUIET) + +if(NOT Protobuf_FOUND) + return() +endif(NOT Protobuf_FOUND) + +if(NOT TARGET mio_circle) + return() +endif(NOT TARGET mio_circle) + +# Recipe Parser +add_subdirectory(proto) +# Log +add_subdirectory(log) +# Core Library +add_subdirectory(core) +# Circle Library +add_subdirectory(circle) +# Tools +add_subdirectory(tools) +add_subdirectory(tests) diff --git a/compiler/circlechef/README.md b/compiler/circlechef/README.md new file mode 100644 index 000000000..1871a0660 --- /dev/null +++ b/compiler/circlechef/README.md @@ -0,0 +1,8 @@ +# circlechef + +## What is circlechef? + +Do you need a circle model for testing? Ask it to _circlechef_. +Given a recipe, _circlechef_ will cook a circle model for you. + +**NOTE** _circlechef_ covers only what _tflchef_ does not cover. This is to support ops that exist only in circle shema, and other things can be made using _tflchef_ and _tflite2circle_. diff --git a/compiler/circlechef/circle/CMakeLists.txt b/compiler/circlechef/circle/CMakeLists.txt new file mode 100644 index 000000000..75165ada3 --- /dev/null +++ b/compiler/circlechef/circle/CMakeLists.txt @@ -0,0 +1,9 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") + +add_library(circlechef_circle STATIC ${SOURCES}) +target_include_directories(circlechef_circle PUBLIC include) +target_include_directories(circlechef_circle PRIVATE src) +target_link_libraries(circlechef_circle circlechef_proto) +target_link_libraries(circlechef_circle mio_circle) +target_link_libraries(circlechef_circle stdex) +target_link_libraries(circlechef_circle cwrap) diff --git a/compiler/circlechef/circle/include/circlechef/RecipeChef.h b/compiler/circlechef/circle/include/circlechef/RecipeChef.h new file mode 100644 index 000000000..d3cafa282 --- /dev/null +++ b/compiler/circlechef/circle/include/circlechef/RecipeChef.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 __RECIPE_CHEF_H__ +#define __RECIPE_CHEF_H__ + +#include <mio/circle/schema_generated.h> +#include <circlechef.pb.h> + +#include <memory> +#include <string> + +namespace circlechef +{ + +/** + * @brief Create ModelRecipe from circle::Model + */ +std::unique_ptr<ModelRecipe> generate_recipe(const circle::Model *model); + +/** + * @brief Write ModelRecipe to file with given name + */ +bool write_recipe(const std::string &filename, std::unique_ptr<ModelRecipe> &recipe); + +} // namespace circlechef + +#endif // __RECIPE_CHEF_H__ diff --git a/compiler/circlechef/circle/src/CircleImport.cpp b/compiler/circlechef/circle/src/CircleImport.cpp new file mode 100644 index 000000000..e970fbce3 --- /dev/null +++ b/compiler/circlechef/circle/src/CircleImport.cpp @@ -0,0 +1,145 @@ +/* + * 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 "CircleImport.h" + +#include "Convert.h" + +#include <sstream> + +namespace circlechef +{ + +const char *kEmptyTensorName = "(noname)"; + +const char *tensor_type(const circle::Tensor *tensor) +{ + return circle::EnumNameTensorType(tensor->type()); +} + +const char *tensor_name(const circle::Tensor *tensor) +{ + auto name = tensor->name(); + if (name) + return name->c_str(); + return kEmptyTensorName; +} + +bool is_valid(const circle::OperatorCode *opcode) +{ + circle::BuiltinOperator code = opcode->builtin_code(); + return (circle::BuiltinOperator_MIN <= code && code <= circle::BuiltinOperator_MAX); +} + +bool is_custom(const circle::OperatorCode *opcode) +{ + circle::BuiltinOperator code = opcode->builtin_code(); + return (code == circle::BuiltinOperator_CUSTOM); +} + +CircleImport::CircleImport(const circle::Model *model) +{ + _subgraphs = model->subgraphs(); + _buffers = model->buffers(); + + auto opcodes = model->operator_codes(); + for (const ::circle::OperatorCode *opcode : *opcodes) + { + _op_codes.push_back(opcode); + } +} + +bool CircleImport::select_sub_graph(uint32_t sgindex) +{ + _tensors = nullptr; + _operators = nullptr; + _inputs.clear(); + _outputs.clear(); + + if (_subgraphs->Length() <= sgindex) + { + assert(false); + return false; + } + + const circle::SubGraph *subgraph = (*_subgraphs)[sgindex]; + + _tensors = subgraph->tensors(); + _operators = subgraph->operators(); + + _inputs = as_index_vector(subgraph->inputs()); + _outputs = as_index_vector(subgraph->outputs()); + + return true; +} + +circle::BuiltinOperator CircleImport::builtin_code(const circle::Operator *op) const +{ + uint32_t index = op->opcode_index(); + assert(index < _op_codes.size()); + const circle::OperatorCode *opcode = _op_codes.at(index); + + return opcode->builtin_code(); +} + +std::string CircleImport::opcode_name(const circle::Operator *op) const +{ + uint32_t index = op->opcode_index(); + assert(index < _op_codes.size()); + const circle::OperatorCode *opcode = _op_codes.at(index); + + if (!is_valid(opcode)) + { + std::ostringstream oss; + oss << "(invalid: " << index << ")"; + return oss.str(); + } + + if (is_custom(opcode)) + { + if (!opcode->custom_code()) + return "(invalid custom)"; + + return opcode->custom_code()->c_str(); + } + + circle::BuiltinOperator code = opcode->builtin_code(); + return EnumNameBuiltinOperator(code); +} + +size_t CircleImport::buffer_info(const circle::Tensor *tensor, const uint8_t **buff_data) +{ + *buff_data = nullptr; + + if (tensor->buffer() == 0) + return 0; + + if (auto *buffer = (*_buffers)[tensor->buffer()]) + { + if (auto *array = buffer->data()) + { + if (size_t size = array->size()) + { + *buff_data = reinterpret_cast<const uint8_t *>(array->data()); + return size; + } + } + } + + return 0; +} + +} // namespace circlechef diff --git a/compiler/circlechef/circle/src/CircleImport.h b/compiler/circlechef/circle/src/CircleImport.h new file mode 100644 index 000000000..a8ef3ee44 --- /dev/null +++ b/compiler/circlechef/circle/src/CircleImport.h @@ -0,0 +1,140 @@ +/* + * 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_IMPORT_H__ +#define __CIRCLE_IMPORT_H__ + +#include <mio/circle/schema_generated.h> + +#include <circlechef.pb.h> + +#include <map> +#include <vector> + +namespace circlechef +{ + +using CircleSubGraphs_t = flatbuffers::Vector<flatbuffers::Offset<circle::SubGraph>>; +using CircleTensors_t = flatbuffers::Vector<flatbuffers::Offset<circle::Tensor>>; +using CircleBuffers_t = flatbuffers::Vector<flatbuffers::Offset<circle::Buffer>>; +using CircleOperators_t = flatbuffers::Vector<flatbuffers::Offset<circle::Operator>>; + +const char *tensor_type(const circle::Tensor *tensor); +const char *tensor_name(const circle::Tensor *tensor); +bool is_valid(const circle::OperatorCode *opcode); +bool is_custom(const circle::OperatorCode *opcode); + +/** + * @brief Loads TF lite file and provides helpers to access attributes + */ +class CircleImport +{ +public: + CircleImport(const circle::Model *model); + + CircleImport() = delete; + +public: + bool select_sub_graph(uint32_t subgraph); + +public: + const CircleBuffers_t *buffers() { return _buffers; } + const CircleTensors_t *tensors() { return _tensors; } + const CircleOperators_t *operators() { return _operators; } + const std::vector<int32_t> &inputs() const { return _inputs; } + const std::vector<int32_t> &outputs() const { return _outputs; } + + uint32_t num_subgraph() const { return _subgraphs->Length(); } + + circle::BuiltinOperator builtin_code(const circle::Operator *op) const; + std::string opcode_name(const circle::Operator *op) const; + size_t buffer_info(const circle::Tensor *tensor, const uint8_t **buff_data); + + /** + * @brief This will record the tensor by index, if it needs filler option, + * such as kernel, bias. + */ + void set_tensor_filler(uint32_t tensor_index) { _tensor_filler[tensor_index] = true; } + + /** + * @brief This will store int32 filler values such as reshape information for the tensor + */ + void set_tensor_filler(uint32_t tensor_index, std::vector<int32_t> &expvalues) + { + _tensor_filler_vint32[tensor_index] = expvalues; + } + + void set_tensor_filler(uint32_t tensor_index, std::vector<float> &expvalues) + { + _tensor_filler_vfloat[tensor_index] = expvalues; + } + + /** + * @brief This will return true if the tensor by index, needs a filler option. + */ + bool get_tensor_filler(uint32_t tensor_index) + { + auto it = _tensor_filler.find(tensor_index); + if (it != _tensor_filler.end()) + { + return it->second; + } + return false; + } + + /** + * @brief This will return true if the tensor by index, needs a int array filler option. + */ + bool get_tensor_filler(uint32_t tensor_index, std::vector<int32_t> &expvalues) + { + auto it = _tensor_filler_vint32.find(tensor_index); + if (it != _tensor_filler_vint32.end()) + { + expvalues = it->second; + return true; + } + return false; + } + + bool get_tensor_filler(uint32_t tensor_index, std::vector<float> &expvalues) + { + auto it = _tensor_filler_vfloat.find(tensor_index); + if (it != _tensor_filler_vfloat.end()) + { + expvalues = it->second; + return true; + } + return false; + } + +private: + const CircleSubGraphs_t *_subgraphs{nullptr}; + const CircleBuffers_t *_buffers{nullptr}; + const CircleTensors_t *_tensors{nullptr}; + const CircleOperators_t *_operators{nullptr}; + + std::vector<const circle::OperatorCode *> _op_codes{}; + std::vector<int32_t> _inputs{}; + std::vector<int32_t> _outputs{}; + + std::map<uint32_t, bool> _tensor_filler{}; + std::map<uint32_t, std::vector<int32_t>> _tensor_filler_vint32{}; + std::map<uint32_t, std::vector<float>> _tensor_filler_vfloat{}; +}; + +} // namespace circlechef + +#endif // __CIRCLE_IMPORT_H__ diff --git a/compiler/circlechef/circle/src/CircleOpChef.h b/compiler/circlechef/circle/src/CircleOpChef.h new file mode 100644 index 000000000..a3bcd97d4 --- /dev/null +++ b/compiler/circlechef/circle/src/CircleOpChef.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_OP_CHEF_H__ +#define __CIRCLE_OP_CHEF_H__ + +#include <mio/circle/schema_generated.h> + +#include <circlechef.pb.h> + +#include "CircleImport.h" + +namespace circlechef +{ + +/** + * @brief Interface for each operators to build circlechef + */ +class CircleOpChef +{ +public: + virtual void filler(const circle::Operator *op, CircleImport *import, + circlechef::ModelRecipe *model_recipe) const = 0; + virtual ::circlechef::Operation *build(const circle::Operator *op, CircleImport *import, + circlechef::ModelRecipe *model_recipe) const = 0; + virtual ~CircleOpChef() {} +}; + +} // namespace circlechef + +#endif // __CIRCLE_OP_CHEF_H__ diff --git a/compiler/circlechef/circle/src/CircleOpChefs.h b/compiler/circlechef/circle/src/CircleOpChefs.h new file mode 100644 index 000000000..6a0ce5dc3 --- /dev/null +++ b/compiler/circlechef/circle/src/CircleOpChefs.h @@ -0,0 +1,26 @@ +/* + * 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_OP_CHEFS_H__ +#define __CIRCLE_OP_CHEFS_H__ + +// In alphabet order +#include "Op/BatchMatMul.h" +#include "Op/BCQFullyConnected.h" +#include "Op/BCQGather.h" +#include "Op/InstanceNorm.h" + +#endif // __CIRCLE_OP_CHEFS_H__ diff --git a/compiler/circlechef/circle/src/CircleOpRegistry.h b/compiler/circlechef/circle/src/CircleOpRegistry.h new file mode 100644 index 000000000..2bf1e19ed --- /dev/null +++ b/compiler/circlechef/circle/src/CircleOpRegistry.h @@ -0,0 +1,71 @@ +/* + * 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_OP_REGISTRY_H__ +#define __CIRCLE_OP_REGISTRY_H__ + +#include "CircleOpChef.h" +#include "CircleOpChefs.h" + +#include <memory> + +namespace circlechef +{ + +/** + * @brief circlechef operator registry + */ +class CircleOpRegistry +{ +public: + /** + * @brief Returns registered CircleOpChef pointer for BuiltinOperator or + * nullptr if not registered + */ + const CircleOpChef *lookup(circle::BuiltinOperator op) const + { + if (_circleop_map.find(op) == _circleop_map.end()) + return nullptr; + + return _circleop_map.at(op).get(); + } + + static CircleOpRegistry &get() + { + static CircleOpRegistry me; + return me; + } + +private: + CircleOpRegistry() + { +#define REG_TFL_OP(OPCODE, CLASS) \ + _circleop_map[circle::BuiltinOperator_##OPCODE] = std::make_unique<CLASS>() + + REG_TFL_OP(BATCH_MATMUL, CircleOpBatchMatMul); + REG_TFL_OP(BCQ_FULLY_CONNECTED, CircleOpBCQFullyConnected); + REG_TFL_OP(BCQ_GATHER, CircleOpBCQGather); + REG_TFL_OP(INSTANCE_NORM, CircleOpInstanceNorm); +#undef REG_TFL_OP + } + +private: + std::map<circle::BuiltinOperator, std::unique_ptr<CircleOpChef>> _circleop_map; +}; + +} // namespace circlechef + +#endif // __CIRCLE_OP_REGISTRY_H__ diff --git a/compiler/circlechef/circle/src/Convert.cpp b/compiler/circlechef/circle/src/Convert.cpp new file mode 100644 index 000000000..77614d9b5 --- /dev/null +++ b/compiler/circlechef/circle/src/Convert.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 "Convert.h" + +namespace circlechef +{ + +circlechef::TensorType as_circlechef_type(const circle::TensorType type) +{ + switch (type) + { + case circle::TensorType_FLOAT32: + return circlechef::FLOAT32; + case circle::TensorType_INT32: + return circlechef::INT32; + case circle::TensorType_INT64: + return circlechef::INT64; + case circle::TensorType_UINT8: + return circlechef::UINT8; + case circle::TensorType_BOOL: + return circlechef::BOOL; + // TODO handle other types + // TensorType_FLOAT16 + // TensorType_STRING + // TensorType_INT16 + // TensorType_COMPLEX64 + default: + throw std::runtime_error{"unsupported tensor type"}; + } +} + +circlechef::Activation as_circlechef_activation(const circle::ActivationFunctionType type) +{ + switch (type) + { + case circle::ActivationFunctionType_NONE: + return circlechef::NONE; + case circle::ActivationFunctionType_RELU: + return circlechef::RELU; + case circle::ActivationFunctionType_RELU6: + return circlechef::RELU6; + // TODO handle other types + // ActivationFunctionType_RELU_N1_TO_1 + // ActivationFunctionType_TANH + // ActivationFunctionType_SIGN_BIT + default: + throw std::runtime_error{"unsupported activation type"}; + } +} + +circlechef::Padding as_circlechef_padding(const circle::Padding padding) +{ + switch (padding) + { + case circle::Padding_SAME: + return circlechef::SAME; + case circle::Padding_VALID: + return circlechef::VALID; + default: + throw std::runtime_error{"unsupported padding"}; + } +} + +} // namespace circlechef diff --git a/compiler/circlechef/circle/src/Convert.h b/compiler/circlechef/circle/src/Convert.h new file mode 100644 index 000000000..7842c4b01 --- /dev/null +++ b/compiler/circlechef/circle/src/Convert.h @@ -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. + */ + +#ifndef __CONVERT_H__ +#define __CONVERT_H__ + +#include <mio/circle/schema_generated.h> + +#include <circlechef.pb.h> + +namespace circlechef +{ + +circlechef::TensorType as_circlechef_type(const circle::TensorType type); +circlechef::Activation as_circlechef_activation(const circle::ActivationFunctionType type); +circlechef::Padding as_circlechef_padding(const circle::Padding padding); + +/** + * @brief extract buffer data to std::vector<DT> + */ +template <typename DT> std::vector<DT> extract_buffer(const circle::Buffer *buffer) +{ + auto buffer_length = buffer->data()->size(); + auto num_elements = buffer_length / sizeof(DT); + std::vector<DT> result(num_elements); + std::memcpy(result.data(), buffer->data()->data(), buffer_length); + return result; +} + +template <typename T> std::vector<T> as_index_vector(const flatbuffers::Vector<T> *flat_array) +{ + if (flat_array == nullptr) + throw std::runtime_error("flat_array is nullptr"); + + std::vector<T> ret(flat_array->Length()); + for (uint32_t i = 0; i < flat_array->Length(); i++) + { + ret[i] = flat_array->Get(i); + } + return ret; +} + +} // namespace circlechef + +#endif // __CONVERT_H__ diff --git a/compiler/circlechef/circle/src/Op/BCQFullyConnected.cpp b/compiler/circlechef/circle/src/Op/BCQFullyConnected.cpp new file mode 100644 index 000000000..0e85f3969 --- /dev/null +++ b/compiler/circlechef/circle/src/Op/BCQFullyConnected.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 "BCQFullyConnected.h" + +#include "Convert.h" + +namespace circlechef +{ + +void CircleOpBCQFullyConnected::filler(const circle::Operator *op, CircleImport *import, + circlechef::ModelRecipe *model_recipe) const +{ + const std::vector<int32_t> &inputs = as_index_vector(op->inputs()); + + import->set_tensor_filler(inputs[1]); + import->set_tensor_filler(inputs[3]); + + const circle::Tensor *tensor2 = import->tensors()->Get(inputs[2]); + assert(tensor2->type() == circle::TensorType::TensorType_INT32); + const circle::Buffer *buffer2 = import->buffers()->Get(tensor2->buffer()); + auto vec2 = extract_buffer<int32_t>(buffer2); + import->set_tensor_filler(inputs[2], vec2); + + const circle::Tensor *tensor4 = import->tensors()->Get(inputs[4]); + assert(tensor4->type() == circle::TensorType::TensorType_INT32); + const circle::Buffer *buffer4 = import->buffers()->Get(tensor4->buffer()); + auto vec4 = extract_buffer<int32_t>(buffer4); + import->set_tensor_filler(inputs[4], vec4); +} + +circlechef::Operation *CircleOpBCQFullyConnected::build(const circle::Operator *op, + CircleImport *import, + circlechef::ModelRecipe *model_recipe) const +{ + auto op_params = op->builtin_options_as_BCQFullyConnectedOptions(); + assert(op_params != nullptr); + + auto operation = model_recipe->add_operation(); + + operation->set_type("BCQFullyConnected"); + + auto op_options = operation->mutable_bcq_fully_connected_options(); + + op_options->set_weights_hidden_size(op_params->weights_hidden_size()); + op_options->set_activation(as_circlechef_activation(op_params->fused_activation_function())); + + return operation; +} + +} // namespace circlechef diff --git a/compiler/circlechef/circle/src/Op/BCQFullyConnected.h b/compiler/circlechef/circle/src/Op/BCQFullyConnected.h new file mode 100644 index 000000000..c0ea581d9 --- /dev/null +++ b/compiler/circlechef/circle/src/Op/BCQFullyConnected.h @@ -0,0 +1,39 @@ +/* + * 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_OP_BCQFULLYCONNECTED_H__ +#define __CIRCLE_OP_BCQFULLYCONNECTED_H__ + +#include "CircleOpChef.h" + +namespace circlechef +{ + +/** + * @brief circlechef operator builder for BCQFullyConnected + */ +class CircleOpBCQFullyConnected : public CircleOpChef +{ +public: + void filler(const circle::Operator *op, CircleImport *import, + circlechef::ModelRecipe *model_recipe) const override; + circlechef::Operation *build(const circle::Operator *op, CircleImport *import, + circlechef::ModelRecipe *model_recipe) const override; +}; + +} // namespace circlechef + +#endif // __CIRCLE_OP_BCQFULLYCONNECTED_H__ diff --git a/compiler/circlechef/circle/src/Op/BCQGather.cpp b/compiler/circlechef/circle/src/Op/BCQGather.cpp new file mode 100644 index 000000000..cde345a34 --- /dev/null +++ b/compiler/circlechef/circle/src/Op/BCQGather.cpp @@ -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. + */ + +#include "BCQGather.h" + +#include "Convert.h" + +namespace circlechef +{ + +void CircleOpBCQGather::filler(const circle::Operator *op, CircleImport *import, + circlechef::ModelRecipe *model_recipe) const +{ + const std::vector<int32_t> &inputs = as_index_vector(op->inputs()); + + import->set_tensor_filler(inputs[0]); + + const circle::Tensor *tensor1 = import->tensors()->Get(inputs[1]); + assert(tensor1->type() == circle::TensorType::TensorType_INT32); + const circle::Buffer *buffer1 = import->buffers()->Get(tensor1->buffer()); + auto vec1 = extract_buffer<int32_t>(buffer1); + import->set_tensor_filler(inputs[1], vec1); + + const circle::Tensor *tensor2 = import->tensors()->Get(inputs[2]); + assert(tensor2->type() == circle::TensorType::TensorType_INT32); + const circle::Buffer *buffer2 = import->buffers()->Get(tensor2->buffer()); + auto vec2 = extract_buffer<int32_t>(buffer2); + import->set_tensor_filler(inputs[2], vec2); + + const circle::Tensor *tensor3 = import->tensors()->Get(inputs[3]); + assert(tensor3->type() == circle::TensorType::TensorType_INT32); + const circle::Buffer *buffer3 = import->buffers()->Get(tensor3->buffer()); + auto vec3 = extract_buffer<int32_t>(buffer3); + import->set_tensor_filler(inputs[3], vec3); +} + +circlechef::Operation *CircleOpBCQGather::build(const circle::Operator *op, CircleImport *import, + circlechef::ModelRecipe *model_recipe) const +{ + auto op_params = op->builtin_options_as_BCQGatherOptions(); + assert(op_params != nullptr); + + auto operation = model_recipe->add_operation(); + + operation->set_type("BCQGather"); + + auto op_options = operation->mutable_bcq_gather_options(); + + op_options->set_input_hidden_size(op_params->input_hidden_size()); + op_options->set_axis(op_params->axis()); + + return operation; +} + +} // namespace circlechef diff --git a/compiler/circlechef/circle/src/Op/BCQGather.h b/compiler/circlechef/circle/src/Op/BCQGather.h new file mode 100644 index 000000000..0ff040551 --- /dev/null +++ b/compiler/circlechef/circle/src/Op/BCQGather.h @@ -0,0 +1,39 @@ +/* + * 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_OP_BCQGATHER_H__ +#define __CIRCLE_OP_BCQGATHER_H__ + +#include "CircleOpChef.h" + +namespace circlechef +{ + +/** + * @brief circlechef operator builder for BCQGather + */ +class CircleOpBCQGather : public CircleOpChef +{ +public: + void filler(const circle::Operator *op, CircleImport *import, + circlechef::ModelRecipe *model_recipe) const override; + circlechef::Operation *build(const circle::Operator *op, CircleImport *import, + circlechef::ModelRecipe *model_recipe) const override; +}; + +} // namespace circlechef + +#endif // __CIRCLE_OP_BCQGATHER_H__ diff --git a/compiler/circlechef/circle/src/Op/BatchMatMul.cpp b/compiler/circlechef/circle/src/Op/BatchMatMul.cpp new file mode 100644 index 000000000..bcf218865 --- /dev/null +++ b/compiler/circlechef/circle/src/Op/BatchMatMul.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 "BatchMatMul.h" + +#include "Convert.h" + +namespace circlechef +{ + +void CircleOpBatchMatMul::filler(const circle::Operator *op, CircleImport *import, + circlechef::ModelRecipe *model_recipe) const +{ + // Nothing to do with filler +} + +circlechef::Operation *CircleOpBatchMatMul::build(const circle::Operator *op, CircleImport *import, + circlechef::ModelRecipe *model_recipe) const +{ + auto op_params = op->builtin_options_as_BatchMatMulOptions(); + assert(op_params != nullptr); + + auto operation = model_recipe->add_operation(); + + operation->set_type("BatchMatMul"); + + auto op_options = operation->mutable_batch_matmul_options(); + + op_options->set_adjoint_lhs(op_params->adjoint_lhs()); + op_options->set_adjoint_rhs(op_params->adjoint_rhs()); + + return operation; +} + +} // namespace circlechef diff --git a/compiler/circlechef/circle/src/Op/BatchMatMul.h b/compiler/circlechef/circle/src/Op/BatchMatMul.h new file mode 100644 index 000000000..3d4036877 --- /dev/null +++ b/compiler/circlechef/circle/src/Op/BatchMatMul.h @@ -0,0 +1,39 @@ +/* + * 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_OP_BATCHMATMUL_H__ +#define __CIRCLE_OP_BATCHMATMUL_H__ + +#include "CircleOpChef.h" + +namespace circlechef +{ + +/** + * @brief circlechef operator builder for batchmatmul + */ +class CircleOpBatchMatMul : public CircleOpChef +{ +public: + void filler(const circle::Operator *op, CircleImport *import, + circlechef::ModelRecipe *model_recipe) const override; + circlechef::Operation *build(const circle::Operator *op, CircleImport *import, + circlechef::ModelRecipe *model_recipe) const override; +}; + +} // namespace circlechef + +#endif // __CIRCLE_OP_BATCHMATMUL_H__ diff --git a/compiler/circlechef/circle/src/Op/InstanceNorm.cpp b/compiler/circlechef/circle/src/Op/InstanceNorm.cpp new file mode 100644 index 000000000..a1395a578 --- /dev/null +++ b/compiler/circlechef/circle/src/Op/InstanceNorm.cpp @@ -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. + */ + +#include "InstanceNorm.h" + +#include "Convert.h" + +namespace circlechef +{ + +void CircleOpInstanceNorm::filler(const circle::Operator *op, CircleImport *import, + circlechef::ModelRecipe *model_recipe) const +{ + // index 1 and 2 maybe constant + const std::vector<int32_t> &inputs = as_index_vector(op->inputs()); + assert(inputs.size() == 3); + + import->set_tensor_filler(inputs[1]); // set gaussian filler + import->set_tensor_filler(inputs[2]); +} + +circlechef::Operation *CircleOpInstanceNorm::build(const circle::Operator *op, CircleImport *import, + circlechef::ModelRecipe *model_recipe) const +{ + auto operation = model_recipe->add_operation(); + + operation->set_type("InstanceNorm"); + + auto op_options = operation->mutable_instance_norm_options(); + + auto op_params = op->builtin_options_as_InstanceNormOptions(); + assert(op_params != nullptr); + + op_options->set_epsilon(op_params->epsilon()); + op_options->set_activation(as_circlechef_activation(op_params->fused_activation_function())); + + return operation; +} + +} // namespace circlechef diff --git a/compiler/circlechef/circle/src/Op/InstanceNorm.h b/compiler/circlechef/circle/src/Op/InstanceNorm.h new file mode 100644 index 000000000..9cb48e184 --- /dev/null +++ b/compiler/circlechef/circle/src/Op/InstanceNorm.h @@ -0,0 +1,39 @@ +/* + * 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_OP_INSTANCE_NORM_H__ +#define __CIRCLE_OP_INSTANCE_NORM_H__ + +#include "CircleOpChef.h" + +namespace circlechef +{ + +/** + * @brief circlechef operator builder for INSTANCE_NORM + */ +class CircleOpInstanceNorm : public CircleOpChef +{ +public: + void filler(const circle::Operator *op, CircleImport *import, + circlechef::ModelRecipe *model_recipe) const override; + circlechef::Operation *build(const circle::Operator *op, CircleImport *import, + circlechef::ModelRecipe *model_recipe) const override; +}; + +} // namespace circlechef + +#endif // __CIRCLE_OP_INSTANCE_NORM_H__ diff --git a/compiler/circlechef/circle/src/RecipeChef.cpp b/compiler/circlechef/circle/src/RecipeChef.cpp new file mode 100644 index 000000000..17ef1be6e --- /dev/null +++ b/compiler/circlechef/circle/src/RecipeChef.cpp @@ -0,0 +1,248 @@ +/* + * 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 <circlechef/RecipeChef.h> + +#include "Convert.h" +#include "CircleImport.h" +#include "CircleOpChef.h" +#include "CircleOpChefs.h" +#include "CircleOpRegistry.h" + +#include <fstream> +#include <sstream> + +namespace circlechef +{ + +void set_inputs(CircleImport *import, circlechef::Operation *operation, const circle::Operator *op) +{ + auto tensors = import->tensors(); + const std::vector<int32_t> &inputs = as_index_vector(op->inputs()); + + for (auto input : inputs) + { + if (input == -1) + { + operation->add_input(""); + } + else + { + auto tensor = tensors->Get(input); + std::string name = tensor_name(tensor); + operation->add_input(name); + } + } +} + +void set_outputs(CircleImport *import, circlechef::Operation *operation, const circle::Operator *op) +{ + auto tensors = import->tensors(); + const std::vector<int32_t> &outputs = as_index_vector(op->outputs()); + + for (auto output : outputs) + { + auto tensor = tensors->Get(output); + std::string name = tensor_name(tensor); + operation->add_output(name); + } +} + +/** + * @brief This will build ModelRecipe from circle::Model + * First to check operand filler options by scanning all operators, + * then translate all operands and operators. + * Last will set network inputs and outputs. + */ +std::unique_ptr<ModelRecipe> generate_recipe(const circle::Model *model) +{ + std::unique_ptr<ModelRecipe> model_recipe{new ModelRecipe()}; + + CircleImport circle_import(model); + + assert(circle_import.num_subgraph() == 1); + circle_import.select_sub_graph(0); + + auto tensors = circle_import.tensors(); + auto buffers = circle_import.buffers(); + auto operators = circle_import.operators(); + + // operand fillers for adding all operators + for (uint32_t i = 0; i < operators->Length(); ++i) + { + const auto *op = operators->Get(i); + circle::BuiltinOperator builtincode = circle_import.builtin_code(op); + + if (const auto *graph_builder = CircleOpRegistry::get().lookup(builtincode)) + { + graph_builder->filler(op, &circle_import, model_recipe.get()); + } + else + { + std::string opcodename = circle_import.opcode_name(op); + throw std::runtime_error{"Not supported: " + opcodename}; + } + } + + // add all operands(tensors) + for (uint32_t i = 0; i < tensors->Length(); ++i) + { + auto tensor = tensors->Get(i); + + // check buffer + if (tensor->buffer() >= buffers->size()) + throw std::runtime_error{"file load failed"}; + + ::circlechef::Operand *operand = model_recipe->add_operand(); + + operand->set_name(tensor_name(tensor)); + operand->set_type(as_circlechef_type(tensor->type())); + + std::vector<int32_t> dims = as_index_vector(tensor->shape()); + ::circlechef::TensorShape *shape = operand->mutable_shape(); + for (auto dim : dims) + { + shape->add_dim(dim); + } + + // filler for weights, bias and so on + std::vector<int32_t> expvalues; + std::vector<float> expfvalues; + if (circle_import.get_tensor_filler(i)) + { + circlechef::TensorFiller *filler = operand->mutable_filler(); + // Note: it is OK to use random weights for functionality validation + filler->set_tag("gaussian"); + filler->add_arg("0.0"); // average + filler->add_arg("0.1"); // standard deviation + } + else if (circle_import.get_tensor_filler(i, expvalues)) + { + circlechef::TensorFiller *filler = operand->mutable_filler(); + filler->set_tag("explicit"); + for (auto value : expvalues) + { + std::ostringstream ss; + ss << value; + filler->add_arg(ss.str()); + } + } + else if (circle_import.get_tensor_filler(i, expfvalues)) + { + circlechef::TensorFiller *filler = operand->mutable_filler(); + filler->set_tag("explicit"); + for (auto value : expfvalues) + { + std::ostringstream ss; + ss << value; + filler->add_arg(ss.str()); + } + } + + auto quant = tensor->quantization(); + if (quant != nullptr) + { + // Note: Calling 'operand->mutable_quant()' will create empty 'quant' node + // in the recipe file. We want this only when valid parameter exist. + if (quant->min() != nullptr && quant->min()->size() > 0) + { + circlechef::TensorQuantization *chef_quant = operand->mutable_quant(); + for (uint32_t idx = 0; idx < quant->min()->size(); ++idx) + chef_quant->add_min(quant->min()->Get(idx)); + } + if (quant->max() != nullptr && quant->max()->size() > 0) + { + circlechef::TensorQuantization *chef_quant = operand->mutable_quant(); + for (uint32_t idx = 0; idx < quant->max()->size(); idx++) + chef_quant->add_max(quant->max()->Get(idx)); + } + if (quant->scale() != nullptr && quant->scale()->size() > 0) + { + circlechef::TensorQuantization *chef_quant = operand->mutable_quant(); + for (uint32_t idx = 0; idx < quant->scale()->size(); ++idx) + chef_quant->add_scale(quant->scale()->Get(idx)); + } + if (quant->zero_point() != nullptr && quant->zero_point()->size() > 0) + { + circlechef::TensorQuantization *chef_quant = operand->mutable_quant(); + for (uint32_t idx = 0; idx < quant->zero_point()->size(); ++idx) + chef_quant->add_zero_point(quant->zero_point()->Get(idx)); + } + } + } + + // add all operators + for (uint32_t i = 0; i < operators->Length(); ++i) + { + const auto *op = operators->Get(i); + circle::BuiltinOperator builtincode = circle_import.builtin_code(op); + + if (const auto *graph_builder = CircleOpRegistry::get().lookup(builtincode)) + { + auto operation = graph_builder->build(op, &circle_import, model_recipe.get()); + + // common for all operators: inputs, outputs + set_inputs(&circle_import, operation, op); + set_outputs(&circle_import, operation, op); + } + else + { + std::string opcodename = circle_import.opcode_name(op); + throw std::runtime_error{"Not supported: " + opcodename}; + } + } + + // network inputs/outputs + const std::vector<int32_t> &inputs = circle_import.inputs(); + const std::vector<int32_t> &outputs = circle_import.outputs(); + + for (const auto input : inputs) + { + auto tensor = tensors->Get(input); + std::string name = tensor_name(tensor); + + model_recipe->add_input(name); + } + for (const auto output : outputs) + { + auto tensor = tensors->Get(output); + std::string name = tensor_name(tensor); + + model_recipe->add_output(name); + } + + return std::move(model_recipe); +} + +bool write_recipe(const std::string &filename, std::unique_ptr<ModelRecipe> &recipe) +{ + std::fstream fo(filename, std::ios::binary | std::ios::out); + + if (!fo.is_open()) + { + throw std::runtime_error{"file store failed"}; + } + + // Note: SerializeToString() or SerializeToOstream() writes in binary mode + // DebugString() and Utf8DebugString() will print as a human readable text + fo << recipe->Utf8DebugString(); + + fo.close(); + + return true; +} + +} // namespace circlechef diff --git a/compiler/circlechef/core/CMakeLists.txt b/compiler/circlechef/core/CMakeLists.txt new file mode 100644 index 000000000..54b3ea53d --- /dev/null +++ b/compiler/circlechef/core/CMakeLists.txt @@ -0,0 +1,9 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") + +add_library(circlechef_core STATIC ${SOURCES}) +target_include_directories(circlechef_core PUBLIC include) +target_include_directories(circlechef_core PRIVATE src) +target_link_libraries(circlechef_core circlechef_proto) +target_link_libraries(circlechef_core circlechef_log) +target_link_libraries(circlechef_core mio_circle) +target_link_libraries(circlechef_core souschef) diff --git a/compiler/circlechef/core/include/circlechef/ModelChef.h b/compiler/circlechef/core/include/circlechef/ModelChef.h new file mode 100644 index 000000000..64326179c --- /dev/null +++ b/compiler/circlechef/core/include/circlechef/ModelChef.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 __MODEL_CHEF_H__ +#define __MODEL_CHEF_H__ + +#include <circlechef.pb.h> + +#include <memory> + +namespace circlechef +{ + +class GeneratedModel final +{ +public: + struct Impl + { + virtual ~Impl() = default; + + virtual const char *base(void) const = 0; + virtual size_t size(void) const = 0; + }; + +public: + GeneratedModel(std::unique_ptr<Impl> &&impl) : _impl{std::move(impl)} + { + // DO NOTHING + } + +public: + const char *base(void) const { return _impl->base(); } + size_t size(void) const { return _impl->size(); } + +private: + std::unique_ptr<Impl> _impl; +}; + +GeneratedModel cook(const ModelRecipe &model_recipe); + +} // namespace circlechef + +#endif // __MODEL_CHEF_H__ diff --git a/compiler/circlechef/core/src/Arguments.h b/compiler/circlechef/core/src/Arguments.h new file mode 100644 index 000000000..9fe7bbb77 --- /dev/null +++ b/compiler/circlechef/core/src/Arguments.h @@ -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. + */ + +#ifndef __ARGUMENTS_H__ +#define __ARGUMENTS_H__ + +#include <cstdint> +#include <string> + +/** + * @brief Read-only string sequence view + */ +struct Arguments +{ + virtual ~Arguments() = default; + + virtual uint32_t count(void) const = 0; + virtual const std::string &value(uint32_t n) const = 0; +}; + +#endif // __ARGUMENTS_H__ diff --git a/compiler/circlechef/core/src/Convert.cpp b/compiler/circlechef/core/src/Convert.cpp new file mode 100644 index 000000000..2db0a6212 --- /dev/null +++ b/compiler/circlechef/core/src/Convert.cpp @@ -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. + */ + +#include "Convert.h" + +#include <stdexcept> + +circle::Padding as_circle_padding(const circlechef::Padding &value) +{ + switch (value) + { + case circlechef::SAME: + return circle::Padding_SAME; + case circlechef::VALID: + return circle::Padding_VALID; + default: + break; + } + + throw std::runtime_error{"Unknown padding value"}; +} + +circle::ActivationFunctionType as_circle_activation(const circlechef::Activation &value) +{ + switch (value) + { + case circlechef::NONE: + return circle::ActivationFunctionType_NONE; + case circlechef::RELU: + return circle::ActivationFunctionType_RELU; + case circlechef::RELU6: + return circle::ActivationFunctionType_RELU6; + default: + break; + } + + throw std::runtime_error{"Unknown activation"}; +} + +circle::TensorType as_circle_tensortype(const circlechef::TensorType &value) +{ + switch (value) + { + case circlechef::FLOAT32: + return circle::TensorType_FLOAT32; + case circlechef::INT32: + return circle::TensorType_INT32; + case circlechef::UINT8: + return circle::TensorType_UINT8; + case circlechef::INT64: + return circle::TensorType_INT64; + case circlechef::BOOL: + return circle::TensorType_BOOL; + default: + break; + } + + throw std::runtime_error{"Unknown tensor type"}; +} diff --git a/compiler/circlechef/core/src/Convert.h b/compiler/circlechef/core/src/Convert.h new file mode 100644 index 000000000..766098da2 --- /dev/null +++ b/compiler/circlechef/core/src/Convert.h @@ -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. + */ + +/** + * @file Convert.h + * @brief This header declares various as_circle_TYPE functions + */ +#ifndef __CONVERT_H__ +#define __CONVERT_H__ + +#include <circlechef.pb.h> +#include <mio/circle/schema_generated.h> + +circle::Padding as_circle_padding(const circlechef::Padding &value); +circle::ActivationFunctionType as_circle_activation(const circlechef::Activation &value); +circle::TensorType as_circle_tensortype(const circlechef::TensorType &value); + +#endif // __CONVERT_H__ diff --git a/compiler/circlechef/core/src/ModelChef.cpp b/compiler/circlechef/core/src/ModelChef.cpp new file mode 100644 index 000000000..76aeacdd9 --- /dev/null +++ b/compiler/circlechef/core/src/ModelChef.cpp @@ -0,0 +1,631 @@ +/* + * 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 "circlechef/ModelChef.h" +#include <souschef/RangedArguments.h> +#include <souschef/Registry.h> + +#include "Convert.h" + +#include <souschef/DataChefs.h> + +#include "OpChef.h" +#include "OpChefs.h" + +#include <souschef/Dataset.h> + +#include "Log.h" + +#include <iterator> +#include <map> +#include <string> +#include <vector> + +#include <cassert> +#include <fstream> +#include <iostream> +#include <numeric> +#include <sstream> +#include <stdexcept> + +namespace +{ + +using namespace souschef; + +template <typename T> std::vector<T> as_vector(const ::google::protobuf::RepeatedPtrField<T> &field) +{ + std::vector<T> res; + for (const auto &elem : field) + { + res.emplace_back(elem); + } + return res; +} + +template <typename T> Dataset<T> as_dataset(const ::google::protobuf::RepeatedPtrField<T> &field) +{ + return Dataset<T>(as_vector<T>(field)); +} + +} // namespace + +namespace +{ + +template <typename T> using Dims = std::vector<T>; + +Dims<int32_t> as_dims(const circlechef::TensorShape &shape) +{ + std::vector<int32_t> res; + + for (auto &dim : shape.dim()) + { + res.emplace_back(static_cast<int32_t>(dim)); + } + + return res; +} + +int32_t element_count(const Dims<int32_t> &dims) +{ + return std::accumulate(dims.begin(), dims.end(), 1, std::multiplies<int32_t>()); +} + +} // namespace + +namespace +{ + +class GeneratedModelImpl final : public circlechef::GeneratedModel::Impl +{ +public: + GeneratedModelImpl(std::unique_ptr<flatbuffers::FlatBufferBuilder> &&builder) + : _builder{std::move(builder)} + { + // DO NOTHING + } + +public: + const char *base(void) const override + { + // Return the base address of generated flatbuffer model + return reinterpret_cast<const char *>(_builder->GetBufferPointer()); + } + +public: + size_t size(void) const override + { + // Return the size of generated flatbuffer model + return _builder->GetSize(); + } + +private: + std::unique_ptr<flatbuffers::FlatBufferBuilder> _builder; +}; + +} // namespace + +namespace +{ + +struct DataChefRegistry final : public Registry<DataChefFactory> +{ +}; + +DataChefRegistry &data_chef_registry(const circlechef::TensorType &type) +{ + static DataChefRegistry s32; + static DataChefRegistry s64; + static DataChefRegistry fp32; + static DataChefRegistry u8; + static DataChefRegistry boolean; + + switch (type) + { + case circlechef::INT32: + return s32; + case circlechef::INT64: + return s64; + case circlechef::FLOAT32: + return fp32; + case circlechef::UINT8: + return u8; + case circlechef::BOOL: + return boolean; + default: + break; + } + + throw std::runtime_error{"Unknown tensor type"}; +} + +struct OpChefRegistry final : public Registry<OpChefFactory> +{ +}; + +OpChefRegistry &op_chef_registry(void) +{ + static OpChefRegistry registry; + return registry; +} + +/// @brief This will prepare a map of unique builtin codes in the model recipe +std::map<circle::BuiltinOperator, int32_t> +gather_builtincode_map(const ::circlechef::ModelRecipe &model_recipe) +{ + // Key and value of the map are BuiltinOperator and operator version + std::map<circle::BuiltinOperator, int32_t> builtin_map; + + for (const auto &operation : model_recipe.operation()) + { + auto op_chef = op_chef_registry().lookup(operation.type()).create(&operation); + if (op_chef->code() == circle::BuiltinOperator_CUSTOM) + continue; + + // Various operation version is unified as the highest version among them + if (builtin_map.find(op_chef->code()) == builtin_map.end() || + builtin_map[op_chef->code()] < operation.version()) + builtin_map[op_chef->code()] = operation.version(); + } + + // Add ops used in Graphs(subgraphs) + for (int g = 0; g < model_recipe.graph_size(); ++g) + { + const auto &graph = model_recipe.graph(g); + for (const auto &operation : graph.operation()) + { + auto op_chef = op_chef_registry().lookup(operation.type()).create(&operation); + if (op_chef->code() == circle::BuiltinOperator_CUSTOM) + continue; + + // Various operation version is unified as the highest version among them + if (builtin_map.find(op_chef->code()) == builtin_map.end() || + builtin_map[op_chef->code()] < operation.version()) + builtin_map[op_chef->code()] = operation.version(); + } + } + + return builtin_map; +} + +/// @brief This will prepare a set of unique custom codes in the mode recipe +std::set<std::string> gather_customcode_set(const ::circlechef::ModelRecipe &model_recipe) +{ + std::set<std::string> customcode_set; + for (const auto &operation : model_recipe.operation()) + { + auto op_chef = op_chef_registry().lookup(operation.type()).create(&operation); + if (op_chef->code() == circle::BuiltinOperator_CUSTOM) + customcode_set.insert(operation.type()); + } + + // Add ops used in Graphs(subgraphs) + for (int g = 0; g < model_recipe.graph_size(); ++g) + { + const auto &graph = model_recipe.graph(g); + for (const auto &operation : graph.operation()) + { + auto op_chef = op_chef_registry().lookup(operation.type()).create(&operation); + if (op_chef->code() == circle::BuiltinOperator_CUSTOM) + customcode_set.insert(operation.type()); + } + } + + return customcode_set; +} + +} // namespace + +namespace +{ + +struct CookParams +{ + std::vector<flatbuffers::Offset<::circle::Buffer>> &buffer_vec; + std::vector<flatbuffers::Offset<::circle::OperatorCode>> &code_vec; + std::vector<flatbuffers::Offset<::circle::SubGraph>> &subgraph_vec; + std::unique_ptr<flatbuffers::FlatBufferBuilder> &flatbuffer_builder; + std::map<circle::BuiltinOperator, int32_t> &builtin_code_map; + std::string noname; +}; + +template <typename T> void cook_graph(const T &graph, CookParams &cp) +{ + LOGGER(l); + + std::vector<flatbuffers::Offset<::circle::Buffer>> &buffer_vec = cp.buffer_vec; + std::vector<flatbuffers::Offset<::circle::OperatorCode>> &code_vec = cp.code_vec; + std::vector<flatbuffers::Offset<::circle::SubGraph>> &subgraph_vec = cp.subgraph_vec; + std::unique_ptr<flatbuffers::FlatBufferBuilder> &flatbuffer_builder = cp.flatbuffer_builder; + std::map<circle::BuiltinOperator, int32_t> &builtin_code_map = cp.builtin_code_map; + + // Operand-related + std::vector<flatbuffers::Offset<::circle::Tensor>> tensor_vec; + + // Operation-related + std::vector<flatbuffers::Offset<::circle::Operator>> operator_vec; + + // default name for graph + std::string graph_name = cp.noname; + if (graph.has_name()) + graph_name = graph.name(); + + // Tensor Name -> Tensor ID mapping (per Graph) + std::map<std::string, int32_t> symbol_table; + + auto lookup = [&symbol_table, &graph_name](const std::string &name) { + if (symbol_table.find(name) != symbol_table.end()) + return symbol_table.at(name); + else if (name == "") + return -1; // -1 in circle means that optional input tensor is empty. + else + { + std::string msg = "circlechef : input not found in " + graph_name + " graph"; + throw std::runtime_error(msg.c_str()); + } + }; + + int32_t buffer_start = buffer_vec.size(); + int32_t buffer_index = 0; + + // Create buffer(s) 1~n(I) for input(s) + const auto size_input = graph.input_size(); + for (int ci = 0; ci < size_input; ++ci) + { + circle::BufferBuilder buffer_builder{*flatbuffer_builder}; + buffer_vec.emplace_back(buffer_builder.Finish()); + } + // Create buffer(s) n(I)+1~n(I)+n(O) for output(s) + const auto size_output = graph.output_size(); + for (int co = 0; co < size_output; ++co) + { + circle::BufferBuilder buffer_builder{*flatbuffer_builder}; + buffer_vec.emplace_back(buffer_builder.Finish()); + } + + auto input_names = as_dataset(graph.input()).vectorize(); + auto output_names = as_dataset(graph.output()).vectorize(); + + for (const auto &operand : graph.operand()) + { + assert(operand.has_name()); + + assert(operand.has_type()); + + flatbuffers::Offset<flatbuffers::Vector<int32_t>> shape; + std::vector<int32_t> dims; + if (operand.has_shape()) + { + dims = as_dims(operand.shape()); + shape = flatbuffer_builder->CreateVector(dims); + } + + auto name = flatbuffer_builder->CreateString(operand.name()); + + buffer_index = 0; + + // Create Buffer if filler is specified + if (operand.has_filler()) + { + const auto &filler = operand.filler(); + + assert(filler.has_tag()); + + auto args = ranged_arguments(filler.arg().begin(), filler.arg().end()); + auto chef = data_chef_registry(operand.type()).lookup(filler.tag()).create(args); + + assert(chef != nullptr); + + // Create Data + int32_t count = (element_count(dims) > 0) ? element_count(dims) : filler.arg_size(); + auto data_vec = chef->generate(count); + auto data = flatbuffer_builder->CreateVector(data_vec); + + // Create Buffer + circle::BufferBuilder buffer_builder{*flatbuffer_builder}; + buffer_builder.add_data(data); + auto buffer = buffer_builder.Finish(); + + // Update Buffer Index & Vector + buffer_index = buffer_vec.size(); + buffer_vec.emplace_back(buffer); + } + else + { + // if this is input or output, assign to that buffer_index + int idx = 0; + for (auto it = input_names.begin(); it != input_names.end(); ++it, ++idx) + { + if (*it == operand.name()) + { + buffer_index = buffer_start + idx; + break; + } + } + if (buffer_index == 0) + { + idx = 0; + for (auto it = output_names.begin(); it != output_names.end(); ++it, ++idx) + { + if (*it == operand.name()) + { + buffer_index = buffer_start + size_input + idx; + break; + } + } + } + if (buffer_index == 0) + { + // we couldn't find the buffer; create an empty buffer for this tensor + buffer_index = buffer_vec.size(); + + circle::BufferBuilder buffer_builder{*flatbuffer_builder}; + buffer_vec.emplace_back(buffer_builder.Finish()); + } + } + assert(buffer_index != 0); + + flatbuffers::Offset<circle::QuantizationParameters> quant_index; + + // Create QuantizationParameters if quant is specified + if (operand.has_quant()) + { + const auto &quant = operand.quant(); + + // Create each parameters + // NOTE if some parameters are not given, those will be set to default value + std::vector<float> quant_max_vec(quant.max_size()); + std::vector<float> quant_min_vec(quant.min_size()); + std::vector<float> quant_scale_vec(quant.scale_size()); + std::vector<int64_t> quant_zero_point_vec(quant.zero_point_size()); + + for (uint32_t i = 0; i < quant.max_size(); ++i) + quant_max_vec.at(i) = quant.max(i); + for (uint32_t i = 0; i < quant.min_size(); ++i) + quant_min_vec.at(i) = quant.min(i); + for (uint32_t i = 0; i < quant.scale_size(); ++i) + quant_scale_vec.at(i) = quant.scale(i); + for (uint32_t i = 0; i < quant.zero_point_size(); ++i) + quant_zero_point_vec.at(i) = quant.zero_point(i); + + auto quant_max = flatbuffer_builder->CreateVector(quant_max_vec); + auto quant_min = flatbuffer_builder->CreateVector(quant_min_vec); + auto quant_scale = flatbuffer_builder->CreateVector(quant_scale_vec); + auto quant_zero_point = flatbuffer_builder->CreateVector(quant_zero_point_vec); + + // Create QuantizationParameters + circle::QuantizationParametersBuilder quant_builder{*flatbuffer_builder}; + quant_builder.add_max(quant_max); + quant_builder.add_min(quant_min); + quant_builder.add_scale(quant_scale); + quant_builder.add_zero_point(quant_zero_point); + + // Update QuantizationParameters Index + quant_index = quant_builder.Finish(); + } + + // Create Tensor + circle::TensorBuilder tensor_builder{*flatbuffer_builder}; + + tensor_builder.add_shape(shape); + tensor_builder.add_type(as_circle_tensortype(operand.type())); + tensor_builder.add_buffer(buffer_index); + tensor_builder.add_name(name); + if (operand.has_quant()) + tensor_builder.add_quantization(quant_index); + + // Append! + tensor_vec.emplace_back(tensor_builder.Finish()); + + // Update Tensor Name -> Tensor Index Map + int32_t tensor_index = symbol_table.size(); + const auto &tensor_name = operand.name(); + + INFO(l) << "Symbol [" << tensor_name << "] = Tensor " << tensor_index << std::endl; + + symbol_table[tensor_name] = tensor_index; + } + + // Create Operator + for (const auto &operation : graph.operation()) + { + assert(operation.has_type()); + + auto op_chef = op_chef_registry().lookup(operation.type()).create(&operation); + + // Create 'inputs' + std::vector<int32_t> input_vec = as_dataset(operation.input()).map(lookup).vectorize(); + auto inputs = flatbuffer_builder->CreateVector(input_vec); + + // Create 'outputs' + std::vector<int32_t> output_vec = as_dataset(operation.output()).map(lookup).vectorize(); + auto outputs = flatbuffer_builder->CreateVector(output_vec); + + // Create Option + auto options = op_chef->value(*flatbuffer_builder); + + // Create Custom option + auto circle_custom_options = op_chef->custom_value(*flatbuffer_builder); + + // Create Operator + circle::OperatorBuilder op_builder{*flatbuffer_builder}; + + // Get operator code index from builtin_code_map with assumption, order of + // builtin_code_map is same as that of code_vec + auto op_it = builtin_code_map.find(op_chef->code()); + assert(op_it != builtin_code_map.end()); + uint32_t opcode_index = std::distance(builtin_code_map.begin(), op_it); + + op_builder.add_opcode_index(opcode_index); + op_builder.add_inputs(inputs); + op_builder.add_outputs(outputs); + op_builder.add_builtin_options_type(op_chef->type()); + op_builder.add_builtin_options(options); + op_builder.add_custom_options(circle_custom_options); + op_builder.add_custom_options_format(circle::CustomOptionsFormat_FLEXBUFFERS); + // Append Operator + operator_vec.emplace_back(op_builder.Finish()); + } + + // Create network input/output vector + std::vector<int32_t> input_vec = as_dataset(graph.input()).map(lookup).vectorize(); + std::vector<int32_t> output_vec = as_dataset(graph.output()).map(lookup).vectorize(); + + // Create "SubGraph" arguments + auto tensors = flatbuffer_builder->CreateVector(tensor_vec); + auto inputs = flatbuffer_builder->CreateVector(input_vec); + auto outputs = flatbuffer_builder->CreateVector(output_vec); + auto operators = flatbuffer_builder->CreateVector(operator_vec); + auto name = flatbuffer_builder->CreateString(graph_name); + + circle::SubGraphBuilder subgraph_builder{*flatbuffer_builder}; + + subgraph_builder.add_tensors(tensors); + subgraph_builder.add_inputs(inputs); + subgraph_builder.add_outputs(outputs); + subgraph_builder.add_operators(operators); + subgraph_builder.add_name(name); + + subgraph_vec.emplace_back(subgraph_builder.Finish()); +} + +} // namespace + +namespace circlechef +{ + +/** + * @brief Generate a (in-memory) TensorFlow Lite model from a given model recipe + */ +GeneratedModel cook(const ::circlechef::ModelRecipe &model_recipe) +{ +// Initialize Op Chef Registry +#define OP_CHEF(NAME, FACTORY_CLASS) \ + op_chef_registry().add(#NAME, std::unique_ptr<FACTORY_CLASS>(new FACTORY_CLASS())); +#include "OpChef.def" +#undef OP_CHEF + +// Initialize Data Chef Registry +#define DATA_CHEF(TYPE, NAME, FACTORY_CLASS) \ + data_chef_registry(::circlechef::TYPE) \ + .add(#NAME, std::unique_ptr<FACTORY_CLASS>(new FACTORY_CLASS())); +#include <souschef/DataChef.def> +#undef DATA_CHEF + + // + // Create FlatBufferBuilder + // + auto flatbuffer_builder = + std::unique_ptr<flatbuffers::FlatBufferBuilder>(new flatbuffers::FlatBufferBuilder(1024)); + + // Operand-related + std::vector<flatbuffers::Offset<::circle::Buffer>> buffer_vec; + + // Operation-related + std::vector<flatbuffers::Offset<::circle::OperatorCode>> code_vec; + + // Graphs-related + std::vector<flatbuffers::Offset<::circle::SubGraph>> subgraph_vec; + + // Create OperatorCode with Builtin Operator + std::map<circle::BuiltinOperator, int32_t> builtin_code_map = + gather_builtincode_map(model_recipe); + for (auto const &opcode : builtin_code_map) + { + circle::OperatorCodeBuilder code_builder{*flatbuffer_builder}; + code_builder.add_builtin_code(opcode.first); + code_builder.add_version(opcode.second); + auto code = code_builder.Finish(); + // Update OperatorCode vector + code_vec.emplace_back(code); + } + + // Create OperatorCode with Custom Operator + std::set<std::string> custom_code_set = gather_customcode_set(model_recipe); + if (custom_code_set.size() && + builtin_code_map.find(circle::BuiltinOperator_CUSTOM) == builtin_code_map.end()) + builtin_code_map[circle::BuiltinOperator_CUSTOM] = 1; + + for (auto opcode : custom_code_set) + { + auto custom_code = flatbuffer_builder->CreateString(opcode); + circle::OperatorCodeBuilder code_builder{*flatbuffer_builder}; + code_builder.add_builtin_code(circle::BuiltinOperator_CUSTOM); + code_builder.add_custom_code(custom_code); + auto code = code_builder.Finish(); + // Update OperatorCode vector + code_vec.emplace_back(code); + } + + // Create an Empty Buffer + // + // Buffer 0 SHOULD be an empty buffer in TensorFlow Lite model file + // (Please refer to the comment for Tensor.buffer field in schema) + { + circle::BufferBuilder buffer_builder{*flatbuffer_builder}; + buffer_vec.emplace_back(buffer_builder.Finish()); + } + + // + // Create Main graph + // + CookParams cp{buffer_vec, code_vec, subgraph_vec, flatbuffer_builder, builtin_code_map, "main"}; + + cook_graph<::circlechef::ModelRecipe>(model_recipe, cp); + + // + // Create subgraphs if exist + // + for (int g = 0; g < model_recipe.graph_size(); ++g) + { + const auto &graph = model_recipe.graph(g); + + std::ostringstream stringStream; + stringStream << "sub_" << (g + 1); + + CookParams cp{buffer_vec, code_vec, subgraph_vec, + flatbuffer_builder, builtin_code_map, stringStream.str()}; + + cook_graph<::circlechef::Graph>(graph, cp); + } + + // Create "Model" arguments + auto buffers = flatbuffer_builder->CreateVector(buffer_vec); + auto operator_codes = flatbuffer_builder->CreateVector(code_vec); + auto subgraphs = flatbuffer_builder->CreateVector(subgraph_vec); + auto description = flatbuffer_builder->CreateString("Generated by circlechef"); + + // Create "Model" + circle::ModelBuilder model_builder{*flatbuffer_builder}; + + model_builder.add_version(3); + model_builder.add_operator_codes(operator_codes); + model_builder.add_subgraphs(subgraphs); + model_builder.add_description(description); + model_builder.add_buffers(buffers); + + auto model = model_builder.Finish(); + + // Finalize + ::circle::FinishModelBuffer(*flatbuffer_builder, model); + + // Return "GenerateModel" + return GeneratedModel{ + std::unique_ptr<GeneratedModelImpl>(new GeneratedModelImpl(std::move(flatbuffer_builder)))}; +} + +} // namespace circlechef diff --git a/compiler/circlechef/core/src/Op/BCQFullyConnected.cpp b/compiler/circlechef/core/src/Op/BCQFullyConnected.cpp new file mode 100644 index 000000000..4c82c52cc --- /dev/null +++ b/compiler/circlechef/core/src/Op/BCQFullyConnected.cpp @@ -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. + */ + +#include "BCQFullyConnected.h" + +#include "Convert.h" + +flatbuffers::Offset<void> BCQFullyConnectedChef::value(flatbuffers::FlatBufferBuilder &fbb) const +{ + auto &operation = (*_operation); + + assert(operation.has_bcq_fully_connected_options()); + + circle::BCQFullyConnectedOptionsBuilder bcq_fully_connected_options_builder{fbb}; + bcq_fully_connected_options_builder.add_weights_hidden_size( + operation.bcq_fully_connected_options().weights_hidden_size()); + bcq_fully_connected_options_builder.add_fused_activation_function( + as_circle_activation(operation.bcq_fully_connected_options().activation())); + + return bcq_fully_connected_options_builder.Finish().Union(); +} + +std::unique_ptr<OpChef> +BCQFullyConnectedChefFactory::create(const circlechef::Operation *operation) const +{ + return std::unique_ptr<OpChef>{new BCQFullyConnectedChef{operation}}; +} diff --git a/compiler/circlechef/core/src/Op/BCQFullyConnected.h b/compiler/circlechef/core/src/Op/BCQFullyConnected.h new file mode 100644 index 000000000..41e6b53d5 --- /dev/null +++ b/compiler/circlechef/core/src/Op/BCQFullyConnected.h @@ -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. + */ + +#ifndef __OP_BCQFULLYCONNECTED_H__ +#define __OP_BCQFULLYCONNECTED_H__ + +#include "OpChef.h" + +class BCQFullyConnectedChef final : public OpChef +{ +public: + explicit BCQFullyConnectedChef(const circlechef::Operation *operation) : _operation{operation} + { + // DO NOTHING + } + +public: + circle::BuiltinOperator code(void) const override + { + return circle::BuiltinOperator_BCQ_FULLY_CONNECTED; + } + + circle::BuiltinOptions type(void) const override + { + return circle::BuiltinOptions_BCQFullyConnectedOptions; + } + + flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override; + +private: + const circlechef::Operation *_operation; +}; + +struct BCQFullyConnectedChefFactory final : public OpChefFactory +{ + std::unique_ptr<OpChef> create(const circlechef::Operation *operation) const override; +}; + +#endif // __OP_BCQFULLYCONNECTED_H__ diff --git a/compiler/circlechef/core/src/Op/BCQGather.cpp b/compiler/circlechef/core/src/Op/BCQGather.cpp new file mode 100644 index 000000000..08f6f611f --- /dev/null +++ b/compiler/circlechef/core/src/Op/BCQGather.cpp @@ -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. + */ + +#include "BCQGather.h" + +flatbuffers::Offset<void> BCQGatherChef::value(flatbuffers::FlatBufferBuilder &fbb) const +{ + auto &operation = (*_operation); + + assert(operation.has_bcq_gather_options()); + + circle::BCQGatherOptionsBuilder bcq_gather_options_builder{fbb}; + bcq_gather_options_builder.add_input_hidden_size( + operation.bcq_gather_options().input_hidden_size()); + bcq_gather_options_builder.add_axis(operation.bcq_gather_options().axis()); + + return bcq_gather_options_builder.Finish().Union(); +} + +std::unique_ptr<OpChef> BCQGatherChefFactory::create(const circlechef::Operation *operation) const +{ + return std::unique_ptr<OpChef>{new BCQGatherChef{operation}}; +} diff --git a/compiler/circlechef/core/src/Op/BCQGather.h b/compiler/circlechef/core/src/Op/BCQGather.h new file mode 100644 index 000000000..24a797a41 --- /dev/null +++ b/compiler/circlechef/core/src/Op/BCQGather.h @@ -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. + */ + +#ifndef __OP_BCQGATHER_H__ +#define __OP_BCQGATHER_H__ + +#include "OpChef.h" + +class BCQGatherChef final : public OpChef +{ +public: + explicit BCQGatherChef(const circlechef::Operation *operation) : _operation{operation} + { + // DO NOTHING + } + +public: + circle::BuiltinOperator code(void) const override { return circle::BuiltinOperator_BCQ_GATHER; } + + circle::BuiltinOptions type(void) const override + { + return circle::BuiltinOptions_BCQGatherOptions; + } + + flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override; + +private: + const circlechef::Operation *_operation; +}; + +struct BCQGatherChefFactory final : public OpChefFactory +{ + std::unique_ptr<OpChef> create(const circlechef::Operation *operation) const override; +}; + +#endif // __OP_BCQGATHER_H__ diff --git a/compiler/circlechef/core/src/Op/BatchMatMul.cpp b/compiler/circlechef/core/src/Op/BatchMatMul.cpp new file mode 100644 index 000000000..d98c0801a --- /dev/null +++ b/compiler/circlechef/core/src/Op/BatchMatMul.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 "BatchMatMul.h" + +flatbuffers::Offset<void> BatchMatMulChef::value(flatbuffers::FlatBufferBuilder &fbb) const +{ + auto &operation = (*_operation); + + assert(operation.has_batch_matmul_options()); + + circle::BatchMatMulOptionsBuilder batch_matmul_options_options_builder{fbb}; + batch_matmul_options_options_builder.add_adjoint_lhs( + operation.batch_matmul_options().adjoint_lhs()); + batch_matmul_options_options_builder.add_adjoint_rhs( + operation.batch_matmul_options().adjoint_rhs()); + + return batch_matmul_options_options_builder.Finish().Union(); +} + +std::unique_ptr<OpChef> BatchMatMulChefFactory::create(const circlechef::Operation *operation) const +{ + return std::unique_ptr<OpChef>{new BatchMatMulChef{operation}}; +} diff --git a/compiler/circlechef/core/src/Op/BatchMatMul.h b/compiler/circlechef/core/src/Op/BatchMatMul.h new file mode 100644 index 000000000..fbb411eff --- /dev/null +++ b/compiler/circlechef/core/src/Op/BatchMatMul.h @@ -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. + */ + +#ifndef __OP_BATCH_MATMUL_H__ +#define __OP_BATCH_MATMUL_H__ + +#include "OpChef.h" + +class BatchMatMulChef final : public OpChef +{ +public: + explicit BatchMatMulChef(const circlechef::Operation *operation) : _operation{operation} + { + // DO NOTHING + } + +public: + circle::BuiltinOperator code(void) const override { return circle::BuiltinOperator_BATCH_MATMUL; } + + circle::BuiltinOptions type(void) const override + { + return circle::BuiltinOptions_BatchMatMulOptions; + } + + flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override; + +private: + const circlechef::Operation *_operation; +}; + +struct BatchMatMulChefFactory final : public OpChefFactory +{ + std::unique_ptr<OpChef> create(const circlechef::Operation *operation) const override; +}; + +#endif // __OP_BATCH_MATMUL_H__ diff --git a/compiler/circlechef/core/src/Op/InstanceNorm.cpp b/compiler/circlechef/core/src/Op/InstanceNorm.cpp new file mode 100644 index 000000000..115eceffc --- /dev/null +++ b/compiler/circlechef/core/src/Op/InstanceNorm.cpp @@ -0,0 +1,39 @@ +/* + * 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 "InstanceNorm.h" + +#include "Convert.h" + +flatbuffers::Offset<void> InstanceNormChef::value(flatbuffers::FlatBufferBuilder &fbb) const +{ + auto &operation = (*_operation); + + assert(operation.has_instance_norm_options()); + auto circle_activation = as_circle_activation(operation.instance_norm_options().activation()); + + circle::InstanceNormOptionsBuilder options_builder{fbb}; + options_builder.add_epsilon(operation.instance_norm_options().epsilon()); + options_builder.add_fused_activation_function(circle_activation); + + return options_builder.Finish().Union(); +} + +std::unique_ptr<OpChef> +InstanceNormChefFactory::create(const circlechef::Operation *operation) const +{ + return std::unique_ptr<OpChef>{new InstanceNormChef{operation}}; +} diff --git a/compiler/circlechef/core/src/Op/InstanceNorm.h b/compiler/circlechef/core/src/Op/InstanceNorm.h new file mode 100644 index 000000000..f36b5d7b9 --- /dev/null +++ b/compiler/circlechef/core/src/Op/InstanceNorm.h @@ -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. + */ + +#ifndef __OP_INSTANCE_NORM_H__ +#define __OP_INSTANCE_NORM_H__ + +#include "OpChef.h" + +class InstanceNormChef final : public OpChef +{ +public: + explicit InstanceNormChef(const circlechef::Operation *operation) : _operation{operation} + { + // DO NOTHING + } + +public: + circle::BuiltinOperator code(void) const override + { + return circle::BuiltinOperator_INSTANCE_NORM; + } + + circle::BuiltinOptions type(void) const override + { + return circle::BuiltinOptions_InstanceNormOptions; + } + + flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override; + +private: + const circlechef::Operation *_operation; +}; + +struct InstanceNormChefFactory final : public OpChefFactory +{ + std::unique_ptr<OpChef> create(const circlechef::Operation *operation) const override; +}; + +#endif // __OP_INSTANCE_NORM_H__ diff --git a/compiler/circlechef/core/src/OpChef.def b/compiler/circlechef/core/src/OpChef.def new file mode 100644 index 000000000..3128d3ba2 --- /dev/null +++ b/compiler/circlechef/core/src/OpChef.def @@ -0,0 +1,10 @@ +#ifndef OP_CHEF +#error "Define OP first" +#endif // OP_CHEF + +// Please keep the list in alphabetical order +// OP_CHEF(NAME, FACTORY_CLASS) +OP_CHEF(BatchMatMul, BatchMatMulChefFactory) +OP_CHEF(BCQFullyConnected, BCQFullyConnectedChefFactory) +OP_CHEF(BCQGather, BCQGatherChefFactory) +OP_CHEF(InstanceNorm, InstanceNormChefFactory) diff --git a/compiler/circlechef/core/src/OpChef.h b/compiler/circlechef/core/src/OpChef.h new file mode 100644 index 000000000..3479e51ef --- /dev/null +++ b/compiler/circlechef/core/src/OpChef.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 __OP_CHEF_H__ +#define __OP_CHEF_H__ + +#include <circlechef.pb.h> +#include <mio/circle/schema_generated.h> + +#include <memory> + +struct OpChef +{ + virtual ~OpChef() = default; + + virtual circle::BuiltinOperator code(void) const = 0; + virtual circle::BuiltinOptions type(void) const = 0; + virtual flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const = 0; + + // TODO Find a way to place this method in a better place + virtual flatbuffers::Offset<flatbuffers::Vector<uint8_t>> + custom_value(flatbuffers::FlatBufferBuilder &fbb) const + { + return flatbuffers::Offset<flatbuffers::Vector<uint8_t>>(); + } +}; + +struct OpChefFactory +{ + virtual ~OpChefFactory() = default; + + virtual std::unique_ptr<OpChef> create(const circlechef::Operation *operation) const = 0; +}; + +#endif // __OP_CHEF_H__ diff --git a/compiler/circlechef/core/src/OpChefs.h b/compiler/circlechef/core/src/OpChefs.h new file mode 100644 index 000000000..e13c5e0c6 --- /dev/null +++ b/compiler/circlechef/core/src/OpChefs.h @@ -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. + */ + +#ifndef __OP_CHEFS_H__ +#define __OP_CHEFS_H__ + +#include "Op/BatchMatMul.h" +#include "Op/BCQFullyConnected.h" +#include "Op/BCQGather.h" +#include "Op/InstanceNorm.h" + +#endif // __OP_CHEFS_H__ diff --git a/compiler/circlechef/log/CMakeLists.txt b/compiler/circlechef/log/CMakeLists.txt new file mode 100644 index 000000000..6527ca7d8 --- /dev/null +++ b/compiler/circlechef/log/CMakeLists.txt @@ -0,0 +1,7 @@ +# TODO Find how to test logging framework +file(GLOB_RECURSE SOURCES "src/*.cpp") + +add_library(circlechef_log STATIC ${SOURCES}) +target_include_directories(circlechef_log PUBLIC include) +target_link_libraries(circlechef_log PUBLIC hermes) +target_link_libraries(circlechef_log PRIVATE hermes_std) diff --git a/compiler/circlechef/log/include/Log.h b/compiler/circlechef/log/include/Log.h new file mode 100644 index 000000000..ef00b26cc --- /dev/null +++ b/compiler/circlechef/log/include/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 __CIRCLECHEF_LOG_H__ +#define __CIRCLECHEF_LOG_H__ + +#include <hermes.h> + +namespace circlechef +{ + +/** + * @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 CIRCLECHEF_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 circlechef + +#include "LoggingContext.h" + +/** + * HOW TO USE: + * + * LOGGER(l); + * + * INFO(l) << "Hello, World" << std::endl; + * + */ +#define LOGGER(name) ::circlechef::Logger name{::circlechef::LoggingContext::get()}; + +// TODO Support FATAL, ERROR, WARN, and VERBOSE +#define INFO(name) HERMES_INFO(name) + +// WARNING! +// +// THE CURRENT IMPLEMENTATION IS NOT THREAD SAFE. +// + +#endif // __CIRCLECHEF_LOG_H__ diff --git a/compiler/circlechef/log/include/LoggingContext.h b/compiler/circlechef/log/include/LoggingContext.h new file mode 100644 index 000000000..1282cfd45 --- /dev/null +++ b/compiler/circlechef/log/include/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 __CIRCLECHEF_LOGGING_CONTEXT_H__ +#define __CIRCLECHEF_LOGGING_CONTEXT_H__ + +#include <hermes.h> + +namespace circlechef +{ + +/** + * @brief Global logging context + */ +struct LoggingContext +{ + static hermes::Context *get(void); +}; + +} // namespace circlechef + +#endif // __CIRCLECHEF_LOGGING_CONTEXT_H__ diff --git a/compiler/circlechef/log/src/Log.cpp b/compiler/circlechef/log/src/Log.cpp new file mode 100644 index 000000000..11a60fb8d --- /dev/null +++ b/compiler/circlechef/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 "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 circlechef +{ + +Logger::Logger(hermes::Context *ctx) { activate(ctx->sources(), ctx->bus()); } +Logger::~Logger() { deactivate(); } + +} // namespace circlechef + +// +// LoggerConfig +// +namespace circlechef +{ + +LoggerConfig::LoggerConfig() +{ + // Turn on logging if CIRCLECHEF_LOG is set as non-zero value + _enabled = safecast<bool>(std::getenv("CIRCLECHEF_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 circlechef diff --git a/compiler/circlechef/log/src/LoggingContext.cpp b/compiler/circlechef/log/src/LoggingContext.cpp new file mode 100644 index 000000000..b64bd3f3d --- /dev/null +++ b/compiler/circlechef/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 "LoggingContext.h" +#include "Log.h" + +#include <hermes/ConsoleReporter.h> + +#include <memory> + +namespace circlechef +{ + +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 circlechef diff --git a/compiler/circlechef/proto/CMakeLists.txt b/compiler/circlechef/proto/CMakeLists.txt new file mode 100644 index 000000000..3cc26de84 --- /dev/null +++ b/compiler/circlechef/proto/CMakeLists.txt @@ -0,0 +1,5 @@ +Protobuf_Generate(CIRCLECHEF_PROTO "${CMAKE_CURRENT_BINARY_DIR}/generated" "${CMAKE_CURRENT_SOURCE_DIR}" "circlechef.proto") + +add_library(circlechef_proto STATIC ${CIRCLECHEF_PROTO_SOURCES}) +target_include_directories(circlechef_proto PUBLIC ${CIRCLECHEF_PROTO_INCLUDE_DIRS}) +target_link_libraries(circlechef_proto libprotobuf) diff --git a/compiler/circlechef/proto/circlechef.proto b/compiler/circlechef/proto/circlechef.proto new file mode 100644 index 000000000..b8c009b38 --- /dev/null +++ b/compiler/circlechef/proto/circlechef.proto @@ -0,0 +1,110 @@ +syntax = "proto2"; + +package circlechef; + +// +// Initial version +// - Our initial version +// +// Version 1 +// - Backward compatible with Initial version +// - Added Graph to represent sub graphs +// - Added name, version(default as 1), graph in ModelRecipe +// + +// This enum value corresponds to TensorType in TensorFlow Lite schema +enum TensorType { + FLOAT32 = 0; + INT32 = 2; + UINT8 = 3; + INT64 = 4; + BOOL = 6; +} + +message TensorShape { + repeated uint32 dim = 3; +} + +message TensorFiller { + optional string tag = 1; + repeated string arg = 2; +} + +message TensorQuantization { + repeated float min = 1; + repeated float max = 2; + repeated float scale = 3; + repeated int64 zero_point = 4; +} + +message Operand { + optional string name = 1; + optional TensorType type = 2; + optional TensorShape shape = 3; + optional TensorFiller filler = 4; + optional TensorQuantization quant = 5; +} + +// This enum value corresponds to Padding in TensorFlow Lite schema +enum Padding { + SAME = 0; + VALID = 1; +} + +// This enum value corresponds to ActivationFunctionType in TensorFlow Lite schema +enum Activation { + NONE = 0; + RELU = 1; + RELU6 = 3; +} + +message BatchMatMulOptions { + optional bool adjoint_lhs = 1 [default = false]; + optional bool adjoint_rhs = 2 [default = false]; +} + +message InstanceNormOptions { + optional float epsilon = 1 [default = 1e-05]; + optional Activation activation = 2 [default = NONE]; +} + +message BCQFullyConnectedOptions { + optional int32 weights_hidden_size = 1 [default = 0]; + optional Activation activation = 2 [default = NONE]; +} + +message BCQGatherOptions { + optional int32 input_hidden_size = 1 [default = 0]; + optional int32 axis = 2 [default = 0]; +} + +message Operation { + optional string type = 1; + repeated string input = 2; + repeated string output = 3; + optional int32 version = 4 [default = 1]; + + optional BatchMatMulOptions batch_matmul_options = 100; + optional InstanceNormOptions instance_norm_options = 101; + optional BCQFullyConnectedOptions bcq_fully_connected_options = 102; + optional BCQGatherOptions bcq_gather_options = 103; +} + +// For additional subgraphs +message Graph { + repeated Operand operand = 1; + repeated Operation operation = 2; + repeated string input = 3; + repeated string output = 4; + optional string name = 5; +} + +message ModelRecipe { + repeated Operand operand = 1; + repeated Operation operation = 2; + repeated string input = 3; + repeated string output = 4; + optional string name = 5; + optional uint32 version = 6 [default = 1]; + repeated Graph graph = 7; +} diff --git a/compiler/circlechef/requires.cmake b/compiler/circlechef/requires.cmake new file mode 100644 index 000000000..2106146d7 --- /dev/null +++ b/compiler/circlechef/requires.cmake @@ -0,0 +1,9 @@ +require("arser") +require("nnkit") +require("cwrap") +require("mio-circle") +require("safemain") +require("hermes") +require("hermes-std") +require("foder") +require("souschef") diff --git a/compiler/circlechef/tests/CMakeLists.txt b/compiler/circlechef/tests/CMakeLists.txt new file mode 100644 index 000000000..4dc58addf --- /dev/null +++ b/compiler/circlechef/tests/CMakeLists.txt @@ -0,0 +1,70 @@ +nncc_find_resource(CircleRecipes) +set(CIRCLERECIPES_DIR "${CircleRecipes_DIR}") + +file(GLOB RECIPES RELATIVE ${CIRCLERECIPES_DIR} "${CIRCLERECIPES_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}.circle") + + # Copy .recipe + add_custom_command(OUTPUT ${RECIPE_SOURCE_FILE} + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${CIRCLERECIPES_DIR}/${RECIPE}" ${RECIPE_SOURCE_FILE} + DEPENDS "${CIRCLERECIPES_DIR}/${RECIPE}" + COMMENT "Generating ${RECIPE_SOURCE_FILE}") + + # Generate .circle + add_custom_command(OUTPUT ${RECIPE_OUTPUT_FILE} + COMMAND circlechef-file ${RECIPE_SOURCE_FILE} ${RECIPE_OUTPUT_FILE} + DEPENDS circlechef-file ${RECIPE_SOURCE_FILE} + COMMENT "Generating ${RECIPE_OUTPUT_FILE}") + + list(APPEND TESTS ${RECIPE_PREFIX}) + list(APPEND TESTFILES ${RECIPE_OUTPUT_FILE}) +endforeach(RECIPE) + +#Test circlechef-reverse +file(GLOB GEN_CIRCLEFILES RELATIVE ${CIRCLERECIPES_DIR} "${CIRCLERECIPES_DIR}/*/test.reverse") +# Note: While in development, circlechef-reverse may not handle the operator. +# To separate this linkage scan empty test.reverse for test targets for circlechef-reverse. + +foreach(CIRCLEFILE IN ITEMS ${GEN_CIRCLEFILES}) + get_filename_component(CIRCLE_PREFIX ${CIRCLEFILE} DIRECTORY) + + # file from above circlechef-file block + # use circle file as input of circlechef-reverse generated from circlechef-file + set(RECIPE_OUTPUT_FILE "${CIRCLE_PREFIX}.circle") + set(RECIPE_GEN_OUTPUT_FILE "${CIRCLE_PREFIX}.gen.recipe") + set(RECIPE_GEN_OUTPUT_FILE2 "${CIRCLE_PREFIX}.gen.circle") + + # Generate .gen.recipe from generated .circle + add_custom_command(OUTPUT ${RECIPE_GEN_OUTPUT_FILE} + COMMAND circlechef-reverse ${RECIPE_OUTPUT_FILE} ${RECIPE_GEN_OUTPUT_FILE} + DEPENDS circlechef-reverse ${RECIPE_OUTPUT_FILE} + COMMENT "Generating ${RECIPE_GEN_OUTPUT_FILE}") + + # now we are going to generate .gen.circle from .gen.recipe + # to check generated .gen.recipe file is correct by using it. + # as weight values may be different, binary comparision is not acceptable. + add_custom_command(OUTPUT ${RECIPE_GEN_OUTPUT_FILE2} + COMMAND circlechef-file ${RECIPE_GEN_OUTPUT_FILE} ${RECIPE_GEN_OUTPUT_FILE2} + DEPENDS circlechef-file ${RECIPE_GEN_OUTPUT_FILE} + COMMENT "Generating ${RECIPE_GEN_OUTPUT_FILE2}") + + list(APPEND TESTS ${CIRCLE_PREFIX}.gen) + list(APPEND TESTFILES ${RECIPE_GEN_OUTPUT_FILE2}) +endforeach(CIRCLEFILE) + +# Add a dummy target to create a target-level dependency. +# TODO Find a way to create a dependency between circlechef_test and generated testfiles. +add_custom_target(circlechef_testfiles ALL DEPENDS ${TESTFILES}) + +# Using circle_verify for temporary as it only calls flatbuffer validate +# TODO do testing with running the model with runtime/interpreter +add_test(NAME circlechef_test + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/runvalidate.sh" + $<TARGET_FILE:circle-verify> + ${TESTS}) diff --git a/compiler/circlechef/tests/runvalidate.sh b/compiler/circlechef/tests/runvalidate.sh new file mode 100755 index 000000000..46ad125ae --- /dev/null +++ b/compiler/circlechef/tests/runvalidate.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +if [[ $# -le 2 ]]; then + echo "USAGE: $0 [circle-verify path] [prefix 0] " + exit 255 +fi + +CIRCLE_VERIFY_PATH="$1"; shift + +echo "-- Found circle-verify: ${CIRCLE_VERIFY_PATH}" + +TESTED=() +PASSED=() +FAILED=() + +pushd "${WORKDIR}" +while [[ $# -ne 0 ]]; do + PREFIX="$1"; shift + + TESTED+=("${PREFIX}") + + PASSED_TAG="${PREFIX}.passed" + + rm -f "${PASSED_TAG}" + + cat > "${PREFIX}.log" <( + exec 2>&1 + + echo "'${CIRCLE_VERIFY_PATH}' '${PREFIX}.circle'" + "${CIRCLE_VERIFY_PATH}" "${PREFIX}.circle" + + if [[ $? -eq 0 ]]; then + touch "${PASSED_TAG}" + fi + ) + + if [[ -f "${PASSED_TAG}" ]]; then + PASSED+=("$PREFIX") + else + FAILED+=("$PREFIX") + fi +done +popd + +echo "SUMMARY: ${#PASSED[@]} PASS AND ${#FAILED[@]} FAIL AMONG ${#TESTED[@]} TESTS" + +if [[ ${#TESTED[@]} -ne ${#PASSED[@]} ]]; then + echo "FAILED" + for TEST in "${FAILED[@]}" + do + echo "- ${TEST}" + done + exit 255 +fi + +exit 0 diff --git a/compiler/circlechef/tools/CMakeLists.txt b/compiler/circlechef/tools/CMakeLists.txt new file mode 100644 index 000000000..c958614b2 --- /dev/null +++ b/compiler/circlechef/tools/CMakeLists.txt @@ -0,0 +1,6 @@ +# Console-based tool (circlechef) +add_subdirectory(console) +# File-based tool (circlechef-file) +add_subdirectory(file) +# Reverse tool to generate recipe from circle (circlechef-reverse) +add_subdirectory(reverse) diff --git a/compiler/circlechef/tools/console/CMakeLists.txt b/compiler/circlechef/tools/console/CMakeLists.txt new file mode 100644 index 000000000..10168fca3 --- /dev/null +++ b/compiler/circlechef/tools/console/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(circlechef Driver.cpp) +target_link_libraries(circlechef circlechef_core) +target_link_libraries(circlechef safemain) diff --git a/compiler/circlechef/tools/console/Driver.cpp b/compiler/circlechef/tools/console/Driver.cpp new file mode 100644 index 000000000..0909f5927 --- /dev/null +++ b/compiler/circlechef/tools/console/Driver.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 "circlechef/ModelChef.h" + +#include <google/protobuf/io/coded_stream.h> +#include <google/protobuf/io/zero_copy_stream_impl.h> +#include <google/protobuf/text_format.h> + +#include <iostream> + +int entry(int argc, char **argv) +{ + int32_t model_version = 1; + + ::circlechef::ModelRecipe model_recipe; + + // Read a model recipe from standard input + { + google::protobuf::io::IstreamInputStream iis{&std::cin}; + if (!google::protobuf::TextFormat::Parse(&iis, &model_recipe)) + { + std::cerr << "ERROR: Failed to parse recipe" << std::endl; + return 255; + } + + if (model_recipe.has_version()) + { + model_version = model_recipe.version(); + } + } + + if (model_version > 1) + { + std::cerr << "ERROR: Unsupported recipe version: " << model_version << std::endl; + return 255; + } + + auto generated_model = circlechef::cook(model_recipe); + + // Write a generated model into standard output + std::cout.write(generated_model.base(), generated_model.size()); + + return 0; +} diff --git a/compiler/circlechef/tools/file/CMakeLists.txt b/compiler/circlechef/tools/file/CMakeLists.txt new file mode 100644 index 000000000..2524a657c --- /dev/null +++ b/compiler/circlechef/tools/file/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(circlechef-file Driver.cpp) +target_link_libraries(circlechef-file arser) +target_link_libraries(circlechef-file circlechef_core) +target_link_libraries(circlechef-file safemain) diff --git a/compiler/circlechef/tools/file/Driver.cpp b/compiler/circlechef/tools/file/Driver.cpp new file mode 100644 index 000000000..a15da4002 --- /dev/null +++ b/compiler/circlechef/tools/file/Driver.cpp @@ -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. + */ + +#include "circlechef/ModelChef.h" + +#include <google/protobuf/io/coded_stream.h> +#include <google/protobuf/io/zero_copy_stream_impl.h> +#include <google/protobuf/text_format.h> + +#include <arser/arser.h> + +#include <fstream> +#include <iostream> + +int entry(int argc, char **argv) +{ + arser::Arser arser; + arser.add_argument("recipe") + .type(arser::DataType::STR) + .help("Source recipe file path to convert"); + arser.add_argument("circle").type(arser::DataType::STR).help("Target circle file path"); + + try + { + arser.parse(argc, argv); + } + catch (const std::runtime_error &err) + { + std::cout << err.what() << std::endl; + std::cout << arser; + return 0; + } + + int32_t model_version = 1; + + ::circlechef::ModelRecipe model_recipe; + + std::string recipe_path = arser.get<std::string>("recipe"); + // Load model recipe from a file + { + std::ifstream is{recipe_path}; + google::protobuf::io::IstreamInputStream iis{&is}; + if (!google::protobuf::TextFormat::Parse(&iis, &model_recipe)) + { + std::cerr << "ERROR: Failed to parse recipe '" << recipe_path << "'" << std::endl; + return 255; + } + + if (model_recipe.has_version()) + { + model_version = model_recipe.version(); + } + } + + if (model_version > 1) + { + std::cerr << "ERROR: Unsupported recipe version: " << model_version << ", '" << recipe_path + << "'" << std::endl; + return 255; + } + + auto generated_model = circlechef::cook(model_recipe); + + std::string circle_path = arser.get<std::string>("circle"); + // Dump generated model into a file + { + std::ofstream os{circle_path, std::ios::binary}; + os.write(generated_model.base(), generated_model.size()); + } + + return 0; +} diff --git a/compiler/circlechef/tools/reverse/CMakeLists.txt b/compiler/circlechef/tools/reverse/CMakeLists.txt new file mode 100644 index 000000000..a1606c94e --- /dev/null +++ b/compiler/circlechef/tools/reverse/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(circlechef-reverse Driver.cpp) +target_link_libraries(circlechef-reverse arser) +target_link_libraries(circlechef-reverse circlechef_circle) +target_link_libraries(circlechef-reverse safemain) +target_link_libraries(circlechef-reverse foder) diff --git a/compiler/circlechef/tools/reverse/Driver.cpp b/compiler/circlechef/tools/reverse/Driver.cpp new file mode 100644 index 000000000..9c0b9ea24 --- /dev/null +++ b/compiler/circlechef/tools/reverse/Driver.cpp @@ -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. + */ + +#include <circlechef/RecipeChef.h> + +#include <arser/arser.h> +#include <foder/FileLoader.h> + +#include <memory> +#include <iostream> + +int entry(int argc, char **argv) +{ + arser::Arser arser; + arser.add_argument("circle") + .type(arser::DataType::STR) + .help("Source circle file path to convert"); + arser.add_argument("recipe").type(arser::DataType::STR).help("Target recipe file path"); + + try + { + arser.parse(argc, argv); + } + catch (const std::runtime_error &err) + { + std::cout << err.what() << std::endl; + std::cout << arser; + return 0; + } + + std::string circle_path = arser.get<std::string>("circle"); + // Load TF lite model from a circle file + const foder::FileLoader fileLoader{circle_path}; + std::vector<char> modelData = fileLoader.load(); + const circle::Model *circlemodel = circle::GetModel(modelData.data()); + if (circlemodel == nullptr) + { + std::cerr << "ERROR: Failed to load circle '" << circle_path << "'" << std::endl; + return 255; + } + + // Generate ModelRecipe recipe + std::unique_ptr<circlechef::ModelRecipe> recipe = circlechef::generate_recipe(circlemodel); + if (recipe.get() == nullptr) + { + std::cerr << "ERROR: Failed to generate recipe" << std::endl; + return 255; + } + + std::string recipe_path = arser.get<std::string>("recipe"); + // Save to a file + bool result = circlechef::write_recipe(recipe_path, recipe); + if (!result) + { + std::cerr << "ERROR: Failed to write to recipe '" << recipe_path << "'" << std::endl; + return 255; + } + return 0; +} |