diff options
author | Chunseok Lee <chunseok.lee@samsung.com> | 2020-04-23 14:45:49 +0900 |
---|---|---|
committer | Chunseok Lee <chunseok.lee@samsung.com> | 2020-04-23 14:45:49 +0900 |
commit | e2ef8438a24f7c56a0744eb579a6e293ee2fbf8e (patch) | |
tree | 44a1a7951d168dd4370e13593ed03f4bc6d920c5 /compiler/moco-tf | |
parent | 302e6564a7a76109e1178207e44e45a58631c477 (diff) | |
download | nnfw-e2ef8438a24f7c56a0744eb579a6e293ee2fbf8e.tar.gz nnfw-e2ef8438a24f7c56a0744eb579a6e293ee2fbf8e.tar.bz2 nnfw-e2ef8438a24f7c56a0744eb579a6e293ee2fbf8e.zip |
Imported Upstream version 1.4.0upstream/1.4.0submit/tizen/20200423.054851
Diffstat (limited to 'compiler/moco-tf')
105 files changed, 8029 insertions, 0 deletions
diff --git a/compiler/moco-tf/CMakeLists.txt b/compiler/moco-tf/CMakeLists.txt new file mode 100644 index 000000000..5516388a4 --- /dev/null +++ b/compiler/moco-tf/CMakeLists.txt @@ -0,0 +1,51 @@ +if(NOT TARGET mio_tf) + return() +endif(NOT TARGET mio_tf) + +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(moco_tf_frontend SHARED ${SOURCES}) +target_include_directories(moco_tf_frontend PRIVATE src) +target_include_directories(moco_tf_frontend PUBLIC include) +target_link_libraries(moco_tf_frontend PUBLIC loco) +target_link_libraries(moco_tf_frontend PUBLIC moco_lang) +target_link_libraries(moco_tf_frontend PUBLIC moco_import) +target_link_libraries(moco_tf_frontend PUBLIC moco_pass) +target_link_libraries(moco_tf_frontend PUBLIC mio_tf) +target_link_libraries(moco_tf_frontend PRIVATE moco_service) +target_link_libraries(moco_tf_frontend PRIVATE moco_support) +target_link_libraries(moco_tf_frontend PRIVATE bino) +target_link_libraries(moco_tf_frontend PRIVATE fipe) +target_link_libraries(moco_tf_frontend PRIVATE locop) +target_link_libraries(moco_tf_frontend PRIVATE stdex) +target_link_libraries(moco_tf_frontend PRIVATE moco_log) +target_link_libraries(moco_tf_frontend PRIVATE pepper_str) +target_link_libraries(moco_tf_frontend PRIVATE pepper_strcast) +target_link_libraries(moco_tf_frontend PRIVATE locomotiv) +target_link_libraries(moco_tf_frontend PRIVATE plier_tf) +target_link_libraries(moco_tf_frontend PRIVATE locoex_customop) +target_link_libraries(moco_tf_frontend PRIVATE logo) +target_link_libraries(moco_tf_frontend PRIVATE oops) +install(TARGETS moco_tf_frontend DESTINATION lib) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +add_executable(moco_tf_frontend_test ${TESTS}) +target_include_directories(moco_tf_frontend_test PRIVATE src) +target_link_libraries(moco_tf_frontend_test gtest_main) +target_link_libraries(moco_tf_frontend_test bino) +target_link_libraries(moco_tf_frontend_test fipe) +target_link_libraries(moco_tf_frontend_test locop) +target_link_libraries(moco_tf_frontend_test moco_log) +target_link_libraries(moco_tf_frontend_test moco_tf_frontend) +target_link_libraries(moco_tf_frontend_test stdex) +target_link_libraries(moco_tf_frontend_test plier_tf) +target_link_libraries(moco_tf_frontend_test locoex_customop) +target_link_libraries(moco_tf_frontend_test logo) +add_test(moco_tf_frontend_test moco_tf_frontend_test) diff --git a/compiler/moco-tf/README.md b/compiler/moco-tf/README.md new file mode 100644 index 000000000..add1159e1 --- /dev/null +++ b/compiler/moco-tf/README.md @@ -0,0 +1,57 @@ +# moco-tf + +_moco-tf_ translates a TensorFlow model into _loco_ + +## Purpose + +_moco-tf_ is to convert TensorFlow generated model file to in-memory _loco_ IR Graph. + +## How to use + +```cxx +#include <moco/tf/Frontend.h> + +... + + ::moco::tf::Frontend moco; + + std::string pb_path = "path_to_pb_file_to_load"; + + auto loco_graph = moco.load(sig, pb_path, ::moco::tf::Frontend::FileType::Binary); +``` + +## Dependency + +Please refer [requires.cmake](./requires.cmake) for dependant modules. + +## Naming rules + +### TensorFlow node names + +Use `REGISTER_OP` argument used in TensorFlow source `core` folder. + +``` +cd tensorflow/core +grep -Rn "REGISTER_OP" +``` + +To see single Op, `Conv2D` for example +``` +cd tensorflow/core +grep -Rn "REGISTER_OP" | grep "Conv2D" +``` + +### Names related with TensorFlow nodes + +Like `GraphBuilder` and `Canonicalization`, TensorFlow node names can be used as +prefix or suffix. + +- `Conv2DGraphBuilder` +- `Conv2DCanonicalizier` + +### TensorFlow Dialect IR + +Use `TF` prefix with TensorFlow Dialect node names + +- `TFAvgPool` +- `TFConv2D` diff --git a/compiler/moco-tf/doc/Conversion.md b/compiler/moco-tf/doc/Conversion.md new file mode 100644 index 000000000..08551cc3c --- /dev/null +++ b/compiler/moco-tf/doc/Conversion.md @@ -0,0 +1,140 @@ +This document outlines how to express each TensorFlow operation on top of _loco_ + +**CAUTION** All the python examples below are written in Python 3 with TensorFlow v1.13. + +**DISCLAIMER** _loco_ does not support named values, but all the below _loco_ examples assign "name" to each value to make it easy to read. + +### Placeholder + +**Placeholder** in _TensorFlow_ corresponds to **Pull** in _loco_. + +_Python_: +```python +import tensorflow as tf +input = tf.placeholder(dtype=tf.float32, shape=[3, 4], name='input') +print(tf.get_default_graph().as_graph_def()) +``` + +API reference: [tf.placeholder](https://www.tensorflow.org/versions/r1.13/api_docs/python/tf) + +_TensorFlow_ +```prototext +node { + name: "input" + op: "Placeholder" + attr { + key: "dtype" + value { type: DT_FLOAT } + } + attr { + key: "shape" + value { + shape { + dim { size: 3 } + dim { size: 4 } + } + } + } +} +``` + +_loco_: +``` +%input = Pull(dtype: FLOAT32, shape: [3, 4]) +Push(%input) +``` + +### Identity + +**Identity** in _TensorFlow_ corresponds to **Forward** in _loco_. + +_Python_: +```python +import tensorflow as tf +input = tf.placeholder(dtype=tf.float32, shape=[3, 4]) +ident = tf.identity(input) +print(tf.get_default_graph().as_graph_def()) +``` + +API reference: [tf.identity](https://www.tensorflow.org/api_docs/python/tf/identity) + +_TensorFlow_: +``` +node { + name: "Placeholder" + op: "Placeholder" + attr { + key: "dtype" + value { type: DT_FLOAT } + } + attr { + key: "shape" + value { + shape { + dim { size: 3 } + dim { size: 4 } + } + } + } +} +node { + name: "Identity" + op: "Identity" + input: "Placeholder" + attr { + key: "T" + value { type: DT_FLOAT } + } +} +``` + +_loco_: +``` +%input = Pull(dtype: FLOAT32, shape: [3, 4]) +%ident = Forward(%input) +Push(%ident) +``` + +### Const + +**Const** in _TensorFlow_ corresponds to **ConstGen** in _loco_. + +_Python_: +```python +import tensorflow as tf +constant = tf.constant(value=[1.0], dtype=tf.float32, shape=[3, 4]) +tf.get_default_graph().as_graph_def() +``` + +API reference: [tf.constant](https://www.tensorflow.org/versions/r1.13/api_docs/python/tf/constant) + +_TensorFlow_: +``` +node { + name: "Const" + op: "Const" + attr { + key: "dtype" + value { type: DT_FLOAT } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { + dim { size: 3 } + dim { size: 4 } + } + float_val: 1.0 + } + } + } +} +``` + +_loco_: +``` +%constant = ConstGen(dtype: FLOAT32, shape: [3, 4], data: ...); +Push(%constant) +``` diff --git a/compiler/moco-tf/include/moco/tf/Frontend.h b/compiler/moco-tf/include/moco/tf/Frontend.h new file mode 100644 index 000000000..6914fdd38 --- /dev/null +++ b/compiler/moco-tf/include/moco/tf/Frontend.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TENSORFLOW_FRONTEND_H__ +#define __MOCO_TENSORFLOW_FRONTEND_H__ + +#include <moco/Import/ModelSignature.h> + +#include <loco.h> + +#include <tensorflow/core/framework/graph.pb.h> + +namespace moco +{ +namespace tf +{ + +class Frontend +{ +public: + enum class FileType + { + Text, + Binary, + }; + +public: + Frontend(); + +public: + std::unique_ptr<loco::Graph> load(const ModelSignature &, const char *, FileType) const; + std::unique_ptr<loco::Graph> load(const ModelSignature &, std::istream *, FileType) const; + +private: + std::unique_ptr<loco::Graph> import(const ModelSignature &, tensorflow::GraphDef &) const; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TENSORFLOW_FRONTEND_H__ diff --git a/compiler/moco-tf/requires.cmake b/compiler/moco-tf/requires.cmake new file mode 100644 index 000000000..751192fff --- /dev/null +++ b/compiler/moco-tf/requires.cmake @@ -0,0 +1,13 @@ +require("fipe") +require("loco") +require("moco") +require("locop") +require("stdex") +require("moco-log") +require("pepper-strcast") +require("locomotiv") +require("mio-tf") +require("plier-tf") +require("locoex-customop") +require("logo") +require("oops") diff --git a/compiler/moco-tf/src/BroadcastHelper.cpp b/compiler/moco-tf/src/BroadcastHelper.cpp new file mode 100644 index 000000000..fc058c141 --- /dev/null +++ b/compiler/moco-tf/src/BroadcastHelper.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BroadcastHelper.h" + +#include <loco/IR/Nodes.h> +#include <loco/Service/ShapeInference.h> + +#include <cassert> + +namespace +{ + +class NodeWithTensorShape +{ +public: + NodeWithTensorShape() = default; + +public: + NodeWithTensorShape(loco::Node *node, const loco::TensorShape &shape) : _node{node}, _shape{shape} + { + // DO NOTHING + } + +public: + loco::Node *node(void) const { return _node; } + const loco::TensorShape &shape(void) const { return _shape; } + +private: + loco::Node *_node = nullptr; + loco::TensorShape _shape; +}; + +NodeWithTensorShape glue(loco::Node *node, const loco::TensorShape &shape) +{ + return NodeWithTensorShape(node, shape); +} + +/** + * @brief Create a higher-rank TensorShape following NumPy broadcasting semantics + * + * HOW TO USE: + * + * auto expanded_tensor_shape = expand(tensor_shape).to(N); + */ +class TensorShapeExpander +{ +public: + TensorShapeExpander(const loco::TensorShape &shape) : _shape{shape} + { + // DO NOTHING + } + +public: + loco::TensorShape to(uint32_t output_rank) + { + auto const &input_shape = _shape; + uint32_t const input_rank = input_shape.rank(); + + assert(input_rank <= output_rank && "Cannot shrink rank"); + uint32_t const axis_shift = output_rank - input_rank; + + loco::TensorShape output_shape; + + output_shape.rank(output_rank); + for (uint32_t axis = 0; axis < output_rank; ++axis) + { + output_shape.dim(axis) = (axis < axis_shift) ? 1 : input_shape.dim(axis - axis_shift); + } + + return output_shape; + } + +private: + const loco::TensorShape _shape; +}; + +TensorShapeExpander expand(const loco::TensorShape &shape) { return TensorShapeExpander{shape}; } + +/** + * @brief Create a rank-expanded node (if required) + */ +class ExpandRankFunctor final +{ +public: + ExpandRankFunctor(uint32_t rank) : _rank{rank} + { + // DO NOTHING + } + +public: + NodeWithTensorShape operator()(const NodeWithTensorShape &in) const + { + auto const input_node = in.node(); + auto const input_shape = in.shape(); + auto const input_rank = input_shape.rank(); + + uint32_t const expected_rank = _rank; + + assert(input_rank <= expected_rank); + if (input_rank == expected_rank) + { + // Nothing to expand + return in; + } + + auto g = input_node->graph(); + assert(g != nullptr); + + auto output_shape = expand(input_shape).to(expected_rank); + auto output_node = g->nodes()->create<loco::FixedReshape>(); + + output_node->input(input_node); + output_node->rank(expected_rank); + for (uint32_t axis = 0; axis < expected_rank; ++axis) + { + output_node->dim(axis) = output_shape.dim(axis); + } + + return glue(output_node, output_shape); + } + +private: + uint32_t _rank; +}; + +ExpandRankFunctor expand_rank_to(uint32_t rank) { return ExpandRankFunctor{rank}; } + +/** + * @brief Create a dimension-expanded node (if required) + */ +class ExpandDimsFunctor final +{ +public: + ExpandDimsFunctor(const loco::TensorShape &shape) : _shape{shape} + { + // DO NOTHING + } + +public: + NodeWithTensorShape operator()(const NodeWithTensorShape &in) const + { + auto const input_node = in.node(); + auto const input_shape = in.shape(); + const auto &output_shape = _shape; + + assert(input_shape.rank() == output_shape.rank()); + + if (input_shape == output_shape) + { + // Nothing to expand + return in; + } + + uint32_t const rank = output_shape.rank(); + + auto g = input_node->graph(); + assert(g != nullptr); + + auto output_node = g->nodes()->create<loco::TensorBroadcast>(); + + for (uint32_t axis = 0; axis < rank; ++axis) + { + auto input_dim = input_shape.dim(axis); + auto output_dim = output_shape.dim(axis); + + assert(input_dim.known() and output_dim.known()); + + if (!(input_dim == output_dim)) + { + assert(input_dim == 1); + output_node->mapping()->dim(axis) = output_dim; + } + } + + output_node->input(input_node); + + return glue(output_node, output_shape); + } + +private: + loco::TensorShape _shape; +}; + +ExpandDimsFunctor expand_dims_as(const loco::TensorShape &shape) +{ + return ExpandDimsFunctor{shape}; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +loco::Node *BroadcastFunctor::build(loco::Node *node, const loco::TensorShape &shape) const +{ + // clang-format off + return glue(node, shape) + | expand_rank_to(_shape.rank()) + | expand_dims_as(_shape) + | [] (const NodeWithTensorShape &in) { return in.node(); }; + // clang-format on +} + +loco::Node *BroadcastFunctor::build(loco::Node *node) const +{ + return build(node, loco::shape_get(node).as<loco::TensorShape>()); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/BroadcastHelper.h b/compiler/moco-tf/src/BroadcastHelper.h new file mode 100644 index 000000000..6238ad269 --- /dev/null +++ b/compiler/moco-tf/src/BroadcastHelper.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __BROADCAST_HELPER_H__ +#define __BROADCAST_HELPER_H__ + +#include <loco/IR/Node.h> +#include <loco/IR/Dimension.h> +#include <loco/IR/TensorShape.h> + +#include <bino.h> +#include <fipe.h> // include "fipe.h" for clients + +namespace moco +{ +namespace tf +{ + +class BroadcastFunctor final +{ +public: + BroadcastFunctor(const loco::TensorShape &shape) : _shape{shape} + { + // DO NOTHING + } + +public: + loco::Node *build(loco::Node *in_node, const loco::TensorShape &in_shape) const; + + loco::Node *operator()(loco::Node *in_node, const loco::TensorShape &in_shape) const + { + return build(in_node, in_shape); + } + + // This method assumes the followings: + // - loco::shape_known(node) returns true, and + // - loco::shape_get(node).domain() is loco::Domain::Tensor + loco::Node *build(loco::Node *node) const; + + loco::Node *operator()(loco::Node *node) const { return build(node); } + +private: + loco::TensorShape _shape; +}; + +/** + * @brief Create a broadcasted node + * + * First, append canonical.FixedReshape if rank expansion is required. + * Then, append canonical.TensorBroadcast if dimension expansion is required + * + * This mimics "tf.broadcast_to" API in TensorFlow. + */ +static inline auto broadcast_to(const loco::TensorShape &shape) + -> decltype(bino::transform_both(std::declval<BroadcastFunctor>())) +{ + return bino::transform_both(BroadcastFunctor{shape}); +} + +} // namespace tf +} // namespace moco + +#endif // __BROADCAST_HELPER_H__ diff --git a/compiler/moco-tf/src/BroadcastHelper.test.cpp b/compiler/moco-tf/src/BroadcastHelper.test.cpp new file mode 100644 index 000000000..a6cbd719a --- /dev/null +++ b/compiler/moco-tf/src/BroadcastHelper.test.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BroadcastHelper.h" + +#include <loco.h> + +#include <gtest/gtest.h> + +TEST(BroadcastFunctorTest, expand_rank) +{ + // Broadcast Tensor<3> as Tensor<1 x 3> + auto g = loco::make_graph(); + + auto input = g->inputs()->create(); + + auto pull = g->nodes()->create<loco::Pull>(); + pull->index(0); + + loco::TensorShape current_shape; + { + current_shape.rank(1); + current_shape.dim(0) = 3; + } + + loco::TensorShape expected_shape; + { + expected_shape.rank(2); + expected_shape.dim(0) = 1; + expected_shape.dim(1) = 3; + } + + moco::tf::BroadcastFunctor functor{expected_shape}; + + auto node = functor.build(pull, current_shape); + + ASSERT_EQ(node->opnum(), static_cast<uint32_t>(loco::CanonicalOpcode::FixedReshape)); + ASSERT_EQ(node->arg(0), pull); +} + +TEST(BroadcastFunctorTest, expand_dims) +{ + // Broadcast Tensor<1> as Tensor<3> + auto g = loco::make_graph(); + + auto input = g->inputs()->create(); + + auto pull = g->nodes()->create<loco::Pull>(); + pull->index(0); + + loco::TensorShape current_shape; + { + current_shape.rank(1); + current_shape.dim(0) = 1; + } + + loco::TensorShape expected_shape; + { + expected_shape.rank(1); + expected_shape.dim(0) = 3; + } + + moco::tf::BroadcastFunctor functor{expected_shape}; + + auto node = functor.build(pull, current_shape); + + ASSERT_EQ(node->opnum(), static_cast<uint32_t>(loco::CanonicalOpcode::TensorBroadcast)); + ASSERT_EQ(node->arg(0), pull); + + auto tensor_broadcast = dynamic_cast<loco::TensorBroadcast *>(node); + + ASSERT_NE(tensor_broadcast, nullptr); + ASSERT_TRUE(tensor_broadcast->mapping()->defined(0)); + ASSERT_EQ(tensor_broadcast->mapping()->dim(0), 3); +} diff --git a/compiler/moco-tf/src/CanonicalEltwiseInputConnector.cpp b/compiler/moco-tf/src/CanonicalEltwiseInputConnector.cpp new file mode 100644 index 000000000..adeae39de --- /dev/null +++ b/compiler/moco-tf/src/CanonicalEltwiseInputConnector.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CanonicalEltwiseInputConnector.h" + +#include <loco/IR/Nodes.h> + +namespace moco +{ +namespace tf +{ +namespace eltwise +{ +namespace binary +{ + +template <typename NodeTy> void InputConnector<NodeTy>::operator()(const NodePair &p) const +{ + _node->lhs(p.first); + _node->rhs(p.second); +} + +#define INSTANTIATE(OP) template void InputConnector<loco::OP>::operator()(const NodePair &) const; + +INSTANTIATE(EltwiseAdd); +INSTANTIATE(EltwiseSub); +INSTANTIATE(EltwiseMax); +INSTANTIATE(EltwiseMul); +INSTANTIATE(EltwiseDiv); + +#undef INSTANTIATE + +} // namespace binary +} // namespace eltwise +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/CanonicalEltwiseInputConnector.h b/compiler/moco-tf/src/CanonicalEltwiseInputConnector.h new file mode 100644 index 000000000..a50a5011c --- /dev/null +++ b/compiler/moco-tf/src/CanonicalEltwiseInputConnector.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CANONICAL_ELTWISE_INPUT_CONNECTOR_H__ +#define __CANONICAL_ELTWISE_INPUT_CONNECTOR_H__ + +#include <loco/IR/Node.h> + +#include <utility> + +namespace moco +{ +namespace tf +{ +namespace eltwise +{ +namespace binary +{ + +using NodePair = std::pair<loco::Node *, loco::Node *>; + +template <typename NodeTy> class InputConnector +{ +public: + InputConnector(NodeTy *node) : _node{node} + { + // DO NOTHING + } + +public: + void operator()(const NodePair &p) const; + +private: + NodeTy *_node; +}; + +template <typename NodeTy> InputConnector<NodeTy> connect_to(NodeTy *node) +{ + return InputConnector<NodeTy>{node}; +} + +} // namespace binary +} // namespace eltwise +} // namespace tf +} // namespace moco + +#endif // __CANONICAL_ELTWISE_INPUT_CONNECTOR_H__ diff --git a/compiler/moco-tf/src/Canonicalization/AddCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/AddCanonicalizer.cpp new file mode 100644 index 000000000..8028a870c --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/AddCanonicalizer.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AddCanonicalizer.h" + +#include <moco/IR/TFDialect.h> +#include <moco/IR/TFNodes.h> + +#include "TFEltwiseBinaryCanonicalzeHelper.h" + +namespace moco +{ +namespace tf +{ + +bool AddCanonicalizer::transform(TFAdd *node) const +{ + return canonicalize_eltwise_binary_node(node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/AddCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/AddCanonicalizer.h new file mode 100644 index 000000000..53ba9ed58 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/AddCanonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_ADD_CANONICALIZER_H__ +#define __MOCO_TF_ADD_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFAdd to Canonical EltwiseAdd + */ +class AddCanonicalizer : public SimpleNodeTransform<TFAdd> +{ +public: + const char *name(void) const final { return "AddCanonicalizer"; } + +public: + bool transform(TFAdd *node) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_ADD_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.cpp new file mode 100644 index 000000000..e07a4f64f --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AvgPoolCanonicalizer.h" + +#include <moco/IR/TFDialect.h> +#include <moco/Support/TFShapeInferenceHelper.h> + +#include "CodecHelper.h" + +#include <loco/IR/NodeShape.h> + +#include <moco/Log.h> + +namespace +{ + +bool canonicalize_avgpool2d(loco::Graph *graph, moco::TFAvgPool *node) +{ + LOGGER(l); + + /** + * @note This will replace TFAvgPool node with Canonical FeatureEncode + + * AvgPool2D + FeatureDecode + * + * Before + * A -- TFAvgPool -- C + * + * After + * +- TFAvgPool -- + * | + * A -+- FeatureEncode -- AvgPool2D -- FeatureDecode -- C + * + * Where + * A : value of TFAvgPool + * C : a node that uses TFAvgPool as an input + * TFAvgPool is disconnected from other nodes + */ + + auto data_layout = plier::tf::as_data_layout(node->data_layout()); + + auto feature_enc = graph->nodes()->create<loco::FeatureEncode>(); + auto avgPool2d_node = graph->nodes()->create<loco::AvgPool2D>(); + auto feature_dec = graph->nodes()->create<loco::FeatureDecode>(); + + set_feature_enc(feature_enc, data_layout); + set_feature_dec(feature_dec, data_layout); + + avgPool2d_node->convention(loco::AvgPool2D::Convention::Valid); + + auto value_shape = moco::node_shape(node->value()); + assert(value_shape.domain() != loco::Domain::Unknown); + + auto node_stride = moco::stride_of(node->strides(), node->data_layout()); + auto node_window = moco::window_of(node->ksize(), node->data_layout()); + + moco::Padding2DInference infer_padding2d; + + infer_padding2d.padding(node->padding()); + infer_padding2d.stride(node_stride); + infer_padding2d.window(node_window); + + auto input_feature_shape = moco::as_feature_shape(value_shape, node->data_layout()); + auto input_plane_shape = moco::make_plane_shape(input_feature_shape); + + *avgPool2d_node->pad() = infer_padding2d(input_plane_shape); + *avgPool2d_node->stride() = node_stride; + *avgPool2d_node->window() = node_window; + + INFO(l) << "Canonicalize TFAvgPool pad = T " << avgPool2d_node->pad()->top() << ", L " + << avgPool2d_node->pad()->left() << ", B " << avgPool2d_node->pad()->bottom() << ", R " + << avgPool2d_node->pad()->right() << std::endl; + + // update graph + auto node_A = node->value(); + + // update connections + feature_enc->input(node_A); + avgPool2d_node->ifm(feature_enc); + feature_dec->input(avgPool2d_node); + + // replace node + replace(node).with(feature_dec); + + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool AvgPoolCanonicalizer::transform(TFAvgPool *node) const +{ + return canonicalize_avgpool2d(node->graph(), node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.h new file mode 100644 index 000000000..e9c56c868 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_AVGPOOL_CANONICALIZER_H__ +#define __MOCO_TF_AVGPOOL_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFAvgPool to Canonical AvgPool2D + */ +class AvgPoolCanonicalizer : public SimpleNodeTransform<moco::TFAvgPool> +{ +public: + const char *name(void) const final { return "AvgPoolCanonicalizer"; } + +public: + bool transform(TFAvgPool *node) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_AVGPOOL_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.cpp new file mode 100644 index 000000000..a5568ce1a --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BiasAddCanonicalizer.h" + +#include <moco/IR/TFDialect.h> + +#include <moco/Names.h> +#include <moco/Log.h> +#include <plier/tf/Convert.h> + +namespace +{ +using plier::tf::DataLayout; + +bool canonicalize_biasadd(loco::Graph *graph, moco::TFBiasAdd *node) +{ + LOGGER(l); + + /** + * @note This will replace TFBiasAdd node with Canonical BiasEncode + TensorBiasAdd + * + * Before + * A -- TFBiasAdd - C + * B -/ + * + * After + * A -- TFBiasAdd - + * B -/ + * A --------------- TensorBiasAdd - C + * B - BiasEncode -/ + * + * Where + * A : value of TFBiasAdd + * B : bias of TFBiasAdd + * C : a node that uses TFBiasAdd as an input + * TFBiasAdd is disconnected from node C + * A and B are drawn twice to simplify the diagram + */ + + INFO(l) << "TFNodeCanonicalize TFBiasAdd begin"; + + // tensorflow data_format: one of NHWC or NCHW. + auto data_layout = plier::tf::as_data_layout(node->data_layout()); + + // creating loco nodes + auto bias_enc = graph->nodes()->create<loco::BiasEncode>(); + + auto bias_add = graph->nodes()->create<loco::TensorBiasAdd>(); + { + if (data_layout == DataLayout::NHWC) + { + INFO(l) << "TFNodeCanonicalize TFBiasAdd axis 3"; + bias_add->axis(3); + } + else if (data_layout == DataLayout::NCHW) + { + INFO(l) << "TFNodeCanonicalize TFBiasAdd axis 1"; + bias_add->axis(1); // Channel + // Note: the following descrition of TF 1.13 at + // https://www.tensorflow.org/api_docs/python/tf/nn/bias_add seems wrong: + // "bias: A 1-D Tensor with size matching the last dimension of value." + // because providing the size of W (last dimension) to bias throws an error with TensorFlow + } + } + + auto node_A = node->value(); + auto node_B = node->bias(); + + // update connections + bias_add->value(node_A); + bias_add->bias(bias_enc); + bias_enc->input(node_B); + + // replace old with new : about C in above note + replace(node).with(bias_add); + + INFO(l) << "TFNodeCanonicalize TFBiasAdd done"; + + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool BiasAddCanonicalizer::transform(TFBiasAdd *node) const +{ + return canonicalize_biasadd(node->graph(), node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.h new file mode 100644 index 000000000..ff4032ca9 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_BIASADD_CANONICALIZER_H__ +#define __MOCO_TF_BIASADD_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFBiasAdd to Canonical BiasAdd + */ +class BiasAddCanonicalizer final : public SimpleNodeTransform<moco::TFBiasAdd> +{ +public: + const char *name(void) const final { return "BiasAddCanonicalizer"; } + +public: + bool transform(TFBiasAdd *node) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_BIASADD_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.cpp new file mode 100644 index 000000000..b59a3f3d7 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConcatV2Canonicalizer.h" +#include "LogHelper.h" + +#include <moco/IR/TFDialect.h> +#include <moco/Support/TFShapeInferenceHelper.h> + +#include <moco/Log.h> + +#include <loco/Service/ShapeInference.h> + +#include <stdex/Memory.h> +#include <oops/UserExn.h> + +namespace +{ + +using namespace moco::tf; + +bool scalar_value(moco::TFConst *node, int32_t &ret) +{ + auto nodeshape = node_shape(node); + if (!(node->dtype() == loco::DataType::S32)) + return false; + + auto tensor_shape = nodeshape.as<loco::TensorShape>(); + if (!(tensor_shape.rank() == 0 || tensor_shape.rank() == 1)) + return false; + + ret = node->at<loco::DataType::S32>(0); + + return true; +} + +bool canonicalize_concat(loco::Graph *graph, moco::TFConcatV2 *node) +{ + LOGGER(l); + + /** + * @note This will replace TFConcatV2 node with (series of) Canonical + * TensorConcat. Below diagram is an example of three inputs + * + * Before + * A --- TFConcatV2 -- C + * B --/ + * N --/ + * X --/ + * After + * A --- TFConcatV2 + * B --/ + * N --/ + * X --/ + * A --- TensorConcat -- TensorConcat -- C + * B --/ / + * N -----------------/ + * + * Where + * A : first value of TFConcatV2 + * B : second value of TFConcatV2 + * N : third or N'th value of TFConcatV2 + * X : axis node of TFConcatV2 + * C : a node that uses TFConcatV2 as an input + * TFConcatV2 is disconnected from C + * To simplify the diagram in 'After', A, B, N are drawn + * multiple times but they are same nodes. + */ + + const int num_values = node->num_values(); + assert(num_values >= 2); + + // get axis absolute value + auto value_a = node->values(0); + if (!loco::shape_known(value_a)) + return false; + + uint32_t node_rank = 0; + { + auto value_a_shape = moco::node_shape(value_a); + assert(value_a_shape.domain() == loco::Domain::Tensor); + + auto value_a_tensor_shape = value_a_shape.as<loco::TensorShape>(); + node_rank = value_a_tensor_shape.rank(); + } + + int32_t axis_value = 0; + { + // axis should be TFConst + auto axis_node = node->axis(); + auto tfconst = dynamic_cast<moco::TFConst *>(axis_node); + if (tfconst == nullptr) + { + // TODO Check this: this error can be from TFOptimizatier. + throw oops::UserExn("ConcatV2 node has invalid input for axis", node->name()); + } + auto result = scalar_value(tfconst, axis_value); + if (!result) + { + // TODO Check this: this error can be from TFOptimizatier. + throw oops::UserExn("ConcatV2 node has invalid input for axis", node->name()); + } + } + uint32_t axis_absolute = (axis_value >= 0) ? axis_value : (int32_t)node_rank + axis_value; + + INFO(l) << "canonicalize_concat axis(" << axis_absolute << "), value(" << axis_value << "), rank(" + << node_rank << ")"; + + // Convert series of TensorConcat if num_values > 2 + auto concat_node = graph->nodes()->create<loco::TensorConcat>(); + concat_node->lhs(node->values(0)); + concat_node->rhs(node->values(1)); + concat_node->axis(axis_absolute); + + loco::TensorConcat *last_concat = concat_node; + for (int ni = 2; ni < num_values; ++ni) + { + auto concat_node_next = graph->nodes()->create<loco::TensorConcat>(); + + concat_node_next->lhs(last_concat); + concat_node_next->rhs(node->values(ni)); + concat_node_next->axis(axis_absolute); + + // update last concat node + last_concat = concat_node_next; + } + + // replace node + replace(node).with(last_concat); + + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool ConcatV2Canonicalizer::transform(TFConcatV2 *node) const +{ + return canonicalize_concat(node->graph(), node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.h b/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.h new file mode 100644 index 000000000..e6b471b89 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_CONCATV2_CANONICALIZER_H__ +#define __MOCO_TF_CONCATV2_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFConcatV2 to Canonical TensorConcat + */ +class ConcatV2Canonicalizer : public SimpleNodeTransform<moco::TFConcatV2> +{ +public: + const char *name(void) const final { return "ConcatV2Canonicalizer"; } + +public: + bool transform(moco::TFConcatV2 *node) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_CONCATV2_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.cpp new file mode 100644 index 000000000..60629cd5a --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConstCanonicalizer.h" + +#include <moco/IR/TFDialect.h> + +#include <moco/Names.h> +#include <moco/Log.h> + +#include <oops/UserExn.h> + +namespace +{ + +bool canonicalize_const(loco::Graph *graph, moco::TFConst *node) +{ + LOGGER(l); + + /** + * @note This will replace TFConst node with Canonical Const + * + * Before + * TFConst -- C + * + * After + * TFConst - + * ConstGen -- C + * + * Where + * C : a node that uses TFConst as an input + * TFConst is disconnected from other nodes + */ + + INFO(l) << "TFNodeCanonicalize TFConst begin"; + + auto const_node = graph->nodes()->create<loco::ConstGen>(); + + // copy properties + auto dtype = node->dtype(); + const_node->dtype(dtype); + + auto rank = node->rank(); + + if (rank == 0) + { + // This routine implements a workaround that converts a scalar constant (rank-0 tensor) + // into a rank-1 tensor of shape [1]. + // + // TODO Revise this implementation later + const_node->rank(1); + const_node->dim(0) = 1; + } + else + { + const_node->rank(rank); + + for (uint32_t r = 0; r < rank; ++r) + { + if (node->dim(r).known()) + const_node->dim(r) = node->dim(r); + else + const_node->dim(r).unset(); + } + } + + switch (dtype) + { + case loco::DataType::S32: + { + uint32_t input_elements = node->size<loco::DataType::S32>(); + const_node->size<loco::DataType::S32>(input_elements); + for (uint32_t i = 0; i < input_elements; ++i) + { + const_node->at<loco::DataType::S32>(i) = node->at<loco::DataType::S32>(i); + } + break; + } + case loco::DataType::FLOAT32: + { + uint32_t input_elements = node->size<loco::DataType::FLOAT32>(); + const_node->size<loco::DataType::FLOAT32>(input_elements); + for (uint32_t i = 0; i < input_elements; ++i) + { + const_node->at<loco::DataType::FLOAT32>(i) = node->at<loco::DataType::FLOAT32>(i); + } + break; + } + default: + throw oops::UserExn("Const has unsupported data type", node->name()); + } + + // update graph + replace(node).with(const_node); + + INFO(l) << "TFNodeCanonicalize TFConst done"; + + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool ConstCanonicalizer::transform(TFConst *node) const +{ + return canonicalize_const(node->graph(), node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.h new file mode 100644 index 000000000..1b0b2b867 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_CONST_CANONICALIZER_H__ +#define __MOCO_TF_CONST_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFConst to Canonical ConstGen + */ +class ConstCanonicalizer : public SimpleNodeTransform<moco::TFConst> +{ +public: + const char *name(void) const final { return "ConstCanonicalizer"; } + +public: + bool transform(moco::TFConst *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_CONST_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.cpp new file mode 100644 index 000000000..d3cbd4ab3 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.cpp @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Conv2DBackpropInputCanonicalizer.h" + +#include <moco/IR/TFDialect.h> + +#include "CodecHelper.h" + +#include <loco/IR/Stride.h> +#include <loco/IR/Padding2D.h> +#include <loco/Service/ShapeInference.h> + +#include <oops/UserExn.h> + +namespace +{ +using plier::tf::DataLayout; + +void set_filter_enc(loco::FilterEncode *filter_enc) +{ + auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>(); + + // In TensorFlow, Conv2dBackpropInput's filter is a 4-D tensor of following shape: + // [filter_height, filter_width, out_channels, in_channels] or HWOI or HWNC (in/out in loco sense) + enc->perm()->axis(loco::FilterAxis::Height) = 0; + enc->perm()->axis(loco::FilterAxis::Width) = 1; + enc->perm()->axis(loco::FilterAxis::Count) = 2; + enc->perm()->axis(loco::FilterAxis::Depth) = 3; + + filter_enc->encoder(std::move(enc)); +} + +} // namespace + +namespace +{ + +bool stride_2d_from_4d(loco::Stride<2> &ret, const std::vector<int64_t> &strides_4d, + const DataLayout data_layout) +{ + if (!(strides_4d.size() == 4)) + return false; + + switch (data_layout) + { + case DataLayout::NHWC: + ret.vertical(strides_4d.at(1)); + ret.horizontal(strides_4d.at(2)); + break; + case DataLayout::NCHW: + ret.vertical(strides_4d.at(2)); + ret.horizontal(strides_4d.at(3)); + break; + default: + return false; + } + return true; +} + +struct PlaneShape +{ + loco::Dimension vertical; + loco::Dimension horizontal; +}; + +class Padding2DInference final +{ +public: + Padding2DInference(const moco::TFNode *node) { _node = node; } + +public: + loco::Padding2D operator()(void); + +public: + PlaneShape &input() { return _input; } + PlaneShape &output() { return _output; } + loco::Stride<2> &stride() { return _stride; } + loco::Window<2> &window() { return _window; } + moco::TFPadding &padding() { return _padding; } + +private: + /// @brief Check whether ingredients set by non-default values + bool ready() + { + if (not input().vertical.known()) + return false; + if (not input().horizontal.known()) + return false; + if (not output().vertical.known()) + return false; + if (not output().horizontal.known()) + return false; + if (stride().vertical() == 0) + return false; + if (stride().horizontal() == 0) + return false; + if (window().vertical() == 0) + return false; + if (window().horizontal() == 0) + return false; + if (padding().empty()) + return false; + + return true; + } + + inline uint32_t tight_output_for_valid_padding(uint32_t input, uint32_t stride, uint32_t filter) + { + return stride * (input - 1) + filter; + } + + /** + * @note For Conv2DBackpropInput SAME padding, TensorFlow requires this condition to hold + * + * Reference: `::tensorflow::GetWindowedOutputSizeVerboseV2()` from TensorFlow project + */ + inline bool same_padding_applicable(uint32_t input, uint32_t output, uint32_t stride) + { + // Here 'input' and 'output' means Conv2DBackpropInput's actual node input and output. + // Then these three conditions are equivalent: + // + // input == floor((output + stride - 1) / stride) + // input == ceil(output / stride) + // (stride * (input - 1) < output) and (output <= stride * input) + return (stride * (input - 1) < output) and (output <= stride * input); + } + + inline uint32_t padding_needed(uint32_t input, uint32_t output, uint32_t stride, uint32_t filter) + { + return stride * (input - 1) + filter - output; + } + +private: + const moco::TFNode *_node; + PlaneShape _input; + PlaneShape _output; + loco::Stride<2> _stride; + loco::Window<2> _window; + moco::TFPadding _padding; +}; + +loco::Padding2D Padding2DInference::operator()(void) +{ + assert(ready()); + + if (padding() == "VALID") + { + // In case of VALID padding, TensorFlow accepts any size same or larger than + // 'tight fit' output. When output size (set by 'input sizes' node input) is + // larger than tight fit, extra spaces filled with zero. + auto tight_output_vertical = tight_output_for_valid_padding( + input().vertical.value(), stride().vertical(), window().vertical()); + auto tight_output_horizontal = tight_output_for_valid_padding( + input().horizontal.value(), stride().horizontal(), window().horizontal()); + + if (output().vertical.value() < tight_output_vertical or + output().horizontal.value() < tight_output_horizontal) + throw oops::UserExn("input_sizes is too small", _node->name()); + + // Currently, only accept tight fit. + // TODO Support non-tight case by adding zero padding operation + assert(output().vertical.value() == tight_output_vertical); + assert(output().horizontal.value() == tight_output_horizontal); + + return loco::Padding2D(0, 0, 0, 0); + } + + if (padding() == "SAME") + { + // This condition is required by TensorFlow + if (not same_padding_applicable(input().vertical.value(), output().vertical.value(), + stride().vertical()) or + not same_padding_applicable(input().horizontal.value(), output().horizontal.value(), + stride().horizontal())) + throw oops::UserExn("Size mismatch for SAME padding", _node->name()); + + auto whole_pad_vertical = padding_needed(input().vertical.value(), output().vertical.value(), + stride().vertical(), window().vertical()); + auto whole_pad_horizontal = + padding_needed(input().horizontal.value(), output().horizontal.value(), + stride().horizontal(), window().horizontal()); + + loco::Padding2D res; + + res.top(whole_pad_vertical / 2); + res.bottom(whole_pad_vertical - res.top()); + res.left(whole_pad_horizontal / 2); + res.right(whole_pad_horizontal - res.left()); + + return res; + } + + throw oops::UserExn("Usupported padding " + padding(), _node->name()); +} + +/** + * @param[out] ret PlaneShape extracted from 'node' with given 'data_layout' + * @param[in] node + * @param[in] data_layout + * + * @return true on success + */ +bool set_plane_shape(PlaneShape &ret, const loco::Node *node, const DataLayout data_layout) +{ + auto tensor_shape = loco::shape_get(node).as<loco::TensorShape>(); + if (!(tensor_shape.rank() == 4)) + return false; + + switch (data_layout) + { + case DataLayout::NHWC: + ret.vertical = tensor_shape.dim(1).value(); + ret.horizontal = tensor_shape.dim(2).value(); + break; + case DataLayout::NCHW: + ret.vertical = tensor_shape.dim(2).value(); + ret.horizontal = tensor_shape.dim(3).value(); + break; + default: + return false; + } + + return true; +} + +/** + * @param[out] ret 2D Window extracted from HW** filter node + * @param[in] filter_node + * + * @return true on success + */ +bool set_window(loco::Window<2> &ret, const loco::Node *filter_node) +{ + auto tensor_shape = loco::shape_get(filter_node).as<loco::TensorShape>(); + assert(tensor_shape.rank() == 4); + + ret.vertical(tensor_shape.dim(0).value()); + ret.horizontal(tensor_shape.dim(1).value()); + + return true; +} + +} // namespace + +namespace +{ + +bool canonicalize_conv2d_backprop_input(loco::Graph *graph, + moco::TFConv2DBackpropInput *conv2d_backprop) +{ + /** + * @note This will replace TFConv2DBackpropInput node with canonical + * FeatureEncode + FilterEncode + TransposedConv2D + FeatureDecode + * + * Before + * input_sizes ---- + * \ + * filter -------- TFConv2DBackpropInput --- output(s) + * / + * out_backprop --- + * + * After + * input_sizes ---- + * \ + * filter -------- TFConv2DBackpropInput --- + * / + * out_backprop --- + * + * filter ------ FilterEncode ------ TransposedConv2D --- FeatureDecode --- output(s) + * (as ker) / + * out_backprop --- FeatureEncode --- + * (as ifm) + */ + + if (!loco::shape_known(conv2d_backprop->out_backprop())) + return false; + if (!loco::shape_known(conv2d_backprop)) + return false; + if (!loco::shape_known(conv2d_backprop->filter())) + return false; + + auto data_layout = plier::tf::as_data_layout(conv2d_backprop->data_layout()); + + // Nodes to replace + auto feature_enc = graph->nodes()->create<loco::FeatureEncode>(); + auto filter_enc = graph->nodes()->create<loco::FilterEncode>(); + auto tr_conv2d = graph->nodes()->create<loco::TransposedConv2D>(); + auto feature_dec = graph->nodes()->create<loco::FeatureDecode>(); + + set_feature_enc(feature_enc, data_layout); + set_filter_enc(filter_enc); + set_feature_dec(feature_dec, data_layout); + + // Attributes for new TransposedConv2D + loco::Stride<2> stride; + loco::Padding2D pad; + + // Get attributes + { + if (!stride_2d_from_4d(stride, conv2d_backprop->strides(), data_layout)) + throw oops::UserExn("Unsupported strides", conv2d_backprop->name()); + + Padding2DInference infer_pad(conv2d_backprop); + + if (!set_plane_shape(infer_pad.input(), conv2d_backprop->out_backprop(), data_layout)) + throw oops::UserExn("Unsupported out_backprop data_format", conv2d_backprop->name()); + if (!set_plane_shape(infer_pad.output(), conv2d_backprop, data_layout)) + throw oops::UserExn("Unsupported data_format", conv2d_backprop->name()); + if (!set_window(infer_pad.window(), conv2d_backprop->filter())) + throw oops::UserExn("Unsupported filter shape", conv2d_backprop->name()); + infer_pad.stride() = stride; + infer_pad.padding() = conv2d_backprop->padding(); + + // Run padding infer_pad + pad = infer_pad(); + } + + // Set attributes + tr_conv2d->pad()->top(pad.top()); + tr_conv2d->pad()->bottom(pad.bottom()); + tr_conv2d->pad()->left(pad.left()); + tr_conv2d->pad()->right(pad.right()); + + tr_conv2d->stride()->vertical(stride.vertical()); + tr_conv2d->stride()->horizontal(stride.horizontal()); + + // Update graph + auto input_node = conv2d_backprop->out_backprop(); + auto filter_node = conv2d_backprop->filter(); + + // Update connections + feature_enc->input(input_node); + filter_enc->input(filter_node); + tr_conv2d->ifm(feature_enc); + tr_conv2d->ker(filter_enc); + feature_dec->input(tr_conv2d); + + // Replace old conv2d_backprop + replace(conv2d_backprop).with(feature_dec); + + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool Conv2DBackpropInputCanonicalizer::transform(TFConv2DBackpropInput *node) const +{ + return canonicalize_conv2d_backprop_input(node->graph(), node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.h new file mode 100644 index 000000000..bc37bb9cb --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_CONV2DBACKPROPINPUT_CANONICALIZER_H__ +#define __MOCO_TF_CONV2DBACKPROPINPUT_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/// @brief Convert TFConv2DBackpropInput to Canonical TransposedConv2D +class Conv2DBackpropInputCanonicalizer : public SimpleNodeTransform<moco::TFConv2DBackpropInput> +{ +public: + const char *name(void) const final { return "Conv2DBackpropInputCanonicalizer"; } + +public: + bool transform(moco::TFConv2DBackpropInput *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_CONV2DBACKPROPINPUT_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.cpp new file mode 100644 index 000000000..a955793a8 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Conv2DCanonicalizer.h" + +#include <moco/IR/TFDialect.h> +#include <moco/Support/TFShapeInferenceHelper.h> + +#include "CodecHelper.h" + +#include <moco/Log.h> + +namespace +{ +using plier::tf::DataLayout; + +void set_filter_enc(loco::FilterEncode *filter_enc) +{ + auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>(); + + // In TensorFlow, conv2d filter is a 4-D tensor of following shape: + // [filter_height, filter_width, in_channels, out_channels] -> HWIO (HWCN) + enc->perm()->axis(loco::FilterAxis::Height) = 0; + enc->perm()->axis(loco::FilterAxis::Width) = 1; + enc->perm()->axis(loco::FilterAxis::Depth) = 2; + enc->perm()->axis(loco::FilterAxis::Count) = 3; + + filter_enc->encoder(std::move(enc)); +} + +bool canonicalize_conv2d(loco::Graph *graph, moco::TFConv2D *node) +{ + LOGGER(l); + + /** + * @note This will replace TFCon2D node with Canonical FeatureEncode + + * FilterEncode + Conv2D + FeatureDecode + * + * Before + * A -- TFConv2D - C + * B -/ + * + * After + * A -- TFConv2D - + * B -/ + * A -- FeatureEncode - Conv2D - FeatureDecode - C + * B -- FilterEncode -/ + * + * Where + * A : ifm of TFConv2D + * B : ker of TFConv2D + * C : a node that uses TFConv2D as an input + * TFConv2D is disconnected from other nodes + * A and B are drawn twice to simplify the diagram + */ + + auto data_layout = plier::tf::as_data_layout(node->data_layout()); + + auto feature_enc = graph->nodes()->create<loco::FeatureEncode>(); + auto filter_enc = graph->nodes()->create<loco::FilterEncode>(); + auto conv2d = graph->nodes()->create<loco::Conv2D>(); + auto feature_dec = graph->nodes()->create<loco::FeatureDecode>(); + + set_feature_enc(feature_enc, data_layout); + set_filter_enc(filter_enc); + set_feature_dec(feature_dec, data_layout); + + auto input_shape = moco::node_shape(node->input()); + assert(input_shape.domain() != loco::Domain::Unknown); + + auto ker_shape = moco::node_shape(node->filter()); + auto ker_tensor_shape = ker_shape.as<loco::TensorShape>(); // in HWIO + + auto node_stride = moco::stride_of(node->strides(), node->data_layout()); + auto node_window = moco::window_of(ker_tensor_shape, "HWIO"); + + moco::Padding2DInference infer_padding2d; + + infer_padding2d.padding(node->padding()); + infer_padding2d.stride(node_stride); + infer_padding2d.window(node_window); + + auto input_feature_shape = moco::as_feature_shape(input_shape, node->data_layout()); + auto input_plane_shape = moco::make_plane_shape(input_feature_shape); + + *conv2d->pad() = infer_padding2d(input_plane_shape); + *conv2d->stride() = node_stride; + + // update graph + auto node_A = node->input(); + auto node_B = node->filter(); + + // update connections + feature_enc->input(node_A); + filter_enc->input(node_B); + conv2d->ifm(feature_enc); + conv2d->ker(filter_enc); + feature_dec->input(conv2d); + + // replace old node + replace(node).with(feature_dec); + + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool Conv2DCanonicalizer::transform(TFConv2D *node) const +{ + return canonicalize_conv2d(node->graph(), node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.h new file mode 100644 index 000000000..ea39667f3 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_CONV2D_CANONICALIZER_H__ +#define __MOCO_TF_CONV2D_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFConv2D to Canonical Conv2D + */ +class Conv2DCanonicalizer : public SimpleNodeTransform<TFConv2D> +{ +public: + const char *name(void) const final { return "Conv2DCanonicalizer"; } + +public: + bool transform(TFConv2D *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_CONV2D_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.cpp new file mode 100644 index 000000000..50dddf637 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DepthwiseConv2dNativeCanonicalizer.h" + +#include <moco/IR/TFDialect.h> +#include <moco/Support/TFShapeInferenceHelper.h> + +#include "CodecHelper.h" + +#include <moco/Log.h> + +namespace +{ + +using plier::tf::DataLayout; + +void set_filter_enc(loco::DepthwiseFilterEncode *filter_enc) +{ + auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::DepthwiseFilter>>(); + + // In TensorFlow, depthwiseconv2dnative filter is a 4-D tensor of following shape: + // [filter_height, filter_width, in_channels, channel_multiplier] -> HWCM + enc->perm()->axis(loco::DepthwiseFilterAxis::Height) = 0; + enc->perm()->axis(loco::DepthwiseFilterAxis::Width) = 1; + enc->perm()->axis(loco::DepthwiseFilterAxis::Depth) = 2; + enc->perm()->axis(loco::DepthwiseFilterAxis::Multiplier) = 3; + + filter_enc->encoder(std::move(enc)); +} + +bool canonicalize_depthwiseconv2dnative(loco::Graph *graph, moco::TFDepthwiseConv2dNative *node) +{ + LOGGER(l); + + /** + * @note This will replace TFDepthwiseConv2dNative node with Canonical FeatureEncode + + * DepthwiseFilterEncode + DepthwiseConv2D + FeatureDecode + * + * Before + * A -+- TFDepthwiseConv2dNative - C + * | + * B -+ + * + * After + * + * A -+ FeatureEncode ----------------+- DepthwiseConv2D - FeatureDecode - C + * | | + * +-(TFDepthwiseConv2dNative) | + * | | + * B -+ DepthwiseFilterEncode --------+ + * + * Where + * A : ifm of TFDepthwiseConv2dNative + * B : ker of TFDepthwiseConv2dNative + * C : a node that uses TFDepthwiseConv2dNative as an input + * TFDepthwiseConv2dNative is disconnected from other nodes + */ + + INFO(l) << "TFNodeCanonicalize TFDepthwiseConv2dNative begin"; + + auto data_layout = plier::tf::as_data_layout(node->data_layout()); + + auto feature_enc = graph->nodes()->create<loco::FeatureEncode>(); + auto filter_enc = graph->nodes()->create<loco::DepthwiseFilterEncode>(); + auto depthwiseconv2d = graph->nodes()->create<loco::DepthwiseConv2D>(); + auto feature_dec = graph->nodes()->create<loco::FeatureDecode>(); + + set_feature_enc(feature_enc, data_layout); + set_filter_enc(filter_enc); + set_feature_dec(feature_dec, data_layout); + + // Calculate Pad and Stride from inference + auto input_shape = moco::node_shape(node->input()); + auto ker_shape = moco::node_shape(node->filter()); + auto ker_tensor_shape = ker_shape.as<loco::TensorShape>(); + auto node_stride = moco::stride_of(node->strides(), node->data_layout()); + auto node_window = moco::window_of(ker_tensor_shape, "HWCM"); + + moco::Padding2DInference infer_padding2d; + + infer_padding2d.padding(node->padding()); + infer_padding2d.stride(node_stride); + infer_padding2d.window(node_window); + + auto input_feature_shape = moco::as_feature_shape(input_shape, node->data_layout()); + auto input_plane_shape = moco::make_plane_shape(input_feature_shape); + + *depthwiseconv2d->pad() = infer_padding2d(input_plane_shape); + *depthwiseconv2d->stride() = node_stride; + + // update graph + auto node_A = node->input(); + auto node_B = node->filter(); + + // update connections + feature_enc->input(node_A); + filter_enc->input(node_B); + depthwiseconv2d->ifm(feature_enc); + depthwiseconv2d->ker(filter_enc); + feature_dec->input(depthwiseconv2d); + + // replace and disconnect old node + replace(node).with(feature_dec); + + INFO(l) << "TFNodeCanonicalize TFDepthwiseConv2dNative done"; + + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool DepthwiseConv2dNativeCanonicalizer::transform(TFDepthwiseConv2dNative *node) const +{ + return canonicalize_depthwiseconv2dnative(node->graph(), node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.h new file mode 100644 index 000000000..704e1ade9 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_DEPTHWISE_CONV2D_NATIVE_CANONICALIZER_H__ +#define __MOCO_TF_DEPTHWISE_CONV2D_NATIVE_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFDepthwiseConv2dNative to Canonical DepthwiseConv2D + */ +class DepthwiseConv2dNativeCanonicalizer : public SimpleNodeTransform<moco::TFDepthwiseConv2dNative> +{ +public: + const char *name(void) const final { return "DepthwiseConv2dNativeCanonicalizer"; } + +public: + bool transform(moco::TFDepthwiseConv2dNative *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_DEPTHWISE_CONV2D_NATIVE_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.cpp new file mode 100644 index 000000000..3b680cf04 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "IdentityCanonicalizer.h" + +#include "Convert.h" + +#include <moco/IR/TFDialect.h> + +#include <moco/Names.h> +#include <moco/Log.h> + +namespace +{ + +bool canonicalize_identity(loco::Graph *graph, moco::TFIdentity *node) +{ + LOGGER(l); + + /** + * @note This will replace TFIdentity node with Canonical Forward + * + * Before + * A -- TFIdentity -- C + * + * After + * /- TFIdentity -- + * A -- Forward -- C + * + * Where + * A : input of TFIdentity + * C : a node that uses TFIdentity as an input + * TFIdentity is disconnected from the output + */ + + INFO(l) << "TFNodeCanonicalize TFIdentity begin"; + + auto forward_node = graph->nodes()->create<loco::Forward>(); + + auto node_A = node->input(); + + forward_node->input(node_A); + + // update graph + replace(node).with(forward_node); + + INFO(l) << "TFNodeCanonicalize TFIdentity done"; + + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool IdentityCanonicalizer::transform(TFIdentity *node) const +{ + return canonicalize_identity(node->graph(), node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.h new file mode 100644 index 000000000..59b2894c5 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_IDENTITY_CANONICALIZER_H__ +#define __MOCO_TF_IDENTITY_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFIdentity to Canonical Forward + */ +class IdentityCanonicalizer : public SimpleNodeTransform<moco::TFIdentity> +{ +public: + const char *name(void) const final { return "IdentityCanonicalizer"; } + +public: + bool transform(moco::TFIdentity *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_IDENTITY_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.cpp new file mode 100644 index 000000000..06a605717 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MaxPoolCanonicalizer.h" + +#include <moco/IR/TFDialect.h> +#include <moco/Support/TFShapeInferenceHelper.h> + +#include "CodecHelper.h" + +#include <moco/Log.h> + +namespace +{ + +bool canonicalize_maxpool2d(loco::Graph *graph, moco::TFMaxPool *node) +{ + LOGGER(l); + + /** + * @note This will replace TFMaxPool node with Canonical FeatureEncode + + * MaxPool2D + FeatureDecode + * + * Before + * A -- TFMaxPool -- C + * + * After + * +- TFMaxPool -- + * | + * A -+- FeatureEncode -- MaxPool2D -- FeatureDecode -- C + * + * Where + * A : value of TFMaxPool + * C : a node that uses TFMaxPool as an input + * TFMaxPool is disconnected from other nodes + */ + + auto data_layout = plier::tf::as_data_layout(node->data_layout()); + + auto feature_enc = graph->nodes()->create<loco::FeatureEncode>(); + auto maxPool2d_node = graph->nodes()->create<loco::MaxPool2D>(); + auto feature_dec = graph->nodes()->create<loco::FeatureDecode>(); + + set_feature_enc(feature_enc, data_layout); + set_feature_dec(feature_dec, data_layout); + + // paddata to pad + auto input_shape = moco::node_shape(node->input()); + assert(input_shape.domain() != loco::Domain::Unknown); + + auto node_stride = moco::stride_of(node->strides(), node->data_layout()); + auto node_window = moco::window_of(node->ksize(), node->data_layout()); + + moco::Padding2DInference infer_padding2d; + + infer_padding2d.padding(node->padding()); + infer_padding2d.stride(node_stride); + infer_padding2d.window(node_window); + + auto input_feature_shape = moco::as_feature_shape(input_shape, node->data_layout()); + auto input_plane_shape = moco::make_plane_shape(input_feature_shape); + + *maxPool2d_node->pad() = infer_padding2d(input_plane_shape); + *maxPool2d_node->stride() = node_stride; + *maxPool2d_node->window() = node_window; + + INFO(l) << "Canonicalize TFMaxPool pad = T " << maxPool2d_node->pad()->top() << ", L " + << maxPool2d_node->pad()->left() << ", B " << maxPool2d_node->pad()->bottom() << ", R " + << maxPool2d_node->pad()->right() << std::endl; + + // update graph + auto node_A = node->input(); + + // update connections + feature_enc->input(node_A); + maxPool2d_node->ifm(feature_enc); + feature_dec->input(maxPool2d_node); + + // replace node + replace(node).with(feature_dec); + + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool MaxPoolCanonicalizer::transform(TFMaxPool *node) const +{ + return canonicalize_maxpool2d(node->graph(), node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.h new file mode 100644 index 000000000..c58ade528 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_MAXPOOL_CANONICALIZER_H__ +#define __MOCO_TF_MAXPOOL_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFMaxPool to Canonical MaxPool2D + */ +class MaxPoolCanonicalizer : public SimpleNodeTransform<moco::TFMaxPool> +{ +public: + const char *name(void) const final { return "MaxPoolCanonicalizer"; } + +public: + bool transform(moco::TFMaxPool *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_MAXPOOL_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.cpp new file mode 100644 index 000000000..92634d01f --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MaximumCanonicalizer.h" + +#include <moco/IR/TFDialect.h> + +#include "TFEltwiseBinaryCanonicalzeHelper.h" + +namespace moco +{ +namespace tf +{ + +bool MaximumCanonicalizer::transform(moco::TFMaximum *node) const +{ + return canonicalize_eltwise_binary_node(node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.h new file mode 100644 index 000000000..baff4d7ad --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_MAXIMUM_CANONICALIZER_H__ +#define __MOCO_TF_MAXIMUM_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFMaximum to Canonical EltwiseMax + */ +class MaximumCanonicalizer : public SimpleNodeTransform<moco::TFMaximum> +{ +public: + const char *name(void) const final { return "MaximumCanonicalizer"; } + +public: + bool transform(moco::TFMaximum *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_MAXIMUM_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.cpp new file mode 100644 index 000000000..69eaf7900 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MeanCanonicalizer.h" +#include "TFReduceCanonicalzeHelper.h" + +namespace moco +{ +namespace tf +{ + +bool MeanCanonicalizer::transform(moco::TFMean *node) const +{ + return canonicalize_reduce_node(node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.h new file mode 100644 index 000000000..469d7e3cd --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_MEAN_CANONICALIZER_H__ +#define __MOCO_TF_MEAN_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Canonicalize TF-dialect TFMean into canonical TensorReduce(Mean) node + */ +class MeanCanonicalizer : public SimpleNodeTransform<moco::TFMean> +{ +public: + const char *name(void) const final { return "MeanCanonicalizer"; } + +public: + bool transform(moco::TFMean *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_MEAN_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/MulCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/MulCanonicalizer.cpp new file mode 100644 index 000000000..d02f71361 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/MulCanonicalizer.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MulCanonicalizer.h" + +#include <moco/IR/TFDialect.h> + +#include "TFEltwiseBinaryCanonicalzeHelper.h" + +namespace moco +{ +namespace tf +{ + +bool MulCanonicalizer::transform(moco::TFMul *node) const +{ + return canonicalize_eltwise_binary_node(node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/MulCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/MulCanonicalizer.h new file mode 100644 index 000000000..480eec700 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/MulCanonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_MUL_CANONICALIZER_H__ +#define __MOCO_TF_MUL_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFMul to Canonical EltwiseMul + */ +class MulCanonicalizer : public SimpleNodeTransform<moco::TFMul> +{ +public: + const char *name(void) const final { return "MulCanonicalizer"; } + +public: + bool transform(moco::TFMul *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_MUL_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.cpp new file mode 100644 index 000000000..10816f47c --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PadCanonicalizer.h" + +#include <moco/IR/TFDialect.h> + +#include "loco/Service/TypeInference.h" + +#include <stdex/Memory.h> + +namespace +{ + +bool canonicalize_pad(loco::Graph *graph, moco::TFPad *node) +{ + /** + * @note This will replace TFPad node with Canonical TensorConstantPad + * + * Before + * input --- TFPad -- C + * paddings --/ + * After + * paddings ------- TFPad -- + * / + * input ----------- TensorConstantPad -- C + * ConstGen --------/ + * Where + * input : input of TFPad + * paddings : paddings of TFPad. it becomes TensorConstantPad's attribute. + * C : a node that uses TFPad as an input. TFPad is disconnected from C. + * ConstGen : constant value of Pad. TFPad has zero value by default. + */ + + auto pad_node = graph->nodes()->create<loco::TensorConstantPad>(); + + auto constant_node = graph->nodes()->create<loco::ConstGen>(); + + auto input_node = node->input(); + // TODO: support other dtype. + assert(loco::dtype_get(input_node) == loco::DataType::FLOAT32); + constant_node->dtype(loco::DataType::FLOAT32); + // TODO: constant node changes to scalar when it is implemented. + constant_node->shape({1}); + constant_node->size<loco::DataType::FLOAT32>(1); + constant_node->at<loco::DataType::FLOAT32>(0) = 0.0f; + + auto const_paddings_node = dynamic_cast<loco::ConstGen *>(node->paddings()); + // TODO: support S64 type. + assert(const_paddings_node->dtype() == loco::DataType::S32); + assert(const_paddings_node->rank() == 2); + assert(const_paddings_node->dim(1).value() == 2); + + auto padding = pad_node->padding(); + uint32_t padding_rank = const_paddings_node->dim(0).value(); + padding->rank(padding_rank); + + for (uint32_t i = 0; i < padding_rank; i++) + { + padding->front(i) = const_paddings_node->at<loco::DataType::S32>(i << 1); + padding->back(i) = const_paddings_node->at<loco::DataType::S32>((i << 1) + 1); + } + + // update connections + pad_node->input(input_node); + pad_node->constant(constant_node); + + // replace node + replace(node).with(pad_node); + + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool PadCanonicalizer::transform(TFPad *node) const +{ + return canonicalize_pad(node->graph(), node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.h new file mode 100644 index 000000000..64bb6041a --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_PAD_CANONICALIZER_H__ +#define __MOCO_TF_PAD_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFPad to Canonical TensorConstantPad + */ +class PadCanonicalizer final : public SimpleNodeTransform<moco::TFPad> +{ +public: + const char *name(void) const final { return "PadCanonicalizer"; } + +public: + bool transform(moco::TFPad *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_PAD_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.cpp new file mode 100644 index 000000000..f568e909f --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PlaceholderCanonicalizer.h" + +#include <moco/IR/TFDialect.h> + +#include <moco/Names.h> +#include <moco/Log.h> + +namespace +{ + +bool canonicalize_placeholder(loco::Graph *graph, moco::TFPlaceholder *node) +{ + LOGGER(l); + + /** + * @note This will replace TFPlaceholder node with Canonical Pull + * + * Before + * TFPlaceholder -- C + * + * After + * TFPlaceholder - + * Pull -- C + * + * Where + * C : a node that uses TFPlaceholder as an input + * TFPlaceholder is disconnected from other nodes + */ + + INFO(l) << "PlaceholderCanonicalizer begin"; + + auto pull_node = graph->nodes()->create<loco::Pull>(); + + // copy properties + auto dtype = node->dtype(); + pull_node->dtype(dtype); + + auto rank = node->rank(); + + if (rank == 0) + { + // This routine implements a workaround that converts a scalar constant (rank-0 tensor) + // into a rank-1 tensor of shape [1]. + // + // TODO Revise this implementation later + pull_node->rank(1); + pull_node->dim(0) = 1; + } + else + { + pull_node->rank(rank); + + for (uint32_t r = 0; r < rank; ++r) + { + if (node->dim(r).known()) + pull_node->dim(r) = node->dim(r); + else + pull_node->dim(r).unset(); + } + } + + // set loco::Pull GraphInputIndex + pull_node->index(moco::index(node)); + + // update graph + replace(node).with(pull_node); + + INFO(l) << "PlaceholderCanonicalizer done"; + + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool PlaceholderCanonicalizer::transform(TFPlaceholder *node) const +{ + return canonicalize_placeholder(node->graph(), node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.h new file mode 100644 index 000000000..66eafe6af --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_PLACEHOLDER_CANONICALIZER_H__ +#define __MOCO_TF_PLACEHOLDER_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/Nodes/TFPlaceholder.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFPlaceholder to Canonical Pull + * + * @note GraphInputIndex is copied to Pull + */ +class PlaceholderCanonicalizer : public SimpleNodeTransform<::moco::TFPlaceholder> +{ +public: + const char *name(void) const final { return "PlaceholderCanonicalizer"; } + +public: + bool transform(moco::TFPlaceholder *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_PLACEHOLDER_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.cpp new file mode 100644 index 000000000..a448d85fa --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "RealDivCanonicalizer.h" + +#include <moco/IR/TFDialect.h> + +#include "TFEltwiseBinaryCanonicalzeHelper.h" + +namespace moco +{ +namespace tf +{ + +bool RealDivCanonicalizer::transform(moco::TFRealDiv *node) const +{ + return canonicalize_eltwise_binary_node(node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.h new file mode 100644 index 000000000..76e1bd377 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_REALDIV_CANONICALIZER_H__ +#define __MOCO_TF_REALDIV_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFRealDiv to Canonical EltwiseDiv + */ +class RealDivCanonicalizer : public SimpleNodeTransform<moco::TFRealDiv> +{ +public: + const char *name(void) const final { return "RealDivCanonicalizer"; } + +public: + bool transform(moco::TFRealDiv *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_REALDIV_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.cpp new file mode 100644 index 000000000..c53a880a8 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Relu6Canonicalizer.h" + +#include <moco/IR/TFDialect.h> + +#include <stdex/Memory.h> + +namespace +{ + +bool canonicalize_relu6(loco::Graph *graph, moco::TFRelu6 *node) +{ + /** + * @note This will replace TFRelu6 node with Canonical ReLU6 + * + * Before + * A --- TFRelu6 -- C + * After + * +- TFRelu6 -- + * | + * A -+- ReLU6 -- C + * + * Where + * A : features of TFRelu6 + * C : a node that uses TFRelu6 as an input + * TFRelu6 is disconnected from C + */ + + auto relu6_node = graph->nodes()->create<loco::ReLU6>(); + + auto node_A = node->features(); + + // update connections + relu6_node->input(node_A); + + // replace node + replace(node).with(relu6_node); + + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool Relu6Canonicalizer::transform(TFRelu6 *node) const +{ + return canonicalize_relu6(node->graph(), node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.h b/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.h new file mode 100644 index 000000000..d8ad5db8e --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_RELU6_CANONICALIZER_H__ +#define __MOCO_TF_RELU6_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFRelu6 to Canonical ReLU6 + */ +class Relu6Canonicalizer : public SimpleNodeTransform<moco::TFRelu6> +{ +public: + const char *name(void) const final { return "Relu6Canonicalizer"; } + +public: + bool transform(moco::TFRelu6 *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_RELU6_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.cpp new file mode 100644 index 000000000..7965dc931 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ReluCanonicalizer.h" + +#include <moco/IR/TFDialect.h> + +#include <stdex/Memory.h> + +namespace +{ + +bool canonicalize_relu(loco::Graph *graph, moco::TFRelu *node) +{ + /** + * @note This will replace TFRelu node with Canonical ReLU + * + * Before + * A --- TFRelu -- C + * After + * +- TFRelu -- + * | + * A -+- ReLU -- C + * + * Where + * A : features of TFRelu + * C : a node that uses TFRelu as an input + * TFRelu is disconnected from C + */ + + auto relu_node = graph->nodes()->create<loco::ReLU>(); + + auto node_A = node->features(); + + // update connections + relu_node->input(node_A); + + // replace node + replace(node).with(relu_node); + + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool ReluCanonicalizer::transform(TFRelu *node) const +{ + return canonicalize_relu(node->graph(), node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.h new file mode 100644 index 000000000..e27abe158 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_RELU_CANONICALIZER_H__ +#define __MOCO_TF_RELU_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFRelu to Canonical ReLU + */ +class ReluCanonicalizer : public SimpleNodeTransform<moco::TFRelu> +{ +public: + const char *name(void) const final { return "ReluCanonicalizer"; } + +public: + bool transform(moco::TFRelu *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_RELU_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.cpp new file mode 100644 index 000000000..b944568e0 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.cpp @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ReshapeCanonicalizer.h" + +#include <moco/IR/TFDialect.h> + +#include <moco/Log.h> +#include <plier/tf/Convert.h> +#include <oops/UserExn.h> + +#include <cassert> + +namespace +{ +using plier::tf::DataLayout; + +/** + * @brief Check whether given 'new shape' arg is a fixed shape input for Reshape + * + * ConstNode can be moco::TFConst or loco::ConstGen + */ +template <typename ConstNode> bool is_fixed_shape_input(ConstNode *const_shape_input) +{ + if (const_shape_input == nullptr) + return false; + + // Shape input should be integer tensor of rank 1, e.g. [2, 3, 4] or [3, -1] + // TODO Support other possible data types, e.g. S64 + assert(const_shape_input->dtype() == loco::DataType::S32); + assert(const_shape_input->rank() == 1); + + auto shape_rank = const_shape_input->dim(0).value(); + assert(shape_rank > 0); + + for (uint32_t axis = 0; axis < shape_rank; ++axis) + { + auto shape_dim = const_shape_input->template at<loco::DataType::S32>(axis); + if (shape_dim == -1) + { + // has wildcard dimension, i.e. dynamic reshape + return false; + } + if (!(shape_dim >= 1)) + { + throw oops::UserExn("New shape of Reshape has invalid dimension"); + } + } + return true; +} + +/// @note Currently only supports to canonicalize Fixed Reshape +bool canonicalize_reshape(loco::Graph *graph, moco::TFReshape *node) +{ + LOGGER(l); + INFO(l) << "TFNodeCanonicalize TFReshape begin"; + + /** + * This rule canonicalizes TFReshape only when its output shape is known at + * compile time, i.e. fixed reshape case. + * TODO Support other cases like dynamic reshape + * + * This will replace TFReshape + TFConst or Canonical ConstGen(as shape input) + * node pair into Canonical Reshape<ReshapeType::Fixed>, or 'FixedReshape'. + * Shape input (TFConst or Canonical ConstGen) should not have wildcard + * dimension to be converted to FixedReshape. + * + * Before + * TFConst (shape) + * or --- + * ConstGen \ + * \ + * In --------- TFReshape ------- Out(s) + * (tensor) + * + * After + * TFConst + * or --- + * ConstGen \ + * \ + * ---------- TFReshape + * / + * In -------- FixedReshape ----- Out(s) + */ + + // create loco node to replace + auto fixed_reshape = graph->nodes()->create<loco::FixedReshape>(); + + // Supports 2 cases for Reshape's shape input: + // TF-dialect TFConst or Canonical ConstGen + loco::Node *shape_input = node->shape(); + auto tfconst_shape_input = dynamic_cast<moco::TFConst *>(shape_input); + auto constgen_shape_input = dynamic_cast<loco::ConstGen *>(shape_input); + + if (tfconst_shape_input) + { + // Only support fixed reshape + // TODO support dynamic reshape + if (!(is_fixed_shape_input(tfconst_shape_input))) + { + throw oops::UserExn("Supports only fixed reshape", node->name()); + } + + auto rank = tfconst_shape_input->dim(0).value(); + fixed_reshape->rank(rank); + for (uint32_t axis = 0; axis < rank; ++axis) + { + fixed_reshape->dim(axis) = tfconst_shape_input->at<loco::DataType::S32>(axis); + } + } + else if (constgen_shape_input) + { + // ditto + if (!(is_fixed_shape_input(constgen_shape_input))) + { + throw oops::UserExn("Supports only fixed reshape", node->name()); + } + + auto rank = constgen_shape_input->dim(0).value(); + fixed_reshape->rank(rank); + for (uint32_t axis = 0; axis < rank; ++axis) + { + fixed_reshape->dim(axis) = constgen_shape_input->at<loco::DataType::S32>(axis); + } + } + else + { + // TODO support dynamic reshape from not const node + throw oops::UserExn("Supports only const node as input shape", node->name()); + } + + // replace + auto in = node->tensor(); + fixed_reshape->input(in); + + replace(node).with(fixed_reshape); + + INFO(l) << "TFNodeCanonicalize TFReshape done"; + + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool ReshapeCanonicalizer::transform(TFReshape *node) const +{ + return canonicalize_reshape(node->graph(), node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.h new file mode 100644 index 000000000..1a792024e --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_RESHAPE_CANONICALIZER_H__ +#define __MOCO_TF_RESHAPE_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFReshape to Canonical Reshape + */ +class ReshapeCanonicalizer : public SimpleNodeTransform<moco::TFReshape> +{ +public: + const char *name(void) const final { return "ReshapeCanonicalizer"; } + +public: + bool transform(moco::TFReshape *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_RESHAPE_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.cpp new file mode 100644 index 000000000..c31dbf6d6 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "RsqrtCanonicalizer.h" + +#include <moco/IR/TFDialect.h> +#include <moco/Support/TFShapeInferenceHelper.h> + +#include <moco/Log.h> + +#include <loco/Service/TypeInference.h> + +#include <stdex/Memory.h> +#include <oops/UserExn.h> + +namespace +{ + +template <typename T> +bool prepare_const_gen(loco::ConstGen *const_node, const loco::TensorShape &tensorshape, T value); + +template <> +bool prepare_const_gen<float>(loco::ConstGen *const_node, const loco::TensorShape &tensorshape, + float value) +{ + LOGGER(l); + + uint32_t const_num_elements = 1; + + auto dtype = loco::DataType::FLOAT32; + const_node->dtype(dtype); + + auto rank = tensorshape.rank(); + const_node->rank(rank); + for (uint32_t r = 0; r < rank; ++r) + { + if (tensorshape.dim(r).known()) + const_node->dim(r) = tensorshape.dim(r); + else + return false; + + assert(tensorshape.dim(r).value() > 0); + + const_num_elements *= tensorshape.dim(r).value(); + } + + INFO(l) << "prepare_const_gen : Elements = " << const_num_elements; + + const_node->size<loco::DataType::FLOAT32>(const_num_elements); + for (uint32_t i = 0; i < const_num_elements; ++i) + { + const_node->at<loco::DataType::FLOAT32>(i) = value; + } + + return true; +} + +bool canonicalize_rsqrt(loco::Graph *graph, moco::TFRsqrt *node) +{ + /** + * @note This will replace TFRsqrt node with Canonical EltwiseSqrt + EltwiseRealDiv + * + * Before + * A --- TFRsqrt -- C + * After + * +- TFRsqrt -- + * | + * | ConstGen --+ + * | \ + * A -+- EltwiseSqrt -- EltwiseDiv -- C + * + * Where + * A : features of TFRsqrt + * C : a node that uses TFSqrt as an input + * TFRsqrt is disconnected from C + * TFRsqrt is converted to 1 / EltwiseSqrt + */ + + auto nodeshape = moco::node_shape(node); + if (nodeshape.domain() == loco::Domain::Unknown) + { + // We need this shape information + assert(false); // this shouldn't happen, let's add an alarm + return false; + } + auto tensorshape = nodeshape.as<loco::TensorShape>(); + + if (!loco::dtype_known(node)) + { + // We need type of this node + return false; + } + + auto sqrt_node = graph->nodes()->create<loco::EltwiseSqrt>(); + auto eltdiv_node = graph->nodes()->create<loco::EltwiseDiv>(); + auto const_node = graph->nodes()->create<loco::ConstGen>(); + + auto dtype = loco::dtype_get(node); + + switch (dtype) + { + case loco::DataType::FLOAT32: + if (!prepare_const_gen<float>(const_node, tensorshape, 1.0f)) + throw oops::UserExn("Cannot handle unknown shape", node->name()); + break; + + default: + throw oops::UserExn("Unsupported data type", node->name()); + } + + auto node_A = node->x(); + + // update connections + sqrt_node->input(node_A); + eltdiv_node->lhs(const_node); + eltdiv_node->rhs(sqrt_node); + + // replace node + replace(node).with(eltdiv_node); + + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool RsqrtCanonicalizer::transform(TFRsqrt *node) const +{ + return canonicalize_rsqrt(node->graph(), node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.h new file mode 100644 index 000000000..7fd4ff697 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_RSQRT_CANONICALIZER_H__ +#define __MOCO_TF_RSQRT_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFRsqrt to Canonical EltwiseDiv + EltwiseSqrt + */ +class RsqrtCanonicalizer : public SimpleNodeTransform<moco::TFRsqrt> +{ +public: + const char *name(void) const final { return "RsqrtCanonicalizer"; } + +public: + bool transform(moco::TFRsqrt *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_RSQRT_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.cpp new file mode 100644 index 000000000..98af7b693 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SoftmaxCanonicalizer.h" + +#include <moco/IR/TFDialect.h> +#include <moco/Support/TFShapeInferenceHelper.h> + +#include <moco/Log.h> + +namespace +{ + +bool canonicalize_softmax(loco::Graph *graph, moco::TFSoftmax *node) +{ + LOGGER(l); + + INFO(l) << "TFNodeCanonicalize TFSoftmax begin"; + + /** + * This will replace shape inferred TFSoftmax node into canonical TensorSoftmax + * + * Before + * In ---- TFSoftmax ---- Out(s) + * + * After + * ------ TFSoftmax + * / + * In ---- TensorSoftmax ----- Out(s) + */ + + auto nodeshape = moco::node_shape(node); + // Canonicalization into TensorSoftmax is valid when softmax has shape info + assert(nodeshape.domain() != loco::Domain::Unknown); + + auto softmax_tensor_shape = nodeshape.as<loco::TensorShape>(); + + // Create loco node to replace + auto softmax = graph->nodes()->create<loco::TensorSoftmax>(); + + // replace + auto in = node->logits(); + softmax->input(in); + softmax->axis(softmax_tensor_shape.rank() - 1); + replace(node).with(softmax); + + INFO(l) << "TFNodeCanonicalize TFSoftmax done"; + + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool SoftmaxCanonicalizer::transform(TFSoftmax *node) const +{ + return canonicalize_softmax(node->graph(), node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.h new file mode 100644 index 000000000..ebaf04cfe --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_SOFTMAX_CANONICALIZER_H__ +#define __MOCO_TF_SOFTMAx_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** +* @brief Canonicalize TF-dialect TFSoftmax into canonical Softmax node +*/ +class SoftmaxCanonicalizer : public SimpleNodeTransform<moco::TFSoftmax> +{ +public: + const char *name(void) const final { return "SoftmaxCanonicalizer"; } + +public: + bool transform(moco::TFSoftmax *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_SOFTMAX_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.cpp new file mode 100644 index 000000000..89b9b8a44 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SqrtCanonicalizer.h" + +#include <moco/IR/TFDialect.h> + +namespace +{ + +bool canonicalize_sqrt(loco::Graph *graph, moco::TFSqrt *node) +{ + /** + * @note This will replace TFSqrt node with Canonical EltwiseSqrt + * + * Before + * A --- TFSqrt -- C + * After + * +- TFSqrt -- + * | + * A -+- EltwiseSqrt -- C + * + * Where + * A : features of TFSqrt + * C : a node that uses TFSqrt as an input + * TFSqrt is disconnected from C + */ + + auto sqrt_node = graph->nodes()->create<loco::EltwiseSqrt>(); + + auto node_A = node->x(); + + // update connections + sqrt_node->input(node_A); + + // replace node + replace(node).with(sqrt_node); + + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool SqrtCanonicalizer::transform(TFSqrt *node) const +{ + return canonicalize_sqrt(node->graph(), node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.h new file mode 100644 index 000000000..3f7ffead8 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_SQRT_CANONICALIZER_H__ +#define __MOCO_TF_SQRT_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFsqrt to Canonical EltwiseSqrt + */ +class SqrtCanonicalizer : public SimpleNodeTransform<moco::TFSqrt> +{ +public: + const char *name(void) const final { return "SqrtCanonicalizer"; } + +public: + bool transform(moco::TFSqrt *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_SQRT_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.cpp new file mode 100644 index 000000000..f5b991206 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SqueezeCanonicalizer.h" + +#include <moco/IR/TFDialect.h> +#include <moco/Support/TFShapeInferenceHelper.h> + +#include <moco/Log.h> + +namespace +{ + +bool canonicalize_squeeze_to_reshape(loco::Graph *graph, moco::TFSqueeze *node) +{ + LOGGER(l); + + INFO(l) << "TFNodeCanonicalize TFSqueeze begin"; + + /** + * This will replace shape inferred TFSqueeze node into canonical FixedReshape + * + * Before + * In ---- TFSqueeze ---- Out(s) + * + * After + * ------ TFSqueeze + * / + * In ---- FixedReshape ----- Out(s) + */ + + auto nodeshape = moco::node_shape(node); + // canonicalize into FixedReshape is valid when squeeze has shape info + // TODO Support general Squeeze case + assert(nodeshape.domain() != loco::Domain::Unknown); + + auto squeeze_tensor_shape = nodeshape.as<loco::TensorShape>(); + + // Create loco node to replace + auto reshape = graph->nodes()->create<loco::FixedReshape>(); + + // Copy shape + reshape->rank(squeeze_tensor_shape.rank()); + for (uint32_t axis = 0; axis < squeeze_tensor_shape.rank(); ++axis) + { + assert(squeeze_tensor_shape.dim(axis).known()); + reshape->dim(axis) = squeeze_tensor_shape.dim(axis); + } + + // replace + auto in = node->input(); + reshape->input(in); + replace(node).with(reshape); + + INFO(l) << "TFNodeCanonicalize TFSqueeze done"; + + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool SqueezeCanonicalizer::transform(TFSqueeze *node) const +{ + return canonicalize_squeeze_to_reshape(node->graph(), node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.h new file mode 100644 index 000000000..28a1442bd --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_SQUEEZE_CANONICALIZER_H__ +#define __MOCO_TF_SQUEEZE_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Canonicalize TF-dialect TFSqueeze into canonical FixedReshape node + * + * @note There is no canonical Squeeze node + */ +class SqueezeCanonicalizer : public SimpleNodeTransform<moco::TFSqueeze> +{ +public: + const char *name(void) const final { return "SqueezeCanonicalizer"; } + +public: + bool transform(moco::TFSqueeze *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_SQUEEZE_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.cpp new file mode 100644 index 000000000..574fa3993 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StopGradientCanonicalizer.h" + +#include <moco/IR/TFDialect.h> + +#include <moco/Log.h> + +namespace +{ + +bool canonicalize_stopgradient(loco::Graph *graph, moco::TFStopGradient *node) +{ + LOGGER(l); + + INFO(l) << "TFNodeCanonicalize TFStopGradient begin"; + + /** + * This will replace shape inferred TFStopGradient node into canonical Forward + * + * Before + * In --- TFStopGradient --- Out(s) + * + * After + * -- TFStopGradient + * / + * In --- Forward --- Out(s) + */ + + // Create loco node to replace + auto forward_node = graph->nodes()->create<loco::Forward>(); + + // update connection + forward_node->input(node->input()); + + // replace node + replace(node).with(forward_node); + + INFO(l) << "TFNodeCanonicalize TFStopGradient done"; + + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool StopGradientCanonicalizer::transform(TFStopGradient *node) const +{ + return canonicalize_stopgradient(node->graph(), node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.h new file mode 100644 index 000000000..6a17728a6 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_STOPGRADIENT_CANONICALIZER_H__ +#define __MOCO_TF_STOPGRADIENT_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** +* @brief Canonicalize TF-dialect TFStopGradient into canonical Forward node +*/ +class StopGradientCanonicalizer : public SimpleNodeTransform<moco::TFStopGradient> +{ +public: + const char *name(void) const final { return "StopGradientCanonicalizer"; } + +public: + bool transform(moco::TFStopGradient *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_STOPGRADIENT_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/SubCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/SubCanonicalizer.cpp new file mode 100644 index 000000000..c518b7d64 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/SubCanonicalizer.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SubCanonicalizer.h" + +#include <moco/IR/TFDialect.h> + +#include "TFEltwiseBinaryCanonicalzeHelper.h" + +namespace moco +{ +namespace tf +{ + +bool SubCanonicalizer::transform(moco::TFSub *node) const +{ + return canonicalize_eltwise_binary_node(node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/SubCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/SubCanonicalizer.h new file mode 100644 index 000000000..f715cc86c --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/SubCanonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_SUB_CANONICALIZER_H__ +#define __MOCO_TF_SUB_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFSub to Canonical EltwiseSub + */ +class SubCanonicalizer : public SimpleNodeTransform<moco::TFSub> +{ +public: + const char *name(void) const final { return "SubCanonicalizer"; } + +public: + bool transform(moco::TFSub *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_SUB_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.cpp new file mode 100644 index 000000000..081e0e5f9 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TFPushCanonicalizer.h" + +#include <moco/IR/TFDialect.h> + +#include <stdex/Memory.h> + +namespace +{ + +bool canonicalize_push(loco::Graph *graph, moco::TFPush *node) +{ + /** + * @note This will replace TFRelu node with Canonical ReLU + * + * Before + * A --- TFPush + * After + * +- TFPush + * | + * A -+- Push + * + * Where + * A : from of TFPush + * TFPush will have no GraphOutputIndex + * Push will have GraphOutputIndex that from TFPush + */ + + auto push_node = graph->nodes()->create<loco::Push>(); + + auto node_A = node->from(); + + // update connections + push_node->from(node_A); + + // update output index + push_node->index(node->index()); + node->index_reset(); + + // replace node + replace(node).with(push_node); + + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool TFPushCanonicalizer::transform(TFPush *node) const +{ + return canonicalize_push(node->graph(), node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.h new file mode 100644 index 000000000..569a71f82 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_PUSH_CANONICALIZER_H__ +#define __MOCO_TF_PUSH_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFPush to Canonical Push + */ +class TFPushCanonicalizer : public SimpleNodeTransform<moco::TFPush> +{ +public: + const char *name(void) const final { return "TFPushCanonicalizer"; } + +public: + bool transform(moco::TFPush *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_PUSH_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.cpp new file mode 100644 index 000000000..3f48a50fc --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TanhCanonicalizer.h" + +#include <moco/IR/TFDialect.h> + +#include <stdex/Memory.h> + +namespace +{ + +bool canonicalize_tanh(loco::Graph *graph, moco::TFTanh *node) +{ + /** + * @note This will replace TFTanh node with Canonical Tanh + * + * Before + * A --- TFTanh -- C + * After + * +- TFTanh -- + * | + * A -+-- Tanh --- C + * + * Where + * A : x of TFTanh + * C : a node that uses TFTanh as an input + * TFTanh is disconnected from C + */ + + auto tanh_node = graph->nodes()->create<loco::Tanh>(); + + auto node_A = node->x(); + + // update connections + tanh_node->input(node_A); + + // replace node + replace(node).with(tanh_node); + + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool TanhCanonicalizer::transform(TFTanh *node) const +{ + return canonicalize_tanh(node->graph(), node); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.h new file mode 100644 index 000000000..af5e79fb5 --- /dev/null +++ b/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_TANH_CANONICALIZER_H__ +#define __MOCO_TF_TANH_CANONICALIZER_H__ + +#include "Transform.h" +#include "SimpleNodeTransform.h" + +#include <moco/IR/TFNodes.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Convert TFTanh to Canonical Tanh + */ +class TanhCanonicalizer : public SimpleNodeTransform<moco::TFTanh> +{ +public: + const char *name(void) const final { return "TanhCanonicalizer"; } + +public: + bool transform(moco::TFTanh *) const override; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_TANH_CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalizer.cpp b/compiler/moco-tf/src/Canonicalizer.cpp new file mode 100644 index 000000000..04bc7c57a --- /dev/null +++ b/compiler/moco-tf/src/Canonicalizer.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Canonicalizer.h" + +#include "Knob.h" +#include "ProgressReporter.h" + +#include "Transforms/ShapeInferencePass.h" +#include "Transforms/TypeInferencePass.h" + +#include "Canonicalization/AddCanonicalizer.h" +#include "Canonicalization/AvgPoolCanonicalizer.h" +#include "Canonicalization/BiasAddCanonicalizer.h" +#include "Canonicalization/ConcatV2Canonicalizer.h" +#include "Canonicalization/ConstCanonicalizer.h" +#include "Canonicalization/Conv2DBackpropInputCanonicalizer.h" +#include "Canonicalization/Conv2DCanonicalizer.h" +#include "Canonicalization/DepthwiseConv2dNativeCanonicalizer.h" +#include "Canonicalization/IdentityCanonicalizer.h" +#include "Canonicalization/MaximumCanonicalizer.h" +#include "Canonicalization/MaxPoolCanonicalizer.h" +#include "Canonicalization/MeanCanonicalizer.h" +#include "Canonicalization/MulCanonicalizer.h" +#include "Canonicalization/PadCanonicalizer.h" +#include "Canonicalization/PlaceholderCanonicalizer.h" +#include "Canonicalization/RealDivCanonicalizer.h" +#include "Canonicalization/ReluCanonicalizer.h" +#include "Canonicalization/Relu6Canonicalizer.h" +#include "Canonicalization/ReshapeCanonicalizer.h" +#include "Canonicalization/RsqrtCanonicalizer.h" +#include "Canonicalization/SoftmaxCanonicalizer.h" +#include "Canonicalization/SqrtCanonicalizer.h" +#include "Canonicalization/SqueezeCanonicalizer.h" +#include "Canonicalization/StopGradientCanonicalizer.h" +#include "Canonicalization/SubCanonicalizer.h" +#include "Canonicalization/TanhCanonicalizer.h" +// For virtual nodes +#include "Canonicalization/TFPushCanonicalizer.h" + +#include <moco/IR/TFDialect.h> +#include <moco/IR/TFNodes.h> + +#include <logo/Phase.h> + +#include <stdex/Memory.h> + +#include <cassert> + +namespace +{ + +/** + * @brief Return true if graph has TFDialect nodes + */ +bool has_tf_nodes(loco::Graph *g) +{ + auto active_nodes = loco::active_nodes(loco::output_nodes(g)); + for (auto node : active_nodes) + { + if (node->dialect() == moco::TFDialect::get()) + { + return true; + } + } + return false; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +void Canonicalizer::canonicalize(loco::Graph *g) const +{ + logo::Phase phase; + + /* TRANSFORM DECLARATION BEGIN */ + // Run shape and type inference at the top + phase.emplace_back(stdex::make_unique<ShapeInferencePass>()); + phase.emplace_back(stdex::make_unique<TypeInferencePass>()); + + phase.emplace_back(stdex::make_unique<AddCanonicalizer>()); + phase.emplace_back(stdex::make_unique<AvgPoolCanonicalizer>()); + if (moco::tf::get<moco::tf::Knob::CanonicalizeBiasAdd>()) + phase.emplace_back(stdex::make_unique<BiasAddCanonicalizer>()); + phase.emplace_back(stdex::make_unique<ConcatV2Canonicalizer>()); + if (moco::tf::get<moco::tf::Knob::CanonicalizeConst>()) + phase.emplace_back(stdex::make_unique<ConstCanonicalizer>()); + phase.emplace_back(stdex::make_unique<Conv2DBackpropInputCanonicalizer>()); + if (moco::tf::get<moco::tf::Knob::CanonicalizeConv2D>()) + phase.emplace_back(stdex::make_unique<Conv2DCanonicalizer>()); + phase.emplace_back(stdex::make_unique<DepthwiseConv2dNativeCanonicalizer>()); + phase.emplace_back(stdex::make_unique<IdentityCanonicalizer>()); + phase.emplace_back(stdex::make_unique<MaximumCanonicalizer>()); + phase.emplace_back(stdex::make_unique<MaxPoolCanonicalizer>()); + phase.emplace_back(stdex::make_unique<MeanCanonicalizer>()); + phase.emplace_back(stdex::make_unique<MulCanonicalizer>()); + phase.emplace_back(stdex::make_unique<PadCanonicalizer>()); + phase.emplace_back(stdex::make_unique<PlaceholderCanonicalizer>()); + phase.emplace_back(stdex::make_unique<RealDivCanonicalizer>()); + phase.emplace_back(stdex::make_unique<ReluCanonicalizer>()); + phase.emplace_back(stdex::make_unique<Relu6Canonicalizer>()); + phase.emplace_back(stdex::make_unique<ReshapeCanonicalizer>()); + phase.emplace_back(stdex::make_unique<RsqrtCanonicalizer>()); + phase.emplace_back(stdex::make_unique<SoftmaxCanonicalizer>()); + phase.emplace_back(stdex::make_unique<SqrtCanonicalizer>()); + // NOTE SquaredDifference is handled in ResolveSquaredDifference + phase.emplace_back(stdex::make_unique<SqueezeCanonicalizer>()); + phase.emplace_back(stdex::make_unique<StopGradientCanonicalizer>()); + phase.emplace_back(stdex::make_unique<SubCanonicalizer>()); + phase.emplace_back(stdex::make_unique<TanhCanonicalizer>()); + // For virtual nodes + phase.emplace_back(stdex::make_unique<TFPushCanonicalizer>()); + /* TRANSFORM DECLARATION END */ + + ProgressReporter prog(g, logo::PhaseStrategy::Restart); + logo::PhaseRunner<logo::PhaseStrategy::Restart> phase_runner{g}; + phase_runner.attach(&prog); + phase_runner.run(phase); + + // Assert if graph has TF dialect nodes + assert(!has_tf_nodes(g)); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Canonicalizer.h b/compiler/moco-tf/src/Canonicalizer.h new file mode 100644 index 000000000..098a6719c --- /dev/null +++ b/compiler/moco-tf/src/Canonicalizer.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CANONICALIZER_H__ +#define __CANONICALIZER_H__ + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +class Canonicalizer final +{ +public: + void canonicalize(loco::Graph *) const; +}; + +} // namespace tf +} // namespace moco + +#endif // __CANONICALIZER_H__ diff --git a/compiler/moco-tf/src/Canonicalizer.test.cpp b/compiler/moco-tf/src/Canonicalizer.test.cpp new file mode 100644 index 000000000..8eaf86f2f --- /dev/null +++ b/compiler/moco-tf/src/Canonicalizer.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Canonicalizer.h" + +#include <loco.h> + +#include <gtest/gtest.h> + +// Canonicalizer SHOULD NOT crash even though a given graph is empty +TEST(Canonicalizer, empty_graph) +{ + moco::tf::Canonicalizer cano; + + loco::Graph g; + + cano.canonicalize(&g); + + SUCCEED(); +} diff --git a/compiler/moco-tf/src/CodecHelper.h b/compiler/moco-tf/src/CodecHelper.h new file mode 100644 index 000000000..85e4e2164 --- /dev/null +++ b/compiler/moco-tf/src/CodecHelper.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CODEC_HELPER_H__ +#define __CODEC_HELPER_H__ + +#include <plier/tf/Convert.h> +#include <stdex/Memory.h> + +namespace +{ + +using plier::tf::DataLayout; + +void set_feature_enc(loco::FeatureEncode *feature_enc, DataLayout data_layout) +{ + auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>(); + + if (data_layout == DataLayout::NHWC) + { + enc->perm()->axis(loco::FeatureAxis::Count) = 0; + enc->perm()->axis(loco::FeatureAxis::Height) = 1; + enc->perm()->axis(loco::FeatureAxis::Width) = 2; + enc->perm()->axis(loco::FeatureAxis::Depth) = 3; + } + else if (data_layout == DataLayout::NCHW) + { + enc->perm()->axis(loco::FeatureAxis::Count) = 0; + enc->perm()->axis(loco::FeatureAxis::Depth) = 1; + enc->perm()->axis(loco::FeatureAxis::Height) = 2; + enc->perm()->axis(loco::FeatureAxis::Width) = 3; + } + + feature_enc->encoder(std::move(enc)); +} + +void set_feature_dec(loco::FeatureDecode *feature_dec, DataLayout data_layout) +{ + auto dec = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>(); + + if (data_layout == DataLayout::NHWC) + { + dec->perm()->axis(loco::FeatureAxis::Count) = 0; + dec->perm()->axis(loco::FeatureAxis::Height) = 1; + dec->perm()->axis(loco::FeatureAxis::Width) = 2; + dec->perm()->axis(loco::FeatureAxis::Depth) = 3; + } + else if (data_layout == DataLayout::NCHW) + { + dec->perm()->axis(loco::FeatureAxis::Count) = 0; + dec->perm()->axis(loco::FeatureAxis::Depth) = 1; + dec->perm()->axis(loco::FeatureAxis::Height) = 2; + dec->perm()->axis(loco::FeatureAxis::Width) = 3; + } + + feature_dec->decoder(std::move(dec)); +} + +} // namespace + +#endif // __CODEC_HELPER_H__ diff --git a/compiler/moco-tf/src/Convert.cpp b/compiler/moco-tf/src/Convert.cpp new file mode 100644 index 000000000..6285f5eab --- /dev/null +++ b/compiler/moco-tf/src/Convert.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <algorithm> +#include <cctype> + +// TODO move to some common file +namespace moco +{ + +std::string str_toupper(std::string s) +{ + // from https://en.cppreference.com/w/cpp/string/byte/toupper + std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::toupper(c); }); + return s; +} + +} // namespace moco diff --git a/compiler/moco-tf/src/Convert.h b/compiler/moco-tf/src/Convert.h new file mode 100644 index 000000000..77dab3700 --- /dev/null +++ b/compiler/moco-tf/src/Convert.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <string> + +// TODO move to some common file +namespace moco +{ + +std::string str_toupper(std::string s); + +} // namespace moco + +#endif // __CONVERT_H__ diff --git a/compiler/moco-tf/src/Convert.test.cpp b/compiler/moco-tf/src/Convert.test.cpp new file mode 100644 index 000000000..b02a597cb --- /dev/null +++ b/compiler/moco-tf/src/Convert.test.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <Convert.h> + +#include <gtest/gtest.h> + +#include <string> + +TEST(moco_Convert, string_toupper) +{ + std::string source = "Hello World!!!"; + std::string convert = moco::str_toupper(source); + + ASSERT_EQ(convert, "HELLO WORLD!!!"); +} diff --git a/compiler/moco-tf/src/Frontend.cpp b/compiler/moco-tf/src/Frontend.cpp new file mode 100644 index 000000000..a17d5dd0e --- /dev/null +++ b/compiler/moco-tf/src/Frontend.cpp @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <moco/tf/Frontend.h> +#include <moco/Importer.h> +#include <moco/IR/TFNode.h> +#include <moco/Log.h> + +#include <moco/Import/GraphBuilderRegistry.h> + +#include "Canonicalizer.h" +#include "Optimizer.h" +#include "TFOptimizer.h" + +#include "Transforms.h" + +#include "Op/COpCall.h" + +#include <loco/Service/ShapeInference.h> + +#include <stdex/Memory.h> +#include <oops/UserExn.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> +#include <sstream> +#include <fstream> +#include <stdexcept> + +#include <fcntl.h> +#include <unistd.h> + +namespace +{ + +bool load_text(std::istream *stream, tensorflow::GraphDef &graph_def) +{ + google::protobuf::io::IstreamInputStream iis(stream); + + return google::protobuf::TextFormat::Parse(&iis, &graph_def); +} + +bool load_binary(std::istream *stream, tensorflow::GraphDef &graph_def) +{ + google::protobuf::io::IstreamInputStream iis(stream); + google::protobuf::io::CodedInputStream cis(&iis); + + return graph_def.ParseFromCodedStream(&cis); +} + +void load_tf(std::istream *stream, moco::tf::Frontend::FileType type, + tensorflow::GraphDef &graph_def) +{ + bool result = (type == moco::tf::Frontend::FileType::Text) ? load_text(stream, graph_def) + : load_binary(stream, graph_def); + if (!result) + { + throw oops::UserExn("Failed to parse prototxt from stream"); + } +} + +// If Placeholder has no shape attribute, set unknown_rank property to true. +void set_unknown_rank(tensorflow::GraphDef &tf_graph_def) +{ + for (auto &n : *tf_graph_def.mutable_node()) + { + if (n.op().compare("Placeholder")) + continue; + + auto iter = n.attr().find("shape"); + if (iter == n.attr().end()) + { + tensorflow::AttrValue attr; + attr.mutable_shape()->set_unknown_rank(true); + n.mutable_attr()->insert({"shape", attr}); + } + } +} + +/** + * @brief Set input shape according to signature if node has unknown shape in GraphDef. + * + * @note If shape you provided is wrong or not enough, it returns false. + */ +bool set_input_shape(const moco::ModelSignature &signature, tensorflow::GraphDef &tf_graph_def) +{ + for (auto &n : *tf_graph_def.mutable_node()) + { + if (n.op().compare("Placeholder")) + continue; + + auto node_shape = n.mutable_attr()->at("shape").mutable_shape(); + auto sig_shape = signature.shape(n.name() + ":0"); + + if (node_shape->unknown_rank() || !node_shape->dim_size()) + { + // If shape in GraphDef is unknown, user must provide the shape info. + if (sig_shape == nullptr) + return false; + node_shape->clear_unknown_rank(); + for (uint32_t i = 0; i < sig_shape->rank(); i++) + node_shape->add_dim()->set_size(-1); + } + + for (uint32_t d = 0; d < node_shape->dim_size(); d++) + { + if (node_shape->mutable_dim(d)->size() == -1) + { + if (sig_shape == nullptr) + return false; + node_shape->mutable_dim(d)->set_size(sig_shape->dim(d)); + } + else + { + // If User provide shape info though it already exists in GraphDef, make sure it matches + // the shape of GraphDef. + if (sig_shape && node_shape->dim(d).size() != sig_shape->dim(d)) + return false; + } + } + } + return true; +} + +void transform_tf(const moco::ModelSignature &signature, tensorflow::GraphDef &tf_graph_def) +{ + set_unknown_rank(tf_graph_def); + if (!set_input_shape(signature, tf_graph_def)) + oops::UserExn("Info you provided may be wrong or not enough. Please check the info file."); +} + +/** + * @brief Returns GraphBuilderRegistry that looks up default registry and additions + * such as custom op + */ +moco::GraphBuilderRegistry make_graph_builder_registry(const moco::ModelSignature &sig) +{ + moco::GraphBuilderRegistry registry{&moco::GraphBuilderRegistry::get()}; + + // build a COpCallGraphBuilder per custom op type + for (const auto &custom_op : sig.customops()) + { + std::unique_ptr<moco::tf::COpCallGraphBuilder> builder = + stdex::make_unique<moco::tf::COpCallGraphBuilder>(&sig); + registry.add(custom_op, std::move(builder)); + } + + return registry; +} + +} // namespace + +// TODO Find a proper place for this function + +namespace +{ + +loco::TensorShape tensor_shape(loco::Node *node) +{ + assert(loco::shape_known(node)); + auto node_shape = loco::shape_get(node); + return node_shape.as<loco::TensorShape>(); +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +Frontend::Frontend() +{ + // DO NOTHING +} + +std::unique_ptr<loco::Graph> Frontend::load(const ModelSignature &signature, const char *modelfile, + FileType type) const +{ + // Using c++ standard library, rather than file descriptor, makes these lines portable + std::ifstream ifs{modelfile, std::ios::in | std::ios::binary}; + return load(signature, &ifs, type); +} + +std::unique_ptr<loco::Graph> Frontend::load(const ModelSignature &signature, std::istream *stream, + FileType type) const +{ + tensorflow::GraphDef tf_graph_def; + + load_tf(stream, type, tf_graph_def); + + transform_tf(signature, tf_graph_def); + + auto graph = import(signature, tf_graph_def); + + return std::move(graph); +} + +std::unique_ptr<loco::Graph> Frontend::import(const ModelSignature &signature, + tensorflow::GraphDef &tf_graph_def) const +{ + LOGGER(frontend); + + // Let's use GraphBuilderRegistry with COpCallGraphBuilder + GraphBuilderRegistry registry = make_graph_builder_registry(signature); + + Importer importer{®istry}; + + INFO(frontend) << ">>"; + INFO(frontend) << ">> Import stage started"; + INFO(frontend) << ">>"; + auto graph = importer.import(signature, tf_graph_def); + + TFOptimizer tfoptimizier; + + // Transform TFNodes + INFO(frontend) << ">>"; + INFO(frontend) << ">> TF optimize stage started"; + INFO(frontend) << ">>"; + tfoptimizier.optimize(graph.get()); + + // Fill graph-level input/output shape + // + // ASSUMPTION! All the shapes are known at this point + for (uint32_t n = 0; n < graph->inputs()->size(); ++n) + { + auto input = graph->inputs()->at(n); + auto input_node = moco::placeholder_node(graph.get(), n); + assert(input_node != nullptr); + input->shape(stdex::make_unique<loco::TensorShape>(tensor_shape(input_node))); + } + + for (uint32_t n = 0; n < graph->outputs()->size(); ++n) + { + auto output = graph->outputs()->at(n); + auto output_node = moco::push_node(graph.get(), n); + assert(output_node != nullptr); + output->shape(stdex::make_unique<loco::TensorShape>(::tensor_shape(output_node))); + } + + // Convert graph to hold only Canonical dialect + Canonicalizer canonicalizer; + + INFO(frontend) << ">>"; + INFO(frontend) << ">> Canonicalize stage started"; + INFO(frontend) << ">>"; + canonicalizer.canonicalize(graph.get()); + + // Optimize imported loco::Graph + Optimizer optimizer; + + INFO(frontend) << ">>"; + INFO(frontend) << ">> Canonical optimize stage started"; + INFO(frontend) << ">>"; + optimizer.optimize(graph.get()); + + return std::move(graph); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Frontend.test.cpp b/compiler/moco-tf/src/Frontend.test.cpp new file mode 100644 index 000000000..c665bd9e3 --- /dev/null +++ b/compiler/moco-tf/src/Frontend.test.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "moco/tf/Frontend.h" + +#include "TestHelper.h" + +#include <sstream> + +#include <gtest/gtest.h> + +namespace +{ + +// clang-format off +const char *pbtxt_000 = STRING_CONTENT( +node { + name: "Placeholder" + op: "Placeholder" + attr { + key: "dtype" + value { type: DT_FLOAT } + } + attr { + key: "shape" + value { + shape { + dim { size: 4 } + } + } + } +} +node { + name: "Identity" + op: "Identity" + input: "Placeholder" + attr { + key: "T" + value { type: DT_FLOAT } + } +} +); +// clang-format on + +} // namespace + +TEST(FrontendTests, testcase_000) +{ + moco::tf::Frontend frontend; + moco::ModelSignature signature; + + signature.add_input(moco::TensorName("Placeholder", 0)); + signature.shape("Placeholder:0", angkor::TensorShape{4}); + signature.add_output(moco::TensorName("Identity", 0)); + + std::stringstream ss{pbtxt_000}; + + auto graph = frontend.load(signature, &ss, moco::tf::Frontend::FileType::Text); + + ASSERT_EQ(graph->inputs()->size(), 1); + ASSERT_EQ(graph->inputs()->at(0)->name(), "Placeholder"); + ASSERT_NE(graph->inputs()->at(0)->shape(), nullptr); + ASSERT_EQ(graph->inputs()->at(0)->shape()->rank(), 1); + ASSERT_EQ(graph->inputs()->at(0)->shape()->dim(0), 4); + + ASSERT_EQ(graph->outputs()->size(), 1); + ASSERT_EQ(graph->outputs()->at(0)->name(), "Identity"); + ASSERT_NE(graph->outputs()->at(0)->shape(), nullptr); + ASSERT_EQ(graph->outputs()->at(0)->shape()->rank(), 1); + ASSERT_EQ(graph->outputs()->at(0)->shape()->dim(0), 4); +} diff --git a/compiler/moco-tf/src/Knob.cpp b/compiler/moco-tf/src/Knob.cpp new file mode 100644 index 000000000..0e1c7e0ea --- /dev/null +++ b/compiler/moco-tf/src/Knob.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Knob.h" + +#include <pepper/strcast.h> + +#include <iostream> +#include <string> + +// Basic Infrastructure to declare and access Knob values +// +// TODO Reuse this infrastructure as a library +namespace +{ + +using KnobName = std::string; + +/** + * @brief Load configuration (from somewhere) + */ +struct KnobLoader +{ + virtual ~KnobLoader() = default; + + virtual bool load(const KnobName &name, bool default_value) const = 0; +}; + +// Template-programming helpers +template <typename T> T knob_load(const KnobLoader &, const KnobName &, const T &); + +template <> +bool knob_load(const KnobLoader &l, const KnobName &knob_name, const bool &default_value) +{ + return l.load(knob_name, default_value); +} + +/** + * @brief Load configuration from environment variables + * + * Given a prefix P, EnvKnobLoader reads a configuration K from concat(P, K). + * + * For example, let us assume that P is "MY_" and K is "CONFIG". + * + * Then, EnvKnobLoader reads configuration CONFIG from environment variable MY_CONFIG. + */ +class EnvKnobLoader final : public KnobLoader +{ +public: + EnvKnobLoader(const std::string &prefix) : _prefix{prefix} + { + // DO NOTHING + } + +public: + bool load(const KnobName &knob_name, bool default_value) const override + { + auto envvar = _prefix + knob_name; + auto s = std::getenv(envvar.c_str()); + + return pepper::safe_strcast<int>(s, default_value ? 1 : 0) != 0; + } + +private: + /// @brief Environment variable prefix + std::string _prefix; +}; + +} // namespace + +namespace +{ + +/** + * TODO Support Knob Loader Injection + * + * Let us assume that there is a compiler "A" based on moco, and it wants to reuse this + * infrastructure. + * + * Under the current design, users have to set "MOCO_XXX" even though they uses "A", which is + * counter-intuitive. + * + * "Knob Loader Injection" aims to address this issue. "Knob Loader Injection" allows "A" to + * inject its own knob loader that reads "A_XXX" environment variables. + */ +const KnobLoader &knob_loader(void) +{ + static EnvKnobLoader loader{"MOCO_"}; + return loader; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +#define KNOB_BOOL(NAME, DEFAULT, DESC) \ + template <> typename KnobTrait<Knob::NAME>::ValueType get<Knob::NAME>(void) \ + { \ + static typename KnobTrait<Knob::NAME>::ValueType value = \ + ::knob_load<typename KnobTrait<Knob::NAME>::ValueType>(::knob_loader(), #NAME, DEFAULT); \ + return value; \ + } +#include "Knob.lst" +#undef KNOB_BOOL + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Knob.h b/compiler/moco-tf/src/Knob.h new file mode 100644 index 000000000..145a81dc3 --- /dev/null +++ b/compiler/moco-tf/src/Knob.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __KNOB_H__ +#define __KNOB_H__ + +namespace moco +{ +namespace tf +{ + +enum class Knob +{ +#define KNOB_BOOL(NAME, DEFAULT, DESC) NAME, +#include "Knob.lst" +#undef KNOB_BOOL +}; + +template <Knob K> struct KnobTrait; + +#define KNOB_BOOL(NAME, DEFAULT, DESC) \ + template <> struct KnobTrait<Knob::NAME> \ + { \ + using ValueType = bool; \ + }; +#include "Knob.lst" +#undef KNOB_BOOL + +template <Knob K> typename KnobTrait<K>::ValueType get(void); + +} // namespace tf +} // namespace moco + +#endif // __KNOB_H__ diff --git a/compiler/moco-tf/src/Knob.lst b/compiler/moco-tf/src/Knob.lst new file mode 100644 index 000000000..b88e064c7 --- /dev/null +++ b/compiler/moco-tf/src/Knob.lst @@ -0,0 +1,39 @@ +#ifndef KNOB_BOOL +#error "KNOB_BOOL is not defined" +#endif // KNOB_BOOL + +// KNOB_BOOL(NAME, DEFAULT_VALUE, DESCRIPTION) + +// TensorFlow dialect transforms +KNOB_BOOL(FuseBinaryIntoPreceding, true, Fuse Binary node to preceding node) +KNOB_BOOL(ResolveFusedBatchNorm, true, Enable ResolveFusedBatchNorm transform) +KNOB_BOOL(ResolveConstantShape, true, Replace determined TFShape to TFConst) +KNOB_BOOL(ResolveReshapeWildcardDim, true, Resolve wildcard dimension in TFReshape node) +KNOB_BOOL(ResolveSquaredDifference, true, Resolve SquaredDifference node) +KNOB_BOOL(RemoveTFIdentityNode, true, Enable RemoveTFIdentityNode optimization) +KNOB_BOOL(SqueezeReduceNode, true, Insert TFSqueeze if ReduceNode do not keep dimensions) +// Constant folding +KNOB_BOOL(ConstantFoldAdd, false, Constant fold for Add node) +KNOB_BOOL(ConstantFoldMul, false, Constant fold for Mul node) +KNOB_BOOL(ConstantFoldPack, false, Constant fold for Pack node) +KNOB_BOOL(ConstantFoldStridedSlice, false, Constant fold for StridedSlice node) + +// Canonicalization +KNOB_BOOL(CanonicalizeBiasAdd, true, Enable Canonicalize for BiasAdd node) +KNOB_BOOL(CanonicalizeConst, true, Enable Canonicalize for Const node) +KNOB_BOOL(CanonicalizeConv2D, true, Enable Canonicalize for Conv2D node) + +// Canonical transforms +KNOB_BOOL(ConstantFolding, true, Enable constant-folding optimization) +KNOB_BOOL(RemoveForwardNode, true, Enable RemoveForwardNode optimization) +KNOB_BOOL(ReorderDecode, true, Enable ReorderDecode optimization) +// BEG: These knobs are valid only when ReorderDecode is enabled +KNOB_BOOL(ReorderDecodeReLU, true, Reorder FeatureDecode-ReLU) +KNOB_BOOL(ReorderDecodeTensorBiasAdd, true, Reorder FeatureDecode-TensorBiasAdd) +// END +KNOB_BOOL(SimplifyDomainConversion, true, Enable SimplifyDomainConversion optimization) +KNOB_BOOL(ResolveDuplicateReshape, true, Resolve duplicated Reshape nodes) +KNOB_BOOL(ResolveRedundantReshape, true, Resolve redundant Reshape node) + +// Graph transformations +KNOB_BOOL(RemoveDeadNode, true, Enable RemoveDeadNode optimization) diff --git a/compiler/moco-tf/src/LogHelper.cpp b/compiler/moco-tf/src/LogHelper.cpp new file mode 100644 index 000000000..92ff75569 --- /dev/null +++ b/compiler/moco-tf/src/LogHelper.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "LogHelper.h" + +namespace loco +{ + +std::ostream &operator<<(std::ostream &os, const loco::FeatureShape &feature_shape) +{ + os << "[" << feature_shape.count().value() << "," << feature_shape.height().value() << "," + << feature_shape.width().value() << "," << feature_shape.depth().value() << "]"; + return os; +} + +std::ostream &operator<<(std::ostream &os, const loco::FilterShape &filter_shape) +{ + os << "[" << filter_shape.height().value() << "," << filter_shape.width().value() << "," + << filter_shape.depth().value() << "," << filter_shape.count().value() << "]"; + return os; +} + +std::ostream &operator<<(std::ostream &os, const loco::TensorShape &tensor_shape) +{ + os << "["; + for (uint32_t r = 0; r < tensor_shape.rank(); ++r) + { + if (r) + os << ","; + os << tensor_shape.dim(r).value(); + } + os << "]"; + return os; +} + +std::ostream &operator<<(std::ostream &os, const loco::Padding2D &pad) +{ + os << "[TLBR " << pad.top() << "," << pad.left() << "," << pad.bottom() << "," << pad.right() + << "]"; + + return os; +} + +} // namespace loco + +std::ostream &operator<<(std::ostream &os, const std::vector<int64_t> &vi64) +{ + for (auto vi : vi64) + { + os << vi << " "; + } + return os; +} + +#include "TFFormattedGraph.h" + +namespace moco +{ +namespace tf +{ + +FormattedGraph fmt(loco::Graph *g) +{ + auto node_summary_builder = stdex::make_unique<TFNodeSummaryBuilderFactory>(); + return std::move(locop::fmt<locop::LinearV1>(g).with(std::move(node_summary_builder))); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/LogHelper.h b/compiler/moco-tf/src/LogHelper.h new file mode 100644 index 000000000..4e3cb5dac --- /dev/null +++ b/compiler/moco-tf/src/LogHelper.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LOG_HELPER_H__ +#define __LOG_HELPER_H__ + +#include <locop/FormattedGraph.h> + +#include <loco/IR/FeatureShape.h> +#include <loco/IR/FilterShape.h> +#include <loco/IR/TensorShape.h> + +#include <sstream> +#include <vector> + +namespace loco +{ + +/** + * @brief dump FeatureShape values to stream + */ +std::ostream &operator<<(std::ostream &os, const loco::FeatureShape &feature_shape); + +/** + * @brief dump FilterShape values to stream + */ +std::ostream &operator<<(std::ostream &os, const loco::FilterShape &filter_shape); + +/** + * @brief dump TensorShape values to stream + */ +std::ostream &operator<<(std::ostream &os, const loco::TensorShape &tensor_shape); + +/** + * @brief dump Padding2D values to stream + */ +std::ostream &operator<<(std::ostream &os, const loco::Padding2D &pad); + +} // namespace loco + +/** + * @brief dump std::vector<int64_t> values to stream + */ +std::ostream &operator<<(std::ostream &os, const std::vector<int64_t> &vi64); + +namespace moco +{ +namespace tf +{ + +using FormattedGraph = locop::FormattedGraphImpl<locop::Formatter::LinearV1>; + +FormattedGraph fmt(loco::Graph *g); + +static inline FormattedGraph fmt(const std::unique_ptr<loco::Graph> &g) { return fmt(g.get()); } + +} // namespace tf +} // namespace moco + +#endif // __LOG_HELPER_H__ diff --git a/compiler/moco-tf/src/Op/COpCall.cpp b/compiler/moco-tf/src/Op/COpCall.cpp new file mode 100644 index 000000000..801196f0f --- /dev/null +++ b/compiler/moco-tf/src/Op/COpCall.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "COpCall.h" + +#include "Convert.h" + +#include <locoex/COpCall.h> +#include <locoex/COpAttrTypes.h> +#include <moco/Names.h> +#include <moco/tf/Frontend.h> +#include <loco.h> +#include <stdex/Memory.h> +#include <oops/UserExn.h> + +#include <vector> +#include <cassert> +#include <stdexcept> + +namespace +{ + +class COpCallGraphUpdate final : public moco::GraphUpdate +{ +public: + COpCallGraphUpdate(locoex::COpCall *node, const std::vector<moco::TensorName> &input_names) + : _node(node), _input_names(input_names) + { + } + + void input(const moco::SymbolTable *) const override; + +private: + locoex::COpCall *_node; + const std::vector<moco::TensorName> _input_names; +}; + +void COpCallGraphUpdate::input(const moco::SymbolTable *tensor_names) const +{ + for (int n = 0; n < _input_names.size(); n++) + { + loco::Node *target = tensor_names->node(_input_names.at(n)); + _node->input(n, target); + } +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool COpCallGraphBuilder::validate(const tensorflow::NodeDef &tf_node) const { return true; } + +void COpCallGraphBuilder::build(const tensorflow::NodeDef &tf_node, + GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // Create a "COpCall" node for CustomOp and set attributes + auto call_node = graph->nodes()->create<locoex::COpCall>(tf_node.input_size()); + { + call_node->op(tf_node.op()); + call_node->name(tf_node.name()); + call_node->dtype(_signature->dtype(tf_node.name())); + + auto shape = _signature->shape(tf_node.name()); + call_node->rank(shape->rank()); + for (int d = 0; d < shape->rank(); d++) + call_node->dim(d) = shape->dim(d); + + for (auto iter = tf_node.attr().begin(); iter != tf_node.attr().end(); iter++) + { + auto name = iter->first; + auto val = iter->second; + + if (val.value_case() == tensorflow::AttrValue::kF) + { + call_node->attr(name, stdex::make_unique<locoex::COpAttrFloat>(val.f())); + } + else if (val.value_case() == tensorflow::AttrValue::kI) + { + call_node->attr(name, stdex::make_unique<locoex::COpAttrInt>(val.i())); + } + // TODO define more types + else + { + throw oops::UserExn("Unsupported attribute type", tf_node.name()); + } + } + } + + // register this node with its name + TensorName output_name(tf_node.name(), 0); + tensor_names->enroll(output_name, call_node); + + // Queue node input update + std::vector<TensorName> input_names; + for (int i = 0; i < tf_node.input_size(); ++i) + { + input_names.emplace_back(TensorName(tf_node.input(i))); + } + auto update = stdex::make_unique<COpCallGraphUpdate>(call_node, input_names); + updates->enroll(std::move(update)); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Op/COpCall.h b/compiler/moco-tf/src/Op/COpCall.h new file mode 100644 index 000000000..0bb8a93c9 --- /dev/null +++ b/compiler/moco-tf/src/Op/COpCall.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OP_COP_CALL_H__ +#define __OP_COP_CALL_H__ + +#include <moco/tf/Frontend.h> + +#include <moco/Import/GraphBuilder.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief GraphBuilder for COpCall node + */ +class COpCallGraphBuilder final : public GraphBuilder +{ +public: + COpCallGraphBuilder(const ModelSignature *signature) : _signature(signature) { /* empty */} + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; + +private: + const ModelSignature *_signature; +}; + +} // namespace tf +} // namespace moco + +#endif // __OP_COP_CALL_H__ diff --git a/compiler/moco-tf/src/Op/COpCall.test.cpp b/compiler/moco-tf/src/Op/COpCall.test.cpp new file mode 100644 index 000000000..f13118292 --- /dev/null +++ b/compiler/moco-tf/src/Op/COpCall.test.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "COpCall.h" + +#include "TestHelper.h" + +#include "Canonicalizer.h" + +#include <moco/Importer.h> + +#include <locoex/COpCall.h> +#include <locoex/COpAttrTypes.h> + +#include <loco.h> +#include <plier/tf/TestHelper.h> +#include <stdex/Memory.h> + +#include <gtest/gtest.h> + +using namespace moco::tf::test; + +namespace +{ +// clang-format off +const char *customop_01_pbtxtdata = STRING_CONTENT( +node { + name: "input1" + op: "Placeholder" + attr { + key: "dtype" value { type: DT_FLOAT } } + attr { + key: "shape" + value { shape { dim { size: 1 } dim { size: 2 } } } + } +} +node { + name: "input2" + op: "Const" + attr { key: "dtype" value { type: DT_FLOAT } } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { dim { size: 1 } dim { size: 2 } } + float_val: 1.1 float_val: 2.2 + } + } + } +} +node { + name: "my/customOp/000" + op: "new_custom_op" + input: "input1" + input: "input2" + attr { key: "my_float" value { f: 0.001 } } + attr { key: "my_int" value { i: 111 } } +} +); + +// clang-format on +} // namespace + +TEST(Call_Test, Call_01) +{ + moco::ModelSignature signature; + { + signature.add_input(moco::TensorName("input1", 0)); + signature.add_output(moco::TensorName("my/customOp/000", 0)); + signature.add_customop("new_custom_op"); + signature.dtype("my/customOp/000", loco::DataType::FLOAT32); + signature.shape("my/customOp/000", {1, 2}); + } + + tensorflow::GraphDef graph_def; + EXPECT_TRUE(plier::tf::parse_graphdef(customop_01_pbtxtdata, graph_def)); + + // import + moco::GraphBuilderRegistry registry{&moco::GraphBuilderRegistry::get()}; + registry.add("new_custom_op", stdex::make_unique<moco::tf::COpCallGraphBuilder>(&signature)); + + moco::Importer importer(®istry); + std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def); + + // what to test: + // - there should exist COpCall + // - two input nodes should exist and not be nullptr + // - attributes should match + + auto *customop = moco::tf::test::find_first_node_bytype<locoex::COpCall>(graph.get()); + ASSERT_NE(customop, nullptr); + + ASSERT_EQ(customop->arity(), 2); + + loco::Node *input_0 = customop->arg(0); + loco::Node *input_1 = customop->arg(1); + ASSERT_NE(input_0, nullptr); + ASSERT_NE(input_1, nullptr); + + auto f_attr = customop->attr<locoex::COpAttrType::Float>("my_float"); + ASSERT_FLOAT_EQ(f_attr->val(), 0.001); + ASSERT_TRUE(f_attr->type() == locoex::COpAttrType::Float); + + auto i_attr = customop->attr<locoex::COpAttrType::Int>("my_int"); + ASSERT_FLOAT_EQ(i_attr->val(), 111); + ASSERT_TRUE(i_attr->type() == locoex::COpAttrType::Int); +} diff --git a/compiler/moco-tf/src/Optimizer.cpp b/compiler/moco-tf/src/Optimizer.cpp new file mode 100644 index 000000000..f33b4109b --- /dev/null +++ b/compiler/moco-tf/src/Optimizer.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Optimizer.h" + +#include "Knob.h" +#include "ProgressReporter.h" +#include "Transforms.h" + +#include <logo/Phase.h> + +#include <stdex/Memory.h> + +namespace moco +{ +namespace tf +{ + +void Optimizer::optimize(loco::Graph *g) const +{ + logo::Phase phase; + + /* TRANSFORM DECLARATION BEGIN */ + // Shape inference is required for ResolveRedundantReshape + phase.emplace_back(stdex::make_unique<ShapeInferencePass>()); + + if (moco::tf::get<moco::tf::Knob::ConstantFolding>()) + { + phase.emplace_back(stdex::make_unique<logo::ConstantFoldingPass>()); + } + + if (moco::tf::get<moco::tf::Knob::RemoveDeadNode>()) + { + phase.emplace_back(stdex::make_unique<logo::RemoveDeadNodePass>()); + } + + if (moco::tf::get<moco::tf::Knob::ReorderDecode>() && + moco::tf::get<moco::tf::Knob::ReorderDecodeTensorBiasAdd>()) + { + phase.emplace_back(stdex::make_unique<logo::ReorderDecodePass<loco::TensorBiasAdd>>()); + } + + if (moco::tf::get<moco::tf::Knob::ReorderDecode>() && + moco::tf::get<moco::tf::Knob::ReorderDecodeReLU>()) + { + phase.emplace_back(stdex::make_unique<logo::ReorderDecodePass<loco::ReLU>>()); + } + + if (moco::tf::get<moco::tf::Knob::SimplifyDomainConversion>()) + { + phase.emplace_back(stdex::make_unique<logo::SimplifyDomainConversionPass>()); + } + + if (moco::tf::get<moco::tf::Knob::RemoveForwardNode>()) + { + phase.emplace_back(stdex::make_unique<logo::RemoveForwardNodePass>()); + } + + if (moco::tf::get<moco::tf::Knob::ResolveDuplicateReshape>()) + { + phase.emplace_back(stdex::make_unique<logo::ResolveDuplicateReshapePass>()); + } + + if (moco::tf::get<moco::tf::Knob::ResolveRedundantReshape>()) + { + phase.emplace_back(stdex::make_unique<logo::ResolveRedundantReshapePass>()); + } + /* TRANSFORM DECLARATION END */ + + ProgressReporter prog(g, logo::PhaseStrategy::Saturate); + logo::PhaseRunner<logo::PhaseStrategy::Saturate> phase_runner{g}; + phase_runner.attach(&prog); + phase_runner.run(phase); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Optimizer.h b/compiler/moco-tf/src/Optimizer.h new file mode 100644 index 000000000..8584df89b --- /dev/null +++ b/compiler/moco-tf/src/Optimizer.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __OPTIMIZER_H__ +#define __OPTIMIZER_H__ + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +class Optimizer final +{ +public: + void optimize(loco::Graph *) const; +}; + +} // namespace tf +} // namespace moco + +#endif // __OPTIMIZER_H__ diff --git a/compiler/moco-tf/src/Optimizer.test.cpp b/compiler/moco-tf/src/Optimizer.test.cpp new file mode 100644 index 000000000..5ffed58e3 --- /dev/null +++ b/compiler/moco-tf/src/Optimizer.test.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Optimizer.h" + +#include <loco.h> + +#include <gtest/gtest.h> + +// Optimizer SHOULD NOT crash even though a given graph is empty +TEST(Optimizer, empty_graph) +{ + moco::tf::Optimizer o; + + loco::Graph g; + + o.optimize(&g); + + SUCCEED(); +} + +TEST(Optimizer, simple_forward_graph) +{ + moco::tf::Optimizer o; + + /** + * Create a simple graph that forwards a constant as output + */ + loco::Graph g; + { + auto constgen = g.nodes()->create<loco::ConstGen>(); + constgen->shape({2, 3}); + + auto forward = g.nodes()->create<loco::Forward>(); + forward->input(constgen); + + auto pull = g.nodes()->create<loco::Push>(); + pull->from(forward); + } + + o.optimize(&g); + + SUCCEED(); +} + +TEST(Optimizer, simple_forward_graph_with_one_valid_output) +{ + moco::tf::Optimizer o; + + /** + * Create a simple graph that forwards a constant as graph-level output + */ + loco::Graph g; + { + auto output = g.outputs()->create(); + + auto constgen = g.nodes()->create<loco::ConstGen>(); + constgen->shape({2, 3}); + constgen->dtype(loco::DataType::FLOAT32); + constgen->size<loco::DataType::FLOAT32>(6); + + auto forward = g.nodes()->create<loco::Forward>(); + forward->input(constgen); + + auto pull = g.nodes()->create<loco::Push>(); + pull->from(forward); + + loco::link(output, pull); + } + + o.optimize(&g); + + SUCCEED(); +} diff --git a/compiler/moco-tf/src/ProgressReporter.cpp b/compiler/moco-tf/src/ProgressReporter.cpp new file mode 100644 index 000000000..41338ffec --- /dev/null +++ b/compiler/moco-tf/src/ProgressReporter.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ProgressReporter.h" + +#include "LogHelper.h" + +#include <logo/Phase.h> +#include <logo/Pass.h> + +#include <moco/Log.h> + +#include <cassert> + +namespace +{ + +char to_char(bool b) { return b ? 'Y' : 'N'; } + +const char *to_str(logo::PhaseStrategy s) +{ + switch (s) + { + case logo::PhaseStrategy::Saturate: + return "Saturate"; + case logo::PhaseStrategy::Restart: + return "Restart"; + } + assert(false); + return ""; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseBegin> *info) +{ + LOGGER(prime); + + INFO(prime) << "=============================================================="; + INFO(prime) << "PhaseRunner<" << to_str(strategy()) << ">"; + INFO(prime) << "Initial graph"; + INFO(prime) << moco::tf::fmt(graph()); +} + +void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseEnd> *info) +{ + LOGGER(prime); + + INFO(prime) << "PhaseRunner<" << to_str(strategy()) << "> - done"; +} + +void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassBegin> *info) +{ + LOGGER(prime); + + INFO(prime) << "--------------------------------------------------------------"; + INFO(prime) << "Before " << logo::pass_name(info->pass()); +} + +void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassEnd> *info) +{ + LOGGER(prime); + + INFO(prime) << "After " << logo::pass_name(info->pass()) + << " (changed: " << to_char(info->changed()) << ")"; + INFO(prime) << moco::tf::fmt(graph()); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/ProgressReporter.h b/compiler/moco-tf/src/ProgressReporter.h new file mode 100644 index 000000000..190d972c5 --- /dev/null +++ b/compiler/moco-tf/src/ProgressReporter.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_PROGRESSREPORTER_H__ +#define __MOCO_TF_PROGRESSREPORTER_H__ + +#include <logo/Phase.h> + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +class ProgressReporter : public logo::PhaseEventListener +{ +public: + ProgressReporter(loco::Graph *graph, logo::PhaseStrategy strategy) + : _graph{graph}, _strategy{strategy} + { + // DO NOTHING + } + +public: + void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseBegin> *) override; + void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseEnd> *) override; + void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassBegin> *) override; + void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassEnd> *) override; + +public: + loco::Graph *graph(void) const { return _graph; } + logo::PhaseStrategy strategy(void) const { return _strategy; } + +private: + loco::Graph *_graph; + logo::PhaseStrategy _strategy; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_PROGRESSREPORTER_H__ diff --git a/compiler/moco-tf/src/SimpleNodeTransform.h b/compiler/moco-tf/src/SimpleNodeTransform.h new file mode 100644 index 000000000..b69cbad6b --- /dev/null +++ b/compiler/moco-tf/src/SimpleNodeTransform.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_SIMPLE_NODE_TRANSFORM_H__ +#define __MOCO_TF_SIMPLE_NODE_TRANSFORM_H__ + +#include "Transform.h" + +namespace moco +{ +namespace tf +{ + +/** + * @brief Per-Node Transform + */ +template <typename ConcreteNode> struct SimpleNodeTransform : public Transform +{ + SimpleNodeTransform() = default; + + virtual ~SimpleNodeTransform() = default; + + // NOTE Users SHOULD implement this method + virtual bool transform(ConcreteNode *node) const = 0; + + bool run(loco::Graph *graph) final + { + using loco::active_nodes; + using loco::output_nodes; + + bool changed = false; + + for (auto node : active_nodes(output_nodes(graph))) + { + if (auto casted = dynamic_cast<ConcreteNode *>(node)) + { + if (transform(casted)) + { + changed = true; + } + } + } + + return changed; + } +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_SIMPLE_NODE_TRANSFORM_H__ diff --git a/compiler/moco-tf/src/SimpleNodeTransform.test.cpp b/compiler/moco-tf/src/SimpleNodeTransform.test.cpp new file mode 100644 index 000000000..781a48781 --- /dev/null +++ b/compiler/moco-tf/src/SimpleNodeTransform.test.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SimpleNodeTransform.h" + +#include <set> + +#include <gtest/gtest.h> + +TEST(SimpleNodeTransformTests, run) +{ + class Transform final : public moco::tf::SimpleNodeTransform<loco::Push> + { + public: + Transform(std::multiset<loco::Node *> *out) : _out{out} + { + // DO NOTHING + } + + public: + bool transform(loco::Push *node) const final + { + _out->insert(node); + return false; + } + + private: + std::multiset<loco::Node *> *_out; + }; + + auto g = loco::make_graph(); + auto output_0 = g->outputs()->create(); + auto push = g->nodes()->create<loco::Push>(); + loco::link(output_0, push); + + std::multiset<loco::Node *> nodes; + Transform transform{&nodes}; + + transform.run(g.get()); + + ASSERT_EQ(nodes.size(), 1); + ASSERT_EQ(nodes.count(push), 1); +} diff --git a/compiler/moco-tf/src/TFEltwiseBinaryCanonicalzeHelper.h b/compiler/moco-tf/src/TFEltwiseBinaryCanonicalzeHelper.h new file mode 100644 index 000000000..df9aec144 --- /dev/null +++ b/compiler/moco-tf/src/TFEltwiseBinaryCanonicalzeHelper.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TF_ELTWISE_BINARY_CANONICALIZE_HELPER_H__ +#define __TF_ELTWISE_BINARY_CANONICALIZE_HELPER_H__ + +#include <moco/IR/TFDialect.h> +#include <moco/IR/TFNodes.h> + +#include "CanonicalEltwiseInputConnector.h" +#include "BroadcastHelper.h" + +#include <loco/IR/Nodes.h> +#include <loco/IR/NodeShape.h> +#include <loco/Service/ShapeInference.h> + +#include <fipe.h> + +namespace +{ + +template <typename TFNodeT> struct EltwiseBinaryCanonicalizationRule; + +template <> struct EltwiseBinaryCanonicalizationRule<moco::TFAdd> +{ + using CanonicalNode = loco::EltwiseAdd; +}; + +template <> struct EltwiseBinaryCanonicalizationRule<moco::TFSub> +{ + using CanonicalNode = loco::EltwiseSub; +}; + +template <> struct EltwiseBinaryCanonicalizationRule<moco::TFMaximum> +{ + using CanonicalNode = loco::EltwiseMax; +}; + +template <> struct EltwiseBinaryCanonicalizationRule<moco::TFMul> +{ + using CanonicalNode = loco::EltwiseMul; +}; + +template <> struct EltwiseBinaryCanonicalizationRule<moco::TFRealDiv> +{ + using CanonicalNode = loco::EltwiseDiv; +}; + +template <typename TFNode> bool canonicalize_eltwise_binary_node(TFNode *node) +{ + auto graph = node->graph(); + + /** + * This will replace T/F Eltwise Binary node with a corresponding Canonical Eltwise node + * + * BEFORE + * A --- T/F Node --- C + * / + * B ---- + * + * AFTER + * A --- T/F Node --- + * / + * B ---- + * + * A --- [FixedReshape] --- [TensorBroadcast] --- Canonical Node -- C + * / + * B --- [FixedReshape] --- [TensorBroadcast] ---- + * + * NOTE + * - [...] means optional node. They may or may not be created during this procedure. + * - T/F Node is disconnected from C after transformation. + */ + + using CanonicalNodeT = typename EltwiseBinaryCanonicalizationRule<TFNode>::CanonicalNode; + + auto node_A = node->x(); + auto node_B = node->y(); + + if (!loco::shape_known(node_A) || !loco::shape_known(node_B)) + return false; + if (!loco::shape_known(node)) + return false; + + auto out_shape = loco::shape_get(node).template as<loco::TensorShape>(); + + // Create a node + auto canonical_node = graph->nodes()->template create<CanonicalNodeT>(); + + using moco::tf::eltwise::binary::connect_to; + using moco::tf::broadcast_to; + + // update connections + std::make_pair(node_A, node_B) | broadcast_to(out_shape) | connect_to(canonical_node); + + // replace node + replace(node).with(canonical_node); + + return true; +} + +} // namespace + +#endif // __TF_ELTWISE_BINARY_CANONICALIZE_HELPER_H__ diff --git a/compiler/moco-tf/src/TFFormattedGraph.cpp b/compiler/moco-tf/src/TFFormattedGraph.cpp new file mode 100644 index 000000000..2ea514a2b --- /dev/null +++ b/compiler/moco-tf/src/TFFormattedGraph.cpp @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TFFormattedGraph.h" + +#include <moco/IR/TFDialect.h> +#include <moco/IR/TFNodes.h> + +#include "LogHelper.h" + +#include <pepper/str.h> +#include <locoex/Service/COpFormattedGraph.h> +#include <oops/InternalExn.h> + +#include <sstream> + +namespace +{ + +std::string opname(uint32_t opnum) +{ + static std::string prefix{"tf."}; + + switch (static_cast<moco::TFOpcode>(opnum)) + { +#define TENSORFLOW_NODE(OPCODE, CLASS) \ + case moco::TFOpcode::OPCODE: \ + return prefix + #OPCODE; +#include <moco/IR/TFNodes.lst> +#undef TENSORFLOW_NODE + default: + break; + }; + + return prefix + "Invalid"; +} + +using namespace moco; +using namespace moco::tf; + +/// TFNodeSummaryBuilder with default implementation +class TFNodeSummaryBuilderBase : public locop::NodeSummaryBuilder +{ +public: + TFNodeSummaryBuilderBase(const locop::SymbolTable *tbl) : _tbl{tbl} + { + // DO NOTHING + } + +public: + bool build(const loco::Node *, locop::NodeSummary &s) const final; + +protected: +#define TENSORFLOW_NODE(OPCODE, CLASS) \ + virtual bool summary(const CLASS *node, locop::NodeSummary &s) const \ + { \ + s.comments().append("Emitted by Default NodeSummaryBuilder"); \ + s.state(locop::NodeSummary::State::PartiallyKnown); \ + return true; \ + } +#include <moco/IR/TFNodes.lst> +#undef TENSORFLOW_NODE + +protected: + const locop::SymbolTable *tbl(void) const { return _tbl; } + + // Please do not use _tbl directly and use tbl(). + // This will be changed to private in near future. +protected: + const locop::SymbolTable *_tbl; +}; + +class TFNodeSummaryBuilder final : public TFNodeSummaryBuilderBase +{ +public: + TFNodeSummaryBuilder(const locop::SymbolTable *tbl) : TFNodeSummaryBuilderBase(tbl) + { + // DO NOTHING + } + +private: +#define IMPLEMENT(CLASS) bool summary(const CLASS *, locop::NodeSummary &) const final + IMPLEMENT(TFAdd); + IMPLEMENT(TFAvgPool); + IMPLEMENT(TFBiasAdd); + IMPLEMENT(TFConcatV2); + IMPLEMENT(TFConst); + IMPLEMENT(TFConv2D); + IMPLEMENT(TFConv2DBackpropInput); + IMPLEMENT(TFDepthwiseConv2dNative); + IMPLEMENT(TFFusedBatchNorm); + IMPLEMENT(TFMaximum); + IMPLEMENT(TFMaxPool); + IMPLEMENT(TFMean); + IMPLEMENT(TFMul); + IMPLEMENT(TFPack); + IMPLEMENT(TFReshape); + IMPLEMENT(TFRsqrt); + IMPLEMENT(TFShape); + IMPLEMENT(TFSoftmax); + IMPLEMENT(TFSqueeze); + IMPLEMENT(TFStopGradient); + IMPLEMENT(TFStridedSlice); + IMPLEMENT(TFTanh); + // For virtual nodes + IMPLEMENT(TFPush); +#undef IMPLEMENT +}; + +bool TFNodeSummaryBuilderBase::build(const loco::Node *node, locop::NodeSummary &s) const +{ + if (node->dialect() != TFDialect::get()) + return false; + +#define TENSORFLOW_NODE(OPCODE, CLASS) \ + if (dynamic_cast<const CLASS *>(node)) \ + { \ + s.opname(opname(node->opnum())); \ + return summary(dynamic_cast<const CLASS *>(node), s); \ + } +#include <moco/IR/TFNodes.lst> +#undef TENSORFLOW_NODE + + return false; +} + +bool TFNodeSummaryBuilder::summary(const TFAdd *node, locop::NodeSummary &s) const +{ + s.args().append("x", tbl()->lookup(node->x())); + s.args().append("y", tbl()->lookup(node->y())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool TFNodeSummaryBuilder::summary(const TFAvgPool *node, locop::NodeSummary &s) const +{ + s.args().append("value", tbl()->lookup(node->value())); + s.args().append("ksize", pepper::str(node->ksize())); + s.args().append("strides", pepper::str(node->strides())); + s.args().append("padding", node->padding()); + s.args().append("data_layout", node->data_layout()); + + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool TFNodeSummaryBuilder::summary(const TFBiasAdd *node, locop::NodeSummary &s) const +{ + s.args().append("value", tbl()->lookup(node->value())); + s.args().append("bias", tbl()->lookup(node->bias())); + s.args().append("data_layout", node->data_layout()); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool TFNodeSummaryBuilder::summary(const TFConcatV2 *node, locop::NodeSummary &s) const +{ + for (uint32_t n = 0; n < node->num_values(); ++n) + { + std::ostringstream ss; + ss << "values(" << n << ")"; + s.args().append(ss.str(), tbl()->lookup(node->values(n))); + } + s.args().append("axis", tbl()->lookup(node->axis())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool TFNodeSummaryBuilder::summary(const TFConst *node, locop::NodeSummary &s) const +{ + std::ostringstream ss; + + auto dtype = node->dtype(); + switch (dtype) + { + case loco::DataType::S32: + ss << node->size<loco::DataType::S32>(); + break; + case loco::DataType::FLOAT32: + ss << node->size<loco::DataType::FLOAT32>(); + break; + default: + INTERNAL_EXN_V("Unsupported data type", node->name()); + } + s.args().append("size", ss.str()); + s.state(locop::NodeSummary::State::PartiallyKnown); + return true; +} + +bool TFNodeSummaryBuilder::summary(const TFConv2D *node, locop::NodeSummary &s) const +{ + s.args().append("input", tbl()->lookup(node->input())); + s.args().append("filter", tbl()->lookup(node->filter())); + s.args().append("padding", node->padding()); + s.args().append("data_layout", node->data_layout()); + s.args().append("strides", pepper::str(node->strides())); + s.state(locop::NodeSummary::State::PartiallyKnown); + return true; +} + +bool TFNodeSummaryBuilder::summary(const TFConv2DBackpropInput *node, locop::NodeSummary &s) const +{ + s.args().append("input_sizes", tbl()->lookup(node->input_sizes())); + s.args().append("filter", tbl()->lookup(node->filter())); + s.args().append("out_backprop", tbl()->lookup(node->out_backprop())); + s.args().append("padding", node->padding()); + s.args().append("data_layout", node->data_layout()); + s.args().append("strides", pepper::str(node->strides())); + s.state(locop::NodeSummary::State::PartiallyKnown); + return true; +} + +bool TFNodeSummaryBuilder::summary(const TFDepthwiseConv2dNative *node, locop::NodeSummary &s) const +{ + s.args().append("input", tbl()->lookup(node->input())); + s.args().append("filter", tbl()->lookup(node->filter())); + s.args().append("padding", node->padding()); + s.args().append("data_layout", node->data_layout()); + s.args().append("strides", pepper::str(node->strides())); + s.state(locop::NodeSummary::State::PartiallyKnown); + return true; +} + +bool TFNodeSummaryBuilder::summary(const TFFusedBatchNorm *node, locop::NodeSummary &s) const +{ + s.args().append("x", tbl()->lookup(node->x())); + s.args().append("scale", tbl()->lookup(node->scale())); + s.args().append("offset", tbl()->lookup(node->offset())); + s.args().append("mean", tbl()->lookup(node->mean())); + s.args().append("variance", tbl()->lookup(node->variance())); + s.args().append("epsilon", pepper::str(node->epsilon())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool TFNodeSummaryBuilder::summary(const TFMaximum *node, locop::NodeSummary &s) const +{ + s.args().append("x", tbl()->lookup(node->x())); + s.args().append("y", tbl()->lookup(node->y())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool TFNodeSummaryBuilder::summary(const TFMaxPool *node, locop::NodeSummary &s) const +{ + s.args().append("input", tbl()->lookup(node->input())); + s.args().append("ksize", pepper::str(node->ksize())); + s.args().append("strides", pepper::str(node->strides())); + s.args().append("padding", node->padding()); + s.args().append("data_layout", node->data_layout()); + + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool TFNodeSummaryBuilder::summary(const TFMean *node, locop::NodeSummary &s) const +{ + s.args().append("input", tbl()->lookup(node->input())); + s.args().append("reduction_indices", tbl()->lookup(node->reduction_indices())); + s.args().append("keep_dims", pepper::str(node->keep_dims())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool TFNodeSummaryBuilder::summary(const TFMul *node, locop::NodeSummary &s) const +{ + s.args().append("x", tbl()->lookup(node->x())); + s.args().append("y", tbl()->lookup(node->y())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool TFNodeSummaryBuilder::summary(const TFPack *node, locop::NodeSummary &s) const +{ + s.args().append("N", pepper::str(node->N())); + s.args().append("axis", pepper::str(node->axis())); + for (uint32_t n = 0; n < node->N(); ++n) + s.args().append("values", tbl()->lookup(node->values(n))); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool TFNodeSummaryBuilder::summary(const TFReshape *node, locop::NodeSummary &s) const +{ + s.args().append("tensor", tbl()->lookup(node->tensor())); + s.args().append("shape", tbl()->lookup(node->shape())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool TFNodeSummaryBuilder::summary(const TFRsqrt *node, locop::NodeSummary &s) const +{ + s.args().append("x", tbl()->lookup(node->x())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool TFNodeSummaryBuilder::summary(const TFShape *node, locop::NodeSummary &s) const +{ + s.args().append("input", tbl()->lookup(node->input())); + s.state(locop::NodeSummary::State::PartiallyKnown); + return true; +} + +bool TFNodeSummaryBuilder::summary(const TFSoftmax *node, locop::NodeSummary &s) const +{ + s.args().append("logits", tbl()->lookup(node->logits())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool TFNodeSummaryBuilder::summary(const TFSqueeze *node, locop::NodeSummary &s) const +{ + s.args().append("input", tbl()->lookup(node->input())); + s.args().append("squeeze_dims", pepper::str(node->squeeze_dims())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool TFNodeSummaryBuilder::summary(const TFStopGradient *node, locop::NodeSummary &s) const +{ + s.args().append("input", tbl()->lookup(node->input())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool TFNodeSummaryBuilder::summary(const TFStridedSlice *node, locop::NodeSummary &s) const +{ + s.args().append("input", tbl()->lookup(node->input())); + s.args().append("begin", tbl()->lookup(node->begin())); + s.args().append("end", tbl()->lookup(node->end())); + if (node->strides() != nullptr) + s.args().append("strides", tbl()->lookup(node->strides())); + s.args().append("begin_mask", pepper::str(node->begin_mask())); + s.args().append("end_mask", pepper::str(node->end_mask())); + s.args().append("ellipsis_mask", pepper::str(node->ellipsis_mask())); + s.args().append("new_axis_mask", pepper::str(node->new_axis_mask())); + s.args().append("shrink_axis_mask", pepper::str(node->shrink_axis_mask())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +bool TFNodeSummaryBuilder::summary(const TFTanh *node, locop::NodeSummary &s) const +{ + s.args().append("x", tbl()->lookup(node->x())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +// For virtual nodes +bool TFNodeSummaryBuilder::summary(const TFPush *node, locop::NodeSummary &s) const +{ + s.args().append("index", node->indexed() ? pepper::str(node->index()) : "?"); + s.args().append("from", tbl()->lookup(node->from())); + s.state(locop::NodeSummary::State::Complete); + return true; +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool MocoNodeSummaryBuilder::build(const loco::Node *node, locop::NodeSummary &s) const +{ + if (locop::CanonicalNodeSummaryBuilder(_tbl).build(node, s)) + { + return true; + } + + if (TFNodeSummaryBuilder(_tbl).build(node, s)) + { + return true; + } + + if (locoex::COpNodeSummaryBuilder(_tbl).build(node, s)) + { + return true; + } + + return false; +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/TFFormattedGraph.h b/compiler/moco-tf/src/TFFormattedGraph.h new file mode 100644 index 000000000..f79208536 --- /dev/null +++ b/compiler/moco-tf/src/TFFormattedGraph.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TF_FORMATTED_GRAPH_H__ +#define __TF_FORMATTED_GRAPH_H__ + +#include <locop/FormattedGraph.h> + +#include <stdex/Memory.h> + +namespace moco +{ +namespace tf +{ + +class MocoNodeSummaryBuilder final : public locop::NodeSummaryBuilder +{ +public: + MocoNodeSummaryBuilder(const locop::SymbolTable *tbl) : _tbl{tbl} + { + // DO NOTHING + } + +public: + bool build(const loco::Node *node, locop::NodeSummary &s) const final; + +private: + const locop::SymbolTable *_tbl; +}; + +class TFNodeSummaryBuilderFactory final : public locop::NodeSummaryBuilderFactory +{ +public: + TFNodeSummaryBuilderFactory() = default; + +public: + std::unique_ptr<locop::NodeSummaryBuilder> create(const locop::SymbolTable *tlb) const final + { + return stdex::make_unique<MocoNodeSummaryBuilder>(tlb); + } +}; + +} // namespace tf +} // namespace moco + +#endif // __TF_FORMATTED_GRAPH_H__ diff --git a/compiler/moco-tf/src/TFOptimizer.cpp b/compiler/moco-tf/src/TFOptimizer.cpp new file mode 100644 index 000000000..2256b99b8 --- /dev/null +++ b/compiler/moco-tf/src/TFOptimizer.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TFOptimizer.h" + +#include "Knob.h" +#include "ProgressReporter.h" +#include "Transforms.h" + +#include <logo/Phase.h> + +#include <stdex/Memory.h> + +namespace moco +{ +namespace tf +{ + +void TFOptimizer::optimize(loco::Graph *g) const +{ + logo::Phase phase; + + /* TRANSFORM DECLARATION BEGIN */ + if (moco::tf::get<moco::tf::Knob::ResolveFusedBatchNorm>()) + { + phase.emplace_back(stdex::make_unique<moco::ResolveFusedBatchNorm>()); + } + if (moco::tf::get<moco::tf::Knob::FuseBinaryIntoPreceding>()) + { + phase.emplace_back(stdex::make_unique<moco::FuseBinaryIntoPreceding>()); + } + if (moco::tf::get<moco::tf::Knob::ResolveConstantShape>()) + { + phase.emplace_back(stdex::make_unique<moco::ResolveConstantShape>()); + } + if (moco::tf::get<moco::tf::Knob::ResolveReshapeWildcardDim>()) + { + phase.emplace_back(stdex::make_unique<moco::ResolveReshapeWildcardDim>()); + } + if (moco::tf::get<moco::tf::Knob::ResolveSquaredDifference>()) + { + phase.emplace_back(stdex::make_unique<moco::ResolveSquaredDifference>()); + } + if (moco::tf::get<moco::tf::Knob::RemoveTFIdentityNode>()) + { + phase.emplace_back(stdex::make_unique<moco::RemoveTFIdentityNode>()); + } + if (moco::tf::get<moco::tf::Knob::RemoveDeadNode>()) + { + phase.emplace_back(stdex::make_unique<logo::RemoveDeadNodePass>()); + } + if (moco::tf::get<moco::tf::Knob::SqueezeReduceNode>()) + { + phase.emplace_back(stdex::make_unique<moco::SqueezeReduceNode>()); + } + // Shape inference is needed for added nodes doing above transformations + phase.emplace_back(stdex::make_unique<moco::tf::ShapeInferencePass>()); + phase.emplace_back(stdex::make_unique<moco::tf::TypeInferencePass>()); + /* TRANSFORM DECLARATION END */ + + ProgressReporter prog(g, logo::PhaseStrategy::Saturate); + logo::PhaseRunner<logo::PhaseStrategy::Saturate> phase_runner{g}; + phase_runner.attach(&prog); + phase_runner.run(phase); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/TFOptimizer.h b/compiler/moco-tf/src/TFOptimizer.h new file mode 100644 index 000000000..69ab74d3e --- /dev/null +++ b/compiler/moco-tf/src/TFOptimizer.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TF_OPTIMIZER_H__ +#define __TF_OPTIMIZER_H__ + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +class TFOptimizer final +{ +public: + void optimize(loco::Graph *) const; +}; + +} // namespace tf +} // namespace moco + +#endif // __TF_OPTIMIZER_H__ diff --git a/compiler/moco-tf/src/TFOptimizer.test.cpp b/compiler/moco-tf/src/TFOptimizer.test.cpp new file mode 100644 index 000000000..26348f6c8 --- /dev/null +++ b/compiler/moco-tf/src/TFOptimizer.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TFOptimizer.h" + +#include <loco.h> + +#include <gtest/gtest.h> + +// TFOptimizer SHOULD NOT crash even though a given graph is empty +TEST(TFOptimizer, empty_graph) +{ + moco::tf::TFOptimizer tfo; + + loco::Graph g; + + tfo.optimize(&g); + + SUCCEED(); +} diff --git a/compiler/moco-tf/src/TFReduceCanonicalzeHelper.h b/compiler/moco-tf/src/TFReduceCanonicalzeHelper.h new file mode 100644 index 000000000..abd24cec8 --- /dev/null +++ b/compiler/moco-tf/src/TFReduceCanonicalzeHelper.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TF_REDUCE_CANONICALIZE_HELPER_H__ +#define __TF_REDUCE_CANONICALIZE_HELPER_H__ + +#include <moco/IR/TFDialect.h> +#include <moco/IR/TFNodes.h> + +#include <loco/Service/ShapeInference.h> + +#include <moco/Log.h> + +namespace +{ + +template <typename TFNodeT> loco::ReduceFunc reduceFunc(void); + +template <> loco::ReduceFunc reduceFunc<moco::TFMean>(void) { return loco::ReduceFunc::Mean; } + +template <typename TFNode> bool canonicalize_reduce_node(TFNode *node) +{ + LOGGER(l); + + INFO(l) << "TFNodeCanonicalize ReduceNode begin"; + + auto graph = node->graph(); + + /** + * This will replace T/F Reduce node with a corresponding Canonical Reduce node + * + * BEFORE + * reduction_indices -------- T/F Node -- C + * input -------/ + * + * AFTER + * +------ T/F Node -- + * | / + * reduction_indices ------- + * | \ + * input -+------ Canonical Node -- C + * + * NOTE + * - T/F Node is disconnected from C after transformation + */ + + // TFSqueeze had to be inserted if keep_dims() was false + assert(node->keep_dims()); + + auto axes_node = node->reduction_indices(); + assert(axes_node != nullptr); + + auto node_tensor_shape = loco::shape_get(node).template as<loco::TensorShape>(); + + // Canonicalization into TensorReduce is valid when reduction indices is constant + // TODO Support general TensorReduce case + std::vector<int32_t> axes_values; + if (auto const_axes = dynamic_cast<moco::TFConst *>(axes_node)) + { + // TODO Support S64 type + assert(const_axes->dtype() == loco::DataType::S32); + + for (uint32_t i = 0; i < const_axes->size<loco::DataType::S32>(); ++i) + { + int32_t axis = const_axes->at<loco::DataType::S32>(i); + if (axis < 0) + axis += node_tensor_shape.rank(); + axes_values.push_back(axis); + } + } + else if (auto const_axes = dynamic_cast<loco::ConstGen *>(axes_node)) + { + // TODO Support S64 type + assert(const_axes->dtype() == loco::DataType::S32); + + for (uint32_t i = 0; i < const_axes->size<loco::DataType::S32>(); ++i) + { + int32_t axis = const_axes->at<loco::DataType::S32>(i); + if (axis < 0) + axis += node_tensor_shape.rank(); + axes_values.push_back(axis); + } + } + else + return false; + + // Create loco node to replace + auto reduce = graph->nodes()->template create<loco::TensorReduce>(); + + // replace + reduce->func(reduceFunc<TFNode>()); + reduce->input(node->input()); + for (uint32_t i = 0; i < axes_values.size(); ++i) + reduce->axes()->insert(axes_values.at(i)); + + replace(node).with(reduce); + + INFO(l) << "TFNodeCanonicalize ReduceNode done"; + + return true; +} + +} // namespace + +#endif // __TF_REDUCE_CANONICALIZE_HELPER_H__ diff --git a/compiler/moco-tf/src/TestHelper.h b/compiler/moco-tf/src/TestHelper.h new file mode 100644 index 000000000..dd32d4433 --- /dev/null +++ b/compiler/moco-tf/src/TestHelper.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TEST_HELPER_H__ +#define __TEST_HELPER_H__ + +#include <loco.h> + +#include <tensorflow/core/framework/graph.pb.h> + +#define STRING_CONTENT(content) #content + +namespace moco +{ +namespace tf +{ +namespace test +{ + +template <typename T> T *find_first_node_bytype(loco::Graph *g) +{ + T *first_node = nullptr; + loco::Graph::NodeContext *nodes = g->nodes(); + uint32_t count = nodes->size(); + + for (uint32_t i = 0; i < count; ++i) + { + first_node = dynamic_cast<T *>(nodes->at(i)); + if (first_node != nullptr) + break; + } + + return first_node; +} + +template <typename T> std::vector<T *> find_nodes_bytype(loco::Graph *g) +{ + std::vector<T *> find_nodes; + loco::Graph::NodeContext *nodes = g->nodes(); + uint32_t count = nodes->size(); + + for (uint32_t i = 0; i < count; ++i) + { + auto node = dynamic_cast<T *>(nodes->at(i)); + if (node != nullptr) + find_nodes.push_back(node); + } + + return find_nodes; +} + +/** + * @brief Append setup output of graph by adding loco::Push node + * + * @note This is subject to change when loco changes I/O treatment + */ +void setup_output_node(loco::Graph *graph, loco::Node *last_node); + +} // namespace test +} // namespace tf +} // namespace moco + +#include <moco/IR/TFNode.h> + +#include <moco/Import/GraphBuilder.h> + +#include <plier/tf/TestHelper.h> + +namespace moco +{ +namespace tf +{ +namespace test +{ + +class TFNodeBuildTester +{ +public: + TFNodeBuildTester(); + +public: + void inputs(const std::vector<std::string> &names); + void output(const char *name); + moco::TFNode *output(void); + + void run(tensorflow::NodeDef &node_def, moco::GraphBuilder &graph_builder); + +private: + std::unique_ptr<moco::SymbolTable> _tensor_names; + std::unique_ptr<loco::Graph> _graph; + + std::vector<moco::TFNode *> _inputs; + const char *_output{nullptr}; +}; + +} // namespace test +} // namespace tf +} // namespace moco + +#endif // __TEST_HELPER_H__ diff --git a/compiler/moco-tf/src/TestHelper.test.cpp b/compiler/moco-tf/src/TestHelper.test.cpp new file mode 100644 index 000000000..1e8c38e36 --- /dev/null +++ b/compiler/moco-tf/src/TestHelper.test.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TestHelper.h" + +#include <google/protobuf/io/coded_stream.h> +#include <google/protobuf/io/zero_copy_stream_impl.h> +#include <google/protobuf/text_format.h> + +#include <cstring> + +namespace moco +{ +namespace tf +{ +namespace test +{ + +void setup_output_node(loco::Graph *graph, loco::Node *last_node) +{ + // add push as output + auto push_node = graph->nodes()->create<loco::Push>(); + push_node->from(last_node); + + // set the graph output name and node object + auto graph_output = graph->outputs()->create(); + graph_output->name("output"); + graph_output->dtype(loco::DataType::FLOAT32); + loco::link(graph_output, push_node); +} + +} // namespace test +} // namespace tf +} // namespace moco + +#include <moco/IR/Nodes/TFConst.h> + +#include <stdex/Memory.h> + +#include <gtest/gtest.h> + +namespace moco +{ +namespace tf +{ +namespace test +{ + +TFNodeBuildTester::TFNodeBuildTester() +{ + _graph = loco::make_graph(); + _tensor_names = stdex::make_unique<moco::SymbolTable>(); +} + +void TFNodeBuildTester::inputs(const std::vector<std::string> &names) +{ + for (auto name : names) + { + auto input = _graph->nodes()->create<moco::TFConst>(); + moco::TensorName name_01(name, 0); + _tensor_names->enroll(name_01, input); + + _inputs.push_back(input); + } +} + +void TFNodeBuildTester::output(const char *name) { _output = name; } + +moco::TFNode *TFNodeBuildTester::output(void) +{ + assert(_output != nullptr); + + moco::TensorName tname(_output, 0); + return static_cast<moco::TFNode *>(_tensor_names->node(tname)); +} + +void TFNodeBuildTester::run(tensorflow::NodeDef &nodedef, moco::GraphBuilder &graphbuilder) +{ + assert(_output != nullptr); + + auto node_defs = stdex::make_unique<moco::NodeDefTable>(); + auto updates = stdex::make_unique<moco::UpdateQueue>(); + + moco::GraphBuilderContext gb_context(_graph.get(), node_defs.get(), _tensor_names.get(), + updates.get()); + + EXPECT_TRUE(graphbuilder.validate(nodedef)); + graphbuilder.build(nodedef, &gb_context); + + for (auto &update : updates->queue()) + { + update->input(_tensor_names.get()); + } + + auto tfnode = output(); + ASSERT_NE(tfnode, nullptr); + + int idx = 0; + ASSERT_EQ(tfnode->arity(), _inputs.size()); + for (auto input : _inputs) + { + ASSERT_EQ(tfnode->arg(idx++), input); + } +} + +} // namespace test +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Transform.cpp b/compiler/moco-tf/src/Transform.cpp new file mode 100644 index 000000000..f19ce21c4 --- /dev/null +++ b/compiler/moco-tf/src/Transform.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Transform.h" + +namespace moco +{ +namespace tf +{ + +std::string transform_name(const Transform *t) +{ + if (t->name() == nullptr) + { + return "(unknown)"; + } + + return t->name(); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Transform.h b/compiler/moco-tf/src/Transform.h new file mode 100644 index 000000000..80cb9f97f --- /dev/null +++ b/compiler/moco-tf/src/Transform.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_TRANSFORM_H__ +#define __MOCO_TF_TRANSFORM_H__ + +#include <loco.h> + +#include <logo/Pass.h> + +#include <string> + +namespace moco +{ +namespace tf +{ + +/** + * @note Transform will be replaced by logo::Pass + */ + +using Transform = logo::Pass; + +std::string transform_name(const Transform *); + +template <typename DERIVED> DERIVED *as(loco::Node *node) { return dynamic_cast<DERIVED *>(node); } + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_TRANSFORM_H__ diff --git a/compiler/moco-tf/src/Transform.test.cpp b/compiler/moco-tf/src/Transform.test.cpp new file mode 100644 index 000000000..e029b54c4 --- /dev/null +++ b/compiler/moco-tf/src/Transform.test.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Transform.h" + +#include <loco.h> + +#include <gtest/gtest.h> + +TEST(Transform, transform_name_over_unnamed_transform) +{ + struct SampleTransform final : public moco::tf::Transform + { + bool run(loco::Graph *) final { return false; } + }; + + SampleTransform sample_transform; + + ASSERT_EQ(moco::tf::transform_name(&sample_transform), "(unknown)"); +} + +TEST(Transform, transform_name_over_named_transform) +{ + struct SampleTransform final : public moco::tf::Transform + { + const char *name(void) const final { return "sample"; } + bool run(loco::Graph *) final { return false; } + }; + + SampleTransform sample_transform; + + ASSERT_EQ(moco::tf::transform_name(&sample_transform), "sample"); +} diff --git a/compiler/moco-tf/src/Transforms.h b/compiler/moco-tf/src/Transforms.h new file mode 100644 index 000000000..f14b81675 --- /dev/null +++ b/compiler/moco-tf/src/Transforms.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_TRANSFORMS_H__ +#define __MOCO_TF_TRANSFORMS_H__ + +#include "Transforms/ShapeInferencePass.h" +#include "Transforms/TypeInferencePass.h" + +#include <logo/Passes.h> +#include <moco/Pass/Passes.h> + +#endif // __MOCO_TF_TRANSFORMS_H__ diff --git a/compiler/moco-tf/src/Transforms/ShapeInferencePass.cpp b/compiler/moco-tf/src/Transforms/ShapeInferencePass.cpp new file mode 100644 index 000000000..64ba9dfb1 --- /dev/null +++ b/compiler/moco-tf/src/Transforms/ShapeInferencePass.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ShapeInferencePass.h" + +#include <moco/IR/TFDialect.h> + +#include <moco/Service/TFShapeInferenceRule.h> + +#include <loco.h> + +#include <loco/IR/CanonicalDialect.h> + +#include <loco/Service/ShapeInference.h> +#include <loco/Service/ShapeInferenceRule.h> +#include <loco/Service/CanonicalShapeInferenceRule.h> +#include <loco/Service/MultiDialectShapeInferenceRule.h> + +#include <locoex/COpDialect.h> +#include <locoex/Service/COpShapeInferenceRule.h> + +namespace moco +{ +namespace tf +{ + +bool ShapeInferencePass::run(loco::Graph *graph) +{ + loco::CanonicalShapeInferenceRule canonical_rule; + moco::TFShapeInferenceRule tf_rule; + locoex::COpShapeInferenceRule cop_rule; // rule for custop op + + loco::MultiDialectShapeInferenceRule rules; + + rules.bind(loco::CanonicalDialect::get(), &canonical_rule) + .bind(TFDialect::get(), &tf_rule) + .bind(locoex::COpDialect::get(), &cop_rule); + + return loco::apply(&rules).to(graph); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Transforms/ShapeInferencePass.h b/compiler/moco-tf/src/Transforms/ShapeInferencePass.h new file mode 100644 index 000000000..f8be5f146 --- /dev/null +++ b/compiler/moco-tf/src/Transforms/ShapeInferencePass.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_SHAPE_INFERENCE_PASS_H__ +#define __MOCO_TF_SHAPE_INFERENCE_PASS_H__ + +#include "Transform.h" + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Run shape inference to the graph + */ +class ShapeInferencePass : public Transform +{ +public: + const char *name(void) const final { return "ShapeInferencePass"; } + +public: + bool run(loco::Graph *graph) override; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_SHAPE_INFERENCE_PASS_H__ diff --git a/compiler/moco-tf/src/Transforms/TypeInferencePass.cpp b/compiler/moco-tf/src/Transforms/TypeInferencePass.cpp new file mode 100644 index 000000000..db6cf7521 --- /dev/null +++ b/compiler/moco-tf/src/Transforms/TypeInferencePass.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TypeInferencePass.h" + +#include <moco/IR/TFDialect.h> + +#include <moco/Service/TFTypeInferenceRule.h> + +#include <loco.h> + +#include <loco/IR/CanonicalDialect.h> +#include <loco/Service/TypeInference.h> + +#include <locoex/COpDialect.h> +#include <locoex/Service/COpTypeInference.h> + +namespace moco +{ +namespace tf +{ + +bool TypeInferencePass::run(loco::Graph *graph) +{ + loco::CanonicalTypeInferenceRule canonical_rule; + moco::TFTypeInferenceRule tf_rule; // rule for TF dialect + locoex::COpTypeInferenceRule cop_rule; // rule for custop op + + loco::MultiDialectTypeInferenceRule rules; + + rules.bind(loco::CanonicalDialect::get(), &canonical_rule) + .bind(TFDialect::get(), &tf_rule) + .bind(locoex::COpDialect::get(), &cop_rule); + + loco::apply(&rules).to(graph); + + return false; +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Transforms/TypeInferencePass.h b/compiler/moco-tf/src/Transforms/TypeInferencePass.h new file mode 100644 index 000000000..88a2f86f1 --- /dev/null +++ b/compiler/moco-tf/src/Transforms/TypeInferencePass.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TF_TYPE_INFERENCE_PASS_H__ +#define __MOCO_TF_TYPE_INFERENCE_PASS_H__ + +#include "Transform.h" + +#include <loco.h> + +namespace moco +{ +namespace tf +{ + +/** + * @brief Run type inference to the graph + */ +class TypeInferencePass : public Transform +{ +public: + const char *name(void) const final { return "TypeInferencePass"; } + +public: + bool run(loco::Graph *graph) override; +}; + +} // namespace tf +} // namespace moco + +#endif // __MOCO_TF_TYPE_INFERENCE_PASS_H__ |