diff options
Diffstat (limited to 'compiler/moco')
255 files changed, 19805 insertions, 0 deletions
diff --git a/compiler/moco/CMakeLists.txt b/compiler/moco/CMakeLists.txt new file mode 100644 index 000000000..9fdd4398e --- /dev/null +++ b/compiler/moco/CMakeLists.txt @@ -0,0 +1,5 @@ +add_subdirectory(lang) +add_subdirectory(support) +add_subdirectory(service) +add_subdirectory(import) +add_subdirectory(pass) diff --git a/compiler/moco/README.md b/compiler/moco/README.md new file mode 100644 index 000000000..13c7aaae3 --- /dev/null +++ b/compiler/moco/README.md @@ -0,0 +1,3 @@ +# moco + +_moco_ provides building blocks to load and process TensorFlow models and to produce graph of loco canonical IR diff --git a/compiler/moco/import/CMakeLists.txt b/compiler/moco/import/CMakeLists.txt new file mode 100644 index 000000000..43107776e --- /dev/null +++ b/compiler/moco/import/CMakeLists.txt @@ -0,0 +1,26 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(moco_import SHARED ${SOURCES}) +target_include_directories(moco_import PRIVATE src) +target_include_directories(moco_import PUBLIC include) +target_link_libraries(moco_import PUBLIC moco_lang) +target_link_libraries(moco_import PUBLIC mio_tf) +target_link_libraries(moco_import PUBLIC stdex) +target_link_libraries(moco_import PRIVATE nncc_common) +target_link_libraries(moco_import PRIVATE plier_tf) +target_link_libraries(moco_import PRIVATE oops) +install(TARGETS moco_import DESTINATION lib) # moco_tf_frontend requires moco_import + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(moco_import_test ${TESTS}) +target_include_directories(moco_import_test PRIVATE src) +target_link_libraries(moco_import_test moco_import) +target_link_libraries(moco_import_test plier_tf) +target_link_libraries(moco_import_test oops) diff --git a/compiler/moco/import/README.md b/compiler/moco/import/README.md new file mode 100644 index 000000000..2704d35d6 --- /dev/null +++ b/compiler/moco/import/README.md @@ -0,0 +1,3 @@ +# moco-import + +_moco-import_ provides importing TensorFlow model file to _moco_ TensorFlow Dialect IR diff --git a/compiler/moco/import/include/moco/GraphHelper.h b/compiler/moco/import/include/moco/GraphHelper.h new file mode 100644 index 000000000..fad62af4e --- /dev/null +++ b/compiler/moco/import/include/moco/GraphHelper.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 __MOCO_GRAPH_HELPER_H__ +#define __MOCO_GRAPH_HELPER_H__ + +#include <moco/IR/TFNode.h> + +#include <loco.h> + +namespace moco +{ + +/** + * @brief find_node_byname() will return a node with type T with given name + * in graph g + * + * @note this uses simple linear search, but can speed up with better + * algorithms when needed. + */ +template <typename T> T *find_node_byname(loco::Graph *g, const char *name) +{ + T *first_node = nullptr; + loco::Graph::NodeContext *nodes = g->nodes(); + uint32_t count = nodes->size(); + + for (uint32_t i = 0; i < count; ++i) + { + auto tfnode = dynamic_cast<TFNode *>(nodes->at(i)); + if (tfnode != nullptr) + { + if (tfnode->name() == name) + { + // if tfnode is NOT type of T then return will be nullptr + // this is OK cause the user wanted to get type T but it isn't + return dynamic_cast<T *>(tfnode); + } + } + } + + return nullptr; +} + +} // namespace moco + +#endif // __MOCO_GRAPH_HELPER_H__ diff --git a/compiler/moco/import/include/moco/Import/GraphBuilder.h b/compiler/moco/import/include/moco/Import/GraphBuilder.h new file mode 100644 index 000000000..c19918def --- /dev/null +++ b/compiler/moco/import/include/moco/Import/GraphBuilder.h @@ -0,0 +1,40 @@ +/* + * 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_IMPORT_GRAPH_BUILDER_H__ +#define __MOCO_IMPORT_GRAPH_BUILDER_H__ + +#include "GraphBuilderContext.h" + +#include <tensorflow/core/framework/graph.pb.h> + +namespace moco +{ + +/** + * @brief Interface of convert TF NodeDef to loco::Node (e.g., Conv2DGraphBuilder) + */ +class GraphBuilder +{ +public: + virtual bool validate(const tensorflow::NodeDef &) const = 0; + virtual void build(const tensorflow::NodeDef &, GraphBuilderContext *) const = 0; + virtual ~GraphBuilder() {} +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_GRAPH_BUILDER_H__ diff --git a/compiler/moco/import/include/moco/Import/GraphBuilderContext.h b/compiler/moco/import/include/moco/Import/GraphBuilderContext.h new file mode 100644 index 000000000..ae4f02c2a --- /dev/null +++ b/compiler/moco/import/include/moco/Import/GraphBuilderContext.h @@ -0,0 +1,144 @@ +/* + * 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_IMPORT_GRAPH_BUILDER_CONTEXT_H__ +#define __MOCO_IMPORT_GRAPH_BUILDER_CONTEXT_H__ + +#include <moco/Names.h> + +#include <loco.h> + +#include <tensorflow/core/framework/graph.pb.h> + +#include <map> +#include <memory> +#include <string> +#include <vector> + +namespace moco +{ + +/** + * @brief Class to store and query tensorflow::NodeDef* with string name key + */ +class NodeDefTable +{ +public: + /** + * @brief Registers a name with corresponding tensorflow::NodeDef* + */ + void enroll(const std::string &node_name, const tensorflow::NodeDef *node); + /** + * @brief Queries enrolled(registered) with name and return node if found + * Will throw runtime_error if not found + */ + const tensorflow::NodeDef *node(const std::string &node_name) const; + +private: + using MapNameNode_t = std::map<std::string, const tensorflow::NodeDef *>; + + MapNameNode_t _table; +}; + +/** + * @brief Class to store and query loco::Node* with string name key + */ +class SymbolTable +{ +public: + /** + * @brief Registers a name with corresponding loco::Node * + */ + void enroll(const TensorName &tensor_name, loco::Node *node); + /** + * @brief Queries enrolled(registered) with name and return node if found + * Will throw runtime_error if not found + */ + loco::Node *node(const TensorName &tensor_name) const; + +private: + using MapNameNode_t = std::map<TensorName, loco::Node *, TensorNameCompare>; + + MapNameNode_t _table; +}; + +/** + * @brief Interface to connect the graph + */ +class GraphUpdate +{ +public: + virtual ~GraphUpdate() = default; + +public: + /** + * @brief Do the graph input connections using the SymbolTable + */ + virtual void input(const SymbolTable *) const = 0; +}; + +/** + * @brief Class to store GraphUpdate objects + */ +class UpdateQueue final +{ +public: + /** + * @brief Registers GraphUpdate objects + */ + void enroll(std::unique_ptr<GraphUpdate> &&update); + +public: + using Queue = std::vector<std::unique_ptr<GraphUpdate>>; + + const Queue &queue() const { return _queue; } + +private: + Queue _queue; +}; + +/** + * @brief Class to store context to build loco graph IR from TensorFlow + */ +class GraphBuilderContext +{ +public: + GraphBuilderContext(loco::Graph *g, NodeDefTable *nodedef, SymbolTable *tensor_names, + UpdateQueue *updates) + : _g(g), _nodedef(nodedef), _tensor_names(tensor_names), _updates(updates) + { + // DO NOTHING + } + + GraphBuilderContext(const GraphBuilderContext &) = delete; + GraphBuilderContext(GraphBuilderContext &&) = delete; + +public: + loco::Graph *graph() { return _g; } + NodeDefTable *nodedef() { return _nodedef; } + SymbolTable *tensor_names() { return _tensor_names; } + UpdateQueue *updates() { return _updates; } + +private: + loco::Graph *_g; + NodeDefTable *_nodedef; + SymbolTable *_tensor_names; + UpdateQueue *_updates; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_GRAPH_BUILDER_CONTEXT_H__ diff --git a/compiler/moco/import/include/moco/Import/GraphBuilderRegistry.h b/compiler/moco/import/include/moco/Import/GraphBuilderRegistry.h new file mode 100644 index 000000000..da65cffb8 --- /dev/null +++ b/compiler/moco/import/include/moco/Import/GraphBuilderRegistry.h @@ -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. + */ + +#ifndef __MOCO_IMPORT_GRAPH_BUILDER_REGISTRY_H__ +#define __MOCO_IMPORT_GRAPH_BUILDER_REGISTRY_H__ + +#include "moco/Import/GraphBuilder.h" + +#include <map> +#include <memory> +#include <string> + +namespace moco +{ + +struct GraphBuilderSource +{ + virtual ~GraphBuilderSource() = default; + + /** + * @brief Returns registered GraphBuilder pointer for operator (nullptr if not present) + */ + virtual const GraphBuilder *lookup(const std::string &op) const = 0; +}; + +/** + * @brief Class to return graph builder for TF nodes + */ +class GraphBuilderRegistry final : public GraphBuilderSource +{ +public: + GraphBuilderRegistry(); + +public: + GraphBuilderRegistry(const GraphBuilderSource *parent) : _parent{parent} + { + // DO NOTHING + } + +public: + /** + * @brief Returns registered GraphBuilder pointer for operator or + * nullptr if not registered + */ + const GraphBuilder *lookup(const std::string &op) const final + { + if (_builder_map.find(op) == _builder_map.end()) + return (_parent == nullptr) ? nullptr : _parent->lookup(op); + + return _builder_map.at(op).get(); + } + + static GraphBuilderRegistry &get() + { + static GraphBuilderRegistry me; + return me; + } + +public: + void add(const std::string op, std::unique_ptr<GraphBuilder> &&builder) + { + _builder_map[op] = std::move(builder); + } + +private: + const GraphBuilderSource *_parent = nullptr; + +private: + std::map<const std::string, std::unique_ptr<GraphBuilder>> _builder_map; +}; + +} // namespace mono + +#endif // __MOCO_IMPORT_GRAPH_BUILDER_REGISTRY_H__ diff --git a/compiler/moco/import/include/moco/Import/ModelSignature.h b/compiler/moco/import/include/moco/Import/ModelSignature.h new file mode 100644 index 000000000..0db7c2795 --- /dev/null +++ b/compiler/moco/import/include/moco/Import/ModelSignature.h @@ -0,0 +1,80 @@ +/* + * 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_IMPORT_MODELSIGNATURE_H__ +#define __MOCO_IMPORT_MODELSIGNATURE_H__ + +#include <moco/Names.h> + +#include <loco.h> +#include <angkor/TensorShape.h> + +#include <string> +#include <vector> + +namespace moco +{ + +/** + * @brief Class to store information to run a model. Normally this info comes from users + * via CLI params or configuration file. + */ +struct ModelSignature +{ +public: + void add_input(const TensorName &input) { _inputs.push_back(input); } + void add_input(const TensorName &&input) { _inputs.push_back(input); } + void add_output(const TensorName &output) { _outputs.push_back(output); } + void add_output(const TensorName &&output) { _outputs.push_back(output); } + + const std::vector<TensorName> &inputs() const { return _inputs; } + const std::vector<TensorName> &outputs() const { return _outputs; } + + /** + * @brief Adds customop op type (not name of node) provided from user + */ + void add_customop(const std::string &op); + const std::vector<std::string> &customops() const { return _customops; } + + /** + * @brief Adds node name and its shape provided from user + */ + void shape(const std::string &node_name, const angkor::TensorShape &shape); + const angkor::TensorShape *shape(const std::string &node_name) const; + + /** + * @brief Adds node name and its dtype provided from user + */ + void dtype(const std::string &node_name, loco::DataType dtype); + loco::DataType dtype(const std::string &node_name) const; + +private: + std::vector<TensorName> _inputs; // graph inputs + std::vector<TensorName> _outputs; // graph outputs + + // For custom op types passed from user (e.g., via CLI) + std::vector<std::string> _customops; + + // For and node names and shapes passed from user (e.g., via CLI) + std::map<std::string, angkor::TensorShape> _shapes; + + // For and node names and dtype passed from user (e.g., via CLI) + std::map<std::string, loco::DataType> _dtypes; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_MODELSIGNATURE_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes.h b/compiler/moco/import/include/moco/Import/Nodes.h new file mode 100644 index 000000000..8c940a28c --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes.h @@ -0,0 +1,53 @@ +/* + * 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_IMPORT_NODES_H__ +#define __MOCO_IMPORT_NODES_H__ + +#include "Nodes/Add.h" +#include "Nodes/AvgPool.h" +#include "Nodes/BiasAdd.h" +#include "Nodes/Concat.h" +#include "Nodes/Const.h" +#include "Nodes/Conv2DBackpropInput.h" +#include "Nodes/Conv2D.h" +#include "Nodes/DepthwiseConv2dNative.h" +#include "Nodes/FakeQuantWithMinMaxVars.h" +#include "Nodes/FusedBatchNorm.h" +#include "Nodes/Identity.h" +#include "Nodes/Maximum.h" +#include "Nodes/MaxPool.h" +#include "Nodes/Mean.h" +#include "Nodes/Mul.h" +#include "Nodes/Pack.h" +#include "Nodes/Pad.h" +#include "Nodes/Placeholder.h" +#include "Nodes/RealDiv.h" +#include "Nodes/Relu6.h" +#include "Nodes/Relu.h" +#include "Nodes/Reshape.h" +#include "Nodes/Rsqrt.h" +#include "Nodes/Shape.h" +#include "Nodes/Softmax.h" +#include "Nodes/Sqrt.h" +#include "Nodes/SquaredDifference.h" +#include "Nodes/Squeeze.h" +#include "Nodes/StopGradient.h" +#include "Nodes/StridedSlice.h" +#include "Nodes/Sub.h" +#include "Nodes/Tanh.h" + +#endif // __MOCO_IMPORT_NODES_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Add.h b/compiler/moco/import/include/moco/Import/Nodes/Add.h new file mode 100644 index 000000000..3d0d0f30f --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Add.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_ADD_H__ +#define __MOCO_IMPORT_OP_ADD_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for Add node + */ +class AddGraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_ADD_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/AvgPool.h b/compiler/moco/import/include/moco/Import/Nodes/AvgPool.h new file mode 100644 index 000000000..4c8087afe --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/AvgPool.h @@ -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. + */ + +#ifndef __MOCO_IMPORT_OP_AVG_POOL_H__ +#define __MOCO_IMPORT_OP_AVG_POOL_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +class AvgPoolGraphBuilder : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const final; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_AVG_POOL_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/BiasAdd.h b/compiler/moco/import/include/moco/Import/Nodes/BiasAdd.h new file mode 100644 index 000000000..214df03de --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/BiasAdd.h @@ -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. + */ + +#ifndef __MOCO_IMPORT_OP_BIAS_ADD_H__ +#define __MOCO_IMPORT_OP_BIAS_ADD_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +class BiasAddGraphBuilder : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const final; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_BIAS_ADD_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Concat.h b/compiler/moco/import/include/moco/Import/Nodes/Concat.h new file mode 100644 index 000000000..2341fb00c --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Concat.h @@ -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. + */ + +#ifndef __MOCO_IMPORT_OP_CONCAT_H__ +#define __MOCO_IMPORT_OP_CONCAT_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +class ConcatV2GraphBuilder : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const final; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_CONCAT_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Const.h b/compiler/moco/import/include/moco/Import/Nodes/Const.h new file mode 100644 index 000000000..1ce378219 --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Const.h @@ -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. + */ + +#ifndef __MOCO_IMPORT_OP_CONST_H__ +#define __MOCO_IMPORT_OP_CONST_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +class ConstGraphBuilder : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const final; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_CONST_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Conv2D.h b/compiler/moco/import/include/moco/Import/Nodes/Conv2D.h new file mode 100644 index 000000000..3bd3dc74a --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Conv2D.h @@ -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. + */ + +#ifndef __MOCO_IMPORT_OP_CONV_2D_H__ +#define __MOCO_IMPORT_OP_CONV_2D_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +class Conv2DGraphBuilder : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const final; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_CONV_2D_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Conv2DBackpropInput.h b/compiler/moco/import/include/moco/Import/Nodes/Conv2DBackpropInput.h new file mode 100644 index 000000000..262a443fe --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Conv2DBackpropInput.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_CONV_2D_BACKPROP_INPUT_H__ +#define __MOCO_IMPORT_OP_CONV_2D_BACKPROP_INPUT_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for Conv2DBackpropInput node + */ +class Conv2DBackpropInputGraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_CONV_2D_BACKPROP_INPUT_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/DepthwiseConv2dNative.h b/compiler/moco/import/include/moco/Import/Nodes/DepthwiseConv2dNative.h new file mode 100644 index 000000000..1dcbba1eb --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/DepthwiseConv2dNative.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_DEPTHWISE_CONV_2D_NATIVE_H__ +#define __MOCO_IMPORT_OP_DEPTHWISE_CONV_2D_NATIVE_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for DepthwiseConv2dNative node + */ +class DepthwiseConv2dNativeGraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_DEPTHWISE_CONV_2D_NATIVE_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/FakeQuantWithMinMaxVars.h b/compiler/moco/import/include/moco/Import/Nodes/FakeQuantWithMinMaxVars.h new file mode 100644 index 000000000..9e223c18e --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/FakeQuantWithMinMaxVars.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_FAKE_QUANT_WITH_MINMAX_VARS_H__ +#define __MOCO_IMPORT_OP_FAKE_QUANT_WITH_MINMAX_VARS_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for FakeQuantWithMinMaxVars node + */ +class FakeQuantWithMinMaxVarsGraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_FAKE_QUANT_WITH_MINMAX_VARS_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/FusedBatchNorm.h b/compiler/moco/import/include/moco/Import/Nodes/FusedBatchNorm.h new file mode 100644 index 000000000..38d1d5682 --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/FusedBatchNorm.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_FUSED_BATCH_NORM_H__ +#define __MOCO_IMPORT_OP_FUSED_BATCH_NORM_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for FusedBatchNorm node + */ +class FusedBatchNormGraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_FUSED_BATCH_NORM_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Identity.h b/compiler/moco/import/include/moco/Import/Nodes/Identity.h new file mode 100644 index 000000000..29e04800f --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Identity.h @@ -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. + */ + +#ifndef __MOCO_IMPORT_OP_IDENTITY_H__ +#define __MOCO_IMPORT_OP_IDENTITY_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +class IdentityGraphBuilder : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const final; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_IDENTITY_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/MaxPool.h b/compiler/moco/import/include/moco/Import/Nodes/MaxPool.h new file mode 100644 index 000000000..696fa71e6 --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/MaxPool.h @@ -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. + */ + +#ifndef __MOCO_IMPORT_OP_MAX_POOL_H__ +#define __MOCO_IMPORT_OP_MAX_POOL_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +class MaxPoolGraphBuilder : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const final; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_MAX_POOL_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Maximum.h b/compiler/moco/import/include/moco/Import/Nodes/Maximum.h new file mode 100644 index 000000000..69d897742 --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Maximum.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_IMPORT_OP_MAXIMUM_H__ +#define __MOCO_IMPORT_OP_MAXIMUM_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for Maximum node + */ +class MaximumGraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_MAXIMUM_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Mean.h b/compiler/moco/import/include/moco/Import/Nodes/Mean.h new file mode 100644 index 000000000..7bae1bb39 --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Mean.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_MEAN_H__ +#define __MOCO_IMPORT_OP_MEAN_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for Mean node + */ +class MeanGraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_MEAN_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Mul.h b/compiler/moco/import/include/moco/Import/Nodes/Mul.h new file mode 100644 index 000000000..667c81954 --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Mul.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_MUL_H__ +#define __MOCO_IMPORT_OP_MUL_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for Mul node + */ +class MulGraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_MUL_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Pack.h b/compiler/moco/import/include/moco/Import/Nodes/Pack.h new file mode 100644 index 000000000..94666ad51 --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Pack.h @@ -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. + */ + +#ifndef __MOCO_IMPORT_OP_PACK_H__ +#define __MOCO_IMPORT_OP_PACK_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +class PackGraphBuilder : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const final; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_PACK_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Pad.h b/compiler/moco/import/include/moco/Import/Nodes/Pad.h new file mode 100644 index 000000000..22eab32ac --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Pad.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_PAD_H__ +#define __MOCO_IMPORT_OP_PAD_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for Pad node + */ +class PadGraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_PAD_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Placeholder.h b/compiler/moco/import/include/moco/Import/Nodes/Placeholder.h new file mode 100644 index 000000000..458600915 --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Placeholder.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_PLACEHOLDER_H__ +#define __MOCO_IMPORT_OP_PLACEHOLDER_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for Placeholder node + */ +class PlaceholderGraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_PLACEHOLDER_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/RealDiv.h b/compiler/moco/import/include/moco/Import/Nodes/RealDiv.h new file mode 100644 index 000000000..142e8b5f8 --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/RealDiv.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_REALDIV_H__ +#define __MOCO_IMPORT_OP_REALDIV_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for RealDiv node + */ +class RealDivGraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_REALDIV_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Relu.h b/compiler/moco/import/include/moco/Import/Nodes/Relu.h new file mode 100644 index 000000000..0bd9cff04 --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Relu.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_RELU_H__ +#define __MOCO_IMPORT_OP_RELU_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for Relu node + */ +class ReluGraphBuilder : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const final; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_RELU_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Relu6.h b/compiler/moco/import/include/moco/Import/Nodes/Relu6.h new file mode 100644 index 000000000..d211b0543 --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Relu6.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_RELU6_H__ +#define __MOCO_IMPORT_OP_RELU6_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for Relu6 node + */ +class Relu6GraphBuilder : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const final; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_RELU6_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Reshape.h b/compiler/moco/import/include/moco/Import/Nodes/Reshape.h new file mode 100644 index 000000000..e8bfeee23 --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Reshape.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_RESHAPE_H__ +#define __MOCO_IMPORT_OP_RESHAPE_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for Reshape node + */ +class ReshapeGraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_RESHAPE_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Rsqrt.h b/compiler/moco/import/include/moco/Import/Nodes/Rsqrt.h new file mode 100644 index 000000000..dedc52323 --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Rsqrt.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_RSQRT_H__ +#define __MOCO_IMPORT_OP_RSQRT_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for Rsqrt node + */ +class RsqrtGraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_RSQRT_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Shape.h b/compiler/moco/import/include/moco/Import/Nodes/Shape.h new file mode 100644 index 000000000..e36e1d546 --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Shape.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_SHAPE_H__ +#define __MOCO_IMPORT_OP_SHAPE_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for Shape node + */ +class ShapeGraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_SHAPE_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Softmax.h b/compiler/moco/import/include/moco/Import/Nodes/Softmax.h new file mode 100644 index 000000000..43fbb8852 --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Softmax.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_SOFTMAX_H__ +#define __MOCO_IMPORT_OP_SOFTMAX_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** +* @brief GraphBuilder for Softmax node +*/ +class SoftmaxGraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_SOFTMAX_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Sqrt.h b/compiler/moco/import/include/moco/Import/Nodes/Sqrt.h new file mode 100644 index 000000000..d17dc3494 --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Sqrt.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_SQRT_H__ +#define __MOCO_IMPORT_OP_SQRT_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for Sqrt node + */ +class SqrtGraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_SQRT_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/SquaredDifference.h b/compiler/moco/import/include/moco/Import/Nodes/SquaredDifference.h new file mode 100644 index 000000000..501464d65 --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/SquaredDifference.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_SQUARED_DIFFERENCE_H__ +#define __MOCO_IMPORT_OP_SQUARED_DIFFERENCE_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for SquaredDifference node + */ +class SquaredDifferenceGraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_SQUARED_DIFFERENCE_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Squeeze.h b/compiler/moco/import/include/moco/Import/Nodes/Squeeze.h new file mode 100644 index 000000000..64ead074b --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Squeeze.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_SQUEEZE_H__ +#define __MOCO_IMPORT_OP_SQUEEZE_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for Squeeze node + */ +class SqueezeGraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_SQUEEZE_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/StopGradient.h b/compiler/moco/import/include/moco/Import/Nodes/StopGradient.h new file mode 100644 index 000000000..e547a8a8b --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/StopGradient.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_STOP_GRADIENT_H__ +#define __MOCO_IMPORT_OP_STOP_GRADIENT_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for StopGradient node + */ +class StopGradientGraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_STOP_GRADIENT_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/StridedSlice.h b/compiler/moco/import/include/moco/Import/Nodes/StridedSlice.h new file mode 100644 index 000000000..61170ebbf --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/StridedSlice.h @@ -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. + */ + +#ifndef __MOCO_IMPORT_OP_STRIDEDSLICE_H__ +#define __MOCO_IMPORT_OP_STRIDEDSLICE_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +class StridedSliceGraphBuilder : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const final; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_STRIDEDSLICE_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Sub.h b/compiler/moco/import/include/moco/Import/Nodes/Sub.h new file mode 100644 index 000000000..d6351e34a --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Sub.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_SUB_H__ +#define __MOCO_IMPORT_OP_SUB_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for Sub node + */ +class SubGraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_SUB_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Tanh.h b/compiler/moco/import/include/moco/Import/Nodes/Tanh.h new file mode 100644 index 000000000..183e117ef --- /dev/null +++ b/compiler/moco/import/include/moco/Import/Nodes/Tanh.h @@ -0,0 +1,37 @@ +/* + * 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_IMPORT_OP_TANH_H__ +#define __MOCO_IMPORT_OP_TANH_H__ + +#include "moco/Import/GraphBuilder.h" + +namespace moco +{ + +/** + * @brief GraphBuilder for Tanh node + */ +class TanhGraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +} // namespace moco + +#endif // __MOCO_IMPORT_OP_TANH_H__ diff --git a/compiler/moco/import/include/moco/Importer.h b/compiler/moco/import/include/moco/Importer.h new file mode 100644 index 000000000..ee0660c52 --- /dev/null +++ b/compiler/moco/import/include/moco/Importer.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_IMPORTER_H__ +#define __MOCO_IMPORTER_H__ + +#include "moco/Import/ModelSignature.h" +#include "moco/Import/GraphBuilderRegistry.h" + +#include <moco/Names.h> + +#include <loco.h> + +#include <tensorflow/core/framework/graph.pb.h> + +#include <memory> + +namespace moco +{ + +class Importer final +{ +public: + Importer(); + +public: + explicit Importer(const GraphBuilderSource *source) : _source{source} + { + // DO NOTHING + } + +public: + std::unique_ptr<loco::Graph> import(const ModelSignature &, tensorflow::GraphDef &) const; + +private: + const GraphBuilderSource *_source = nullptr; +}; + +} // namespace moco + +#endif // __MOCO_IMPORTER_H__ diff --git a/compiler/moco/import/src/Convert.cpp b/compiler/moco/import/src/Convert.cpp new file mode 100644 index 000000000..6285f5eab --- /dev/null +++ b/compiler/moco/import/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/import/src/Convert.h b/compiler/moco/import/src/Convert.h new file mode 100644 index 000000000..77dab3700 --- /dev/null +++ b/compiler/moco/import/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/import/src/GraphBuilderContext.cpp b/compiler/moco/import/src/GraphBuilderContext.cpp new file mode 100644 index 000000000..bbc1d8bd0 --- /dev/null +++ b/compiler/moco/import/src/GraphBuilderContext.cpp @@ -0,0 +1,80 @@ +/* + * 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/Import/GraphBuilderContext.h" + +#include <oops/UserExn.h> + +#include <stdexcept> +#include <string> + +namespace moco +{ + +void NodeDefTable::enroll(const std::string &node_name, const tensorflow::NodeDef *node) +{ + MapNameNode_t::iterator iter = _table.find(node_name); + + if (iter != _table.end()) + { + throw oops::UserExn("Duplicate node name in GraphDef", node_name); + } + + _table[node_name] = node; +} + +const tensorflow::NodeDef *NodeDefTable::node(const std::string &node_name) const +{ + MapNameNode_t::const_iterator iter = _table.find(node_name); + + if (iter == _table.end()) + { + throw oops::UserExn("Cannot find node with name in GraphDef", node_name); + } + + return iter->second; +} + +void SymbolTable::enroll(const TensorName &tensor_name, loco::Node *node) +{ + MapNameNode_t::iterator iter = _table.find(tensor_name); + + if (iter != _table.end()) + { + throw oops::UserExn("Duplicate node name in GraphDef", tensor_name.name()); + } + + _table[tensor_name] = node; +} + +loco::Node *SymbolTable::node(const TensorName &tensor_name) const +{ + MapNameNode_t::const_iterator iter = _table.find(tensor_name); + + if (iter == _table.end()) + { + throw oops::UserExn("Cannot find node with name in GraphDef", tensor_name.name()); + } + + return iter->second; +} + +void UpdateQueue::enroll(std::unique_ptr<GraphUpdate> &&update) +{ + _queue.push_back(std::move(update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/GraphBuilderContext.test.cpp b/compiler/moco/import/src/GraphBuilderContext.test.cpp new file mode 100644 index 000000000..51f6db245 --- /dev/null +++ b/compiler/moco/import/src/GraphBuilderContext.test.cpp @@ -0,0 +1,77 @@ +/* + * 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/Import/GraphBuilderContext.h" +#include <moco/Names.h> + +#include <loco.h> + +#include <oops/UserExn.h> + +#include <gtest/gtest.h> + +TEST(GraphBuilderContext, ctor) +{ + auto graph = loco::make_graph(); + moco::NodeDefTable nodedef; + moco::SymbolTable nodes; + moco::UpdateQueue updates; + + moco::GraphBuilderContext context(graph.get(), &nodedef, &nodes, &updates); + + ASSERT_EQ(context.graph(), graph.get()); + ASSERT_EQ(context.nodedef(), &nodedef); + ASSERT_EQ(context.tensor_names(), &nodes); + ASSERT_EQ(context.updates(), &updates); +} + +TEST(SymbolTable, node_name) +{ + moco::SymbolTable table; + loco::Pull pull_node; + moco::TensorName name("input", 0); + moco::TensorName invalid("invalid", 0); + + table.enroll(name, &pull_node); + ASSERT_EQ(table.node(name), &pull_node); + // duplicate name should throw + EXPECT_THROW(table.enroll(name, &pull_node), oops::UserExn); + // unregistered name should throw + EXPECT_THROW(table.node(invalid), oops::UserExn); +} + +namespace +{ + +class TestGraphUpdate final : public moco::GraphUpdate +{ +public: + void input(const moco::SymbolTable *) const override; +}; + +void TestGraphUpdate::input(const moco::SymbolTable *) const {} + +} // namespace + +TEST(GraphUpdateQueue, queue) +{ + std::unique_ptr<TestGraphUpdate> update(new TestGraphUpdate()); + moco::UpdateQueue updates; + + updates.enroll(std::move(update)); + auto &queue = updates.queue(); + ASSERT_EQ(queue.size(), 1); +} diff --git a/compiler/moco/import/src/GraphBuilderRegistry.cpp b/compiler/moco/import/src/GraphBuilderRegistry.cpp new file mode 100644 index 000000000..3a028513f --- /dev/null +++ b/compiler/moco/import/src/GraphBuilderRegistry.cpp @@ -0,0 +1,63 @@ +/* + * 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/Import/GraphBuilderRegistry.h" +#include "moco/Import/Nodes.h" + +#include <stdex/Memory.h> + +namespace moco +{ + +GraphBuilderRegistry::GraphBuilderRegistry() +{ + add("Add", stdex::make_unique<AddGraphBuilder>()); + add("AvgPool", stdex::make_unique<AvgPoolGraphBuilder>()); + add("BiasAdd", stdex::make_unique<BiasAddGraphBuilder>()); + add("ConcatV2", stdex::make_unique<ConcatV2GraphBuilder>()); + add("Const", stdex::make_unique<ConstGraphBuilder>()); + add("Conv2D", stdex::make_unique<Conv2DGraphBuilder>()); + add("Conv2DBackpropInput", stdex::make_unique<Conv2DBackpropInputGraphBuilder>()); + add("DepthwiseConv2dNative", stdex::make_unique<DepthwiseConv2dNativeGraphBuilder>()); + add("FakeQuantWithMinMaxVars", stdex::make_unique<FakeQuantWithMinMaxVarsGraphBuilder>()); + add("FusedBatchNorm", stdex::make_unique<FusedBatchNormGraphBuilder>()); + add("Identity", stdex::make_unique<IdentityGraphBuilder>()); + add("Maximum", stdex::make_unique<MaximumGraphBuilder>()); + add("MaxPool", stdex::make_unique<MaxPoolGraphBuilder>()); + add("Mean", stdex::make_unique<MeanGraphBuilder>()); + add("Mul", stdex::make_unique<MulGraphBuilder>()); + add("Pack", stdex::make_unique<PackGraphBuilder>()); + add("Pad", stdex::make_unique<PadGraphBuilder>()); + add("Placeholder", stdex::make_unique<PlaceholderGraphBuilder>()); + add("RealDiv", stdex::make_unique<RealDivGraphBuilder>()); + add("Relu", stdex::make_unique<ReluGraphBuilder>()); + add("Relu6", stdex::make_unique<Relu6GraphBuilder>()); + add("Reshape", stdex::make_unique<ReshapeGraphBuilder>()); + add("Rsqrt", stdex::make_unique<RsqrtGraphBuilder>()); + add("Shape", stdex::make_unique<ShapeGraphBuilder>()); + add("Softmax", stdex::make_unique<SoftmaxGraphBuilder>()); + add("Sqrt", stdex::make_unique<SqrtGraphBuilder>()); + add("SquaredDifference", stdex::make_unique<SquaredDifferenceGraphBuilder>()); + add("Squeeze", stdex::make_unique<SqueezeGraphBuilder>()); + add("StopGradient", stdex::make_unique<StopGradientGraphBuilder>()); + add("StridedSlice", stdex::make_unique<StridedSliceGraphBuilder>()); + add("Sub", stdex::make_unique<SubGraphBuilder>()); + add("Tanh", stdex::make_unique<TanhGraphBuilder>()); + + // Virtual node like `TFPush` need not to be added here +} + +} // namespace moco diff --git a/compiler/moco/import/src/Importer.cpp b/compiler/moco/import/src/Importer.cpp new file mode 100644 index 000000000..8d3ca6cfc --- /dev/null +++ b/compiler/moco/import/src/Importer.cpp @@ -0,0 +1,197 @@ +/* + * 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/Importer.h" +#include "moco/Import/GraphBuilder.h" +#include "moco/Import/GraphBuilderContext.h" + +#include "moco/Import/GraphBuilderRegistry.h" + +#include <moco/IR/Nodes/TFPlaceholder.h> +#include <moco/IR/TFNode.h> + +#include <stdex/Memory.h> +#include <oops/UserExn.h> + +#include <cassert> +#include <sstream> +#include <stdexcept> + +namespace +{ + +void convert_graph(const moco::GraphBuilderSource &source, const moco::ModelSignature &signature, + tensorflow::GraphDef &tf_graph_def, loco::Graph *graph) +{ + auto nodedef = stdex::make_unique<moco::NodeDefTable>(); + auto tensor_names = stdex::make_unique<moco::SymbolTable>(); + auto updates = stdex::make_unique<moco::UpdateQueue>(); + + moco::GraphBuilderContext gb_context(graph, nodedef.get(), tensor_names.get(), updates.get()); + + // Building a loco graph + // 1. Convert all the nodes to moco::TFNode + // 2. Connect inputs: set all node input(from a string) to actual node object + // 3. Set graph input + // 4. Create moco::TFPush node and set graph output + + /** + * @brief Prepare tensorflow::NodeDef search table from name + */ + for (const auto &n : tf_graph_def.node()) + { + nodedef->enroll(n.name(), &n); + } + + /** + * @brief 1. Convert all the nodes to moco::TFNode + * + * @note In each build for a TF node, four things happen + * 1) create corresponding moco::TFNode(s) + * 2) read and set the attributes to created moco::TFNode(s) + * 3) register name-moco::TFNode(last one of Nodes) that will be used as the output + * 4) queue a task to set the input of the moco::TFNode(first one of the Nodes) + * this is done only for required nodes depending on the operator + * + * @example Placeholder("in") - Identity("out") + * %1 = Placeholder --> 0x1001 (moco::TFNode* object address) + * (symboltable: register %1, after the registeration table will contain as below; + * "in" : 0x1001 + * ) + * (queue: this will be empty as Pull does not queue a task to set input; + * ) + * + * %2 = Forward --> 0x1002 + * (symboltable: register %2 and table will look like below; + * "in" : 0x1001 + * "out" : 0x1002 + * ) + * (queue: Forward will queue a task with input "in"; + * 0x1002: {"in"} + * ) + */ + for (const auto &n : tf_graph_def.node()) + { + if (const auto *graph_builder = source.lookup(n.op())) + { + if (!graph_builder->validate(n)) + { + throw oops::UserExn("Invalid operator", n.op()); + } + + graph_builder->build(n, &gb_context); + } + else + { + throw oops::UserExn("Not supported", n.op()); + } + } + + /** + * @brief 2. Connect inputs: Iterate updates and call each update input method + * + * @note Continue from above example graph, connecting inputs is done in following steps + * a) iterate queue + * b) call the input method for each update + * c) each update has the moco::TFNode *node and names of the input to connect + * node = 0x1002 and names = {"in"} + * d) from symbol table, "in" will return 0x1001 + * e) set input of 0x1002 with 0x1001 + */ + for (auto &update : updates->queue()) + { + update->input(tensor_names.get()); + } + + /** + * @brief 3. Set graph input + */ + for (auto input : signature.inputs()) + { + auto node = tensor_names->node(input); + assert(node != nullptr); + + auto graph_input = graph->inputs()->create(); + + auto placeholder_node = dynamic_cast<moco::TFPlaceholder *>(node); + assert(placeholder_node != nullptr); + + graph_input->name(input.nodeName()); + + // annotate index that should be passed to loco::Pull + moco::index(placeholder_node, graph_input->index()); + + // This implementation works as "PlaceholderGraphBuilder in Nodes/Placeholder.cpp" + // accepts only TF_FLOAT32 as of now. + // + // TODO Support other types + graph_input->dtype(loco::DataType::FLOAT32); + } + + /** + * @brief 4. Create moco::TFPush node and set graph output + */ + for (auto output : signature.outputs()) + { + auto output_node = tensor_names->node(output); + assert(output_node); + + // create moco::TFPush for output of graph + auto push_node = graph->nodes()->create<moco::TFPush>(); + push_node->from(output_node); // set input of TFPush to output node + + // set the graph output name and node object + auto graph_output = graph->outputs()->create(); + graph_output->name(output.nodeName()); + push_node->index(graph_output->index()); + + // TODO Support other types + graph_output->dtype(loco::DataType::FLOAT32); + } + + // validate graph + assert(loco::valid(graph)); +} + +} // namespace + +namespace moco +{ + +Importer::Importer() +{ + // DO NOTHING +} + +std::unique_ptr<loco::Graph> Importer::import(const ModelSignature &signature, + tensorflow::GraphDef &tf_graph_def) const +{ + auto graph = loco::make_graph(); + + const GraphBuilderSource *source_ptr = &moco::GraphBuilderRegistry::get(); + + if (_source != nullptr) + { + // Use user-defined GraphBuilderSource + source_ptr = _source; + } + + convert_graph(*source_ptr, signature, tf_graph_def, graph.get()); + + return std::move(graph); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Importer.test.cpp b/compiler/moco/import/src/Importer.test.cpp new file mode 100644 index 000000000..23873390c --- /dev/null +++ b/compiler/moco/import/src/Importer.test.cpp @@ -0,0 +1,223 @@ +/* + * 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/Importer.h" +#include "moco/GraphHelper.h" + +#include <moco/IR/Nodes/TFIdentity.h> + +#include "TestHelper.h" +#include <loco.h> +#include <plier/tf/TestHelper.h> + +#include <gtest/gtest.h> + +using namespace moco::test; + +TEST(TensorFlowImport, Dummy) { moco::Importer import; } + +namespace +{ + +// clang-format off +const char *basic_pbtxtdata = STRING_CONTENT( +node { + name: "Placeholder" + op: "Placeholder" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "shape" + value { + shape { + dim { + size: 1 + } + dim { + size: 2 + } + dim { + size: 1 + } + dim { + size: 2 + } + } + } + } +} +node { + name: "output/identity" + op: "Identity" + input: "Placeholder" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, load_model_withio_tf) +{ + moco::ModelSignature signature; + + signature.add_input(moco::TensorName("Placeholder", 0)); + signature.add_output(moco::TensorName("output/identity", 0)); + + tensorflow::GraphDef graph_def; + EXPECT_TRUE(plier::tf::parse_graphdef(basic_pbtxtdata, graph_def)); + + moco::Importer importer; + + std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def); + + // what to test: + // - import reads Placeholder + // - import reads Identity + // - attribute values should match + + auto tfidentity = find_first_node_bytype<moco::TFIdentity>(graph.get()); + ASSERT_NE(tfidentity, nullptr); + ASSERT_NE(tfidentity->input(), nullptr); +} + +namespace +{ + +// clang-format off +const char *query_pbtxtdata = STRING_CONTENT( +node { + name: "Placeholder" + op: "Placeholder" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "shape" + value { + shape { + dim { + size: 1 + } + dim { + size: 2 + } + dim { + size: 1 + } + dim { + size: 2 + } + } + } + } +} +node { + name: "Foo/w_min" + op: "Const" + attr { + key: "dtype" + value { type: DT_FLOAT } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { } + float_val: -1.0 + } + } + } +} +node { + name: "output/identity" + op: "Identity" + input: "Placeholder" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +node { + name: "Foo/w_max" + op: "Const" + attr { + key: "dtype" + value { type: DT_FLOAT } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { } + float_val: -1.0 + } + } + } +} +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, find_node_by_name) +{ + moco::ModelSignature signature; + + signature.add_input(moco::TensorName("Placeholder", 0)); + signature.add_output(moco::TensorName("output/identity", 0)); + + tensorflow::GraphDef graph_def; + EXPECT_TRUE(plier::tf::parse_graphdef(query_pbtxtdata, graph_def)); + + moco::Importer importer; + + std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def); + + // what to test: + // - get name of first Identity node + // - find node by name `Foo/w_min` + // - find node by name `Foo/w_max` + + auto tfidentity = find_first_node_bytype<moco::TFIdentity>(graph.get()); + ASSERT_NE(tfidentity, nullptr); + ASSERT_NE(tfidentity->input(), nullptr); + ASSERT_STREQ(tfidentity->name().c_str(), "output/identity"); + + auto query_node = moco::find_node_byname<moco::TFConst>(graph.get(), "Foo/w_min"); + ASSERT_NE(query_node, nullptr); + ASSERT_STREQ(query_node->name().c_str(), "Foo/w_min"); + + auto query_node2 = moco::find_node_byname<moco::TFConst>(graph.get(), "Foo/w_max"); + ASSERT_NE(query_node2, nullptr); + ASSERT_STREQ(query_node2->name().c_str(), "Foo/w_max"); +} diff --git a/compiler/moco/import/src/ModelSignature.cpp b/compiler/moco/import/src/ModelSignature.cpp new file mode 100644 index 000000000..d4c7e5085 --- /dev/null +++ b/compiler/moco/import/src/ModelSignature.cpp @@ -0,0 +1,66 @@ +/* + * 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/Import/ModelSignature.h" + +#include <oops/UserExn.h> + +namespace moco +{ + +void ModelSignature::add_customop(const std::string &op) +{ + if (std::find(_customops.begin(), _customops.end(), op) == _customops.end()) + _customops.emplace_back(op); + else + throw oops::UserExn("Duplicate custom operator", op); +} + +void ModelSignature::shape(const std::string &node_name, const angkor::TensorShape &shape) +{ + if (_shapes.find(node_name) != _shapes.end()) + throw oops::UserExn("Duplicate node name", node_name); + + _shapes[node_name] = shape; +} + +const angkor::TensorShape *ModelSignature::shape(const std::string &node_name) const +{ + auto res = _shapes.find(node_name); + if (res == _shapes.end()) + return nullptr; + else + return &res->second; +} + +void ModelSignature::dtype(const std::string &node_name, loco::DataType dtype) +{ + if (_dtypes.find(node_name) != _dtypes.end()) + throw oops::UserExn("Duplicate node name", node_name); + + _dtypes[node_name] = dtype; +} + +loco::DataType ModelSignature::dtype(const std::string &node_name) const +{ + auto res = _dtypes.find(node_name); + if (res == _dtypes.end()) + return loco::DataType::Unknown; + else + return res->second; +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Add.cpp b/compiler/moco/import/src/Nodes/Add.cpp new file mode 100644 index 000000000..6981a55e1 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Add.cpp @@ -0,0 +1,85 @@ +/* + * 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/Import/Nodes/Add.h" + +#include <moco/IR/Nodes/TFAdd.h> + +#include <loco.h> +#include <stdex/Memory.h> + +namespace +{ + +using namespace moco; + +/** + * @brief GraphUpdate for TF Add node + */ +class TFAddGraphUpdate final : public GraphUpdate +{ +public: + TFAddGraphUpdate(TFAdd *node, std::vector<TensorName> names) : _node(node), _names(names) {} + + void input(const SymbolTable *) const override; + +private: + TFAdd *_node; + std::vector<TensorName> _names; +}; + +void TFAddGraphUpdate::input(const SymbolTable *tensor_names) const +{ + assert(_names.size() == 2); + + _node->x(tensor_names->node(_names[0])); + _node->y(tensor_names->node(_names[1])); +} + +} // namespace + +namespace moco +{ + +bool AddGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + return node.input_size() == 2; +} + +void AddGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // creating TF dialect Add node + auto tf_add = graph->nodes()->create<TFAdd>(); + tf_add->name(node.name()); + + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, tf_add); + + std::vector<TensorName> add_input_names; + add_input_names.push_back(TensorName(node.input(0))); // x + add_input_names.push_back(TensorName(node.input(1))); // y + + auto tf_add_update = stdex::make_unique<TFAddGraphUpdate>(tf_add, add_input_names); + updates->enroll(std::move(tf_add_update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Add.test.cpp b/compiler/moco/import/src/Nodes/Add.test.cpp new file mode 100644 index 000000000..ace2b0801 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Add.test.cpp @@ -0,0 +1,58 @@ +/* + * 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/Import/Nodes/Add.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ +// clang-format off +const char *add_basic_pbtxt = STRING_CONTENT( + name: "ADD_01" + op: "Add" + input: "input_01" + input: "input_02" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, tf_add_basic) +{ + TFNodeBuildTester tester; + moco::AddGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(add_basic_pbtxt, nodedef)); + + // what to test: + // - TFAdd node should exist + // - both inputs x() and y() should not be null + + tester.inputs({"input_01", "input_02"}); + tester.output("ADD_01"); + tester.run(nodedef, graphbuilder); +} diff --git a/compiler/moco/import/src/Nodes/AvgPool.cpp b/compiler/moco/import/src/Nodes/AvgPool.cpp new file mode 100644 index 000000000..6d7fd36bb --- /dev/null +++ b/compiler/moco/import/src/Nodes/AvgPool.cpp @@ -0,0 +1,140 @@ +/* + * 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/Import/Nodes/AvgPool.h" + +#include <moco/IR/Nodes/TFAvgPool.h> + +#include <moco/Names.h> + +#include "Convert.h" +#include <loco/IR/PermutingCodec.h> +#include <stdex/Memory.h> +#include <plier/tf/Convert.h> +#include <oops/UserExn.h> + +#include <cassert> +#include <stdexcept> + +using namespace plier::tf; + +namespace +{ + +using namespace moco; + +class TFAvgPoolGraphUpdate final : public GraphUpdate +{ +public: + TFAvgPoolGraphUpdate(TFAvgPool *node, const TensorName &name) + : _avgpool_node(node), _value_name(name) + { + } + + void input(const SymbolTable *) const override; + +private: + TFAvgPool *_avgpool_node; + const TensorName _value_name; +}; + +void TFAvgPoolGraphUpdate::input(const SymbolTable *node_table) const +{ + loco::Node *value_node = node_table->node(_value_name); + _avgpool_node->value(value_node); +} + +} // namespace + +namespace moco +{ + +bool AvgPoolGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + if (node.input_size() != 1) + return false; + + // note: even though "data_format" is not entered when a model is written, + // TF seems to generate "data_format" field into a pb file + if (!plier::tf::has_attrs(node, {"T", "data_format", "ksize", "padding", "strides"})) + return false; + + auto tf_ksize = get_list_attr(node, "ksize"); + auto ksize = as_int64_list(tf_ksize); + if (ksize.size() != 4) + { + // TODO support ksize length for 1 and 2 + throw oops::UserExn("AvgPool only supports ksize length 4", node.name()); + } + + auto tf_strides = get_list_attr(node, "strides"); + auto strides = as_int64_list(tf_strides); + if (strides.size() != 4) + { + // TODO support strides length for 1 and 2 + throw oops::UserExn("AvgPool only supports strides length 4", node.name()); + } + + return true; +} + +void AvgPoolGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // name of loco nodes + ::std::string avgPool2d_name = node.name(); + + // tensorflow data_format: one of NHWC or NCHW. + auto data_layout = get_string_attr(node, "data_format"); + auto avgPool_node = graph->nodes()->create<TFAvgPool>(); + avgPool_node->name(node.name()); + avgPool_node->data_layout(data_layout); + + // padding + auto padding = moco::str_toupper(get_string_attr(node, "padding")); + avgPool_node->padding(padding); + + // ksize + auto tf_ksize = get_list_attr(node, "ksize"); + auto ksize = as_int64_list(tf_ksize); + avgPool_node->ksize(ksize); + + // strides + auto tf_strides = get_list_attr(node, "strides"); + auto strides = as_int64_list(tf_strides); + avgPool_node->strides(strides); + + // To set the input node of encode_node with avgPool2d_name + TensorName output_name(avgPool2d_name, 0); + tensor_names->enroll(output_name, avgPool_node); + + // Record ifm inputs to featureEncode_node + auto update = stdex::make_unique<TFAvgPoolGraphUpdate>(avgPool_node, TensorName(node.input(0))); + + updates->enroll(std::move(update)); +} + +} // namespace moco + +// TODO Consider a case when TF AvgPool is for 3D. +// AvgPool works for 2D and other Dimensions, such as 3D +// So, in future, some other GraphBuilder decide if AvgPoolGraphBuilder is used or +// other GraphBuilder is used for TF AvgPool diff --git a/compiler/moco/import/src/Nodes/AvgPool.test.cpp b/compiler/moco/import/src/Nodes/AvgPool.test.cpp new file mode 100644 index 000000000..7d62f0eaa --- /dev/null +++ b/compiler/moco/import/src/Nodes/AvgPool.test.cpp @@ -0,0 +1,99 @@ +/* + * 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/Import/Nodes/AvgPool.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ +// clang-format off +const char *avgpool_01_pbtxtdata = STRING_CONTENT( + name: "avgpool" + op: "AvgPool" + input: "const/float" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "data_format" + value { + s: "NHWC" + } + } + attr { + key: "ksize" + value { + list { + i: 1 + i: 2 + i: 3 + i: 1 + } + } + } + attr { + key: "padding" + value { + s: "VALID" + } + } + attr { + key: "strides" + value { + list { + i: 1 + i: 3 + i: 2 + i: 1 + } + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, AvgPool_01) +{ + TFNodeBuildTester tester; + moco::AvgPoolGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(avgpool_01_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFAvgPool + // - input should exist + // - attributes value should match + + tester.inputs({"const/float"}); + tester.output("avgpool"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFAvgPool *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->data_layout(), "NHWC"); + ASSERT_EQ(test_node->padding(), "VALID"); + ASSERT_EQ(test_node->ksize(), std::vector<int64_t>({1, 2, 3, 1})); + ASSERT_EQ(test_node->strides(), std::vector<int64_t>({1, 3, 2, 1})); +} diff --git a/compiler/moco/import/src/Nodes/BiasAdd.cpp b/compiler/moco/import/src/Nodes/BiasAdd.cpp new file mode 100644 index 000000000..a3eb91116 --- /dev/null +++ b/compiler/moco/import/src/Nodes/BiasAdd.cpp @@ -0,0 +1,122 @@ +/* + * 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/Import/Nodes/BiasAdd.h" + +#include <moco/IR/Nodes/TFBiasAdd.h> + +#include <moco/Names.h> + +#include <loco.h> +#include <loco/IR/PermutingCodec.h> +#include <stdex/Memory.h> +#include <plier/tf/Convert.h> +#include <oops/UserExn.h> + +#include <cassert> +#include <vector> + +namespace +{ +using namespace moco; + +class TFBiasAddGraphUpdate final : public GraphUpdate +{ +public: + TFBiasAddGraphUpdate(TFBiasAdd *biasadd, std::vector<TensorName> &names) + : _biasadd(biasadd), _names(names) + { + } + + void input(const SymbolTable *) const override; + +private: + TFBiasAdd *_biasadd; + std::vector<TensorName> _names; +}; + +void TFBiasAddGraphUpdate::input(const SymbolTable *node_table) const +{ + assert(_names.size() == 2); + + auto value_node = node_table->node(_names[0]); + auto bias_node = node_table->node(_names[1]); + assert(value_node != nullptr); + assert(bias_node != nullptr); + + _biasadd->value(value_node); + _biasadd->bias(bias_node); +} + +} // namespace + +namespace moco +{ + +bool BiasAddGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + if (node.input_size() != 2) + return false; + + // note: even though "data_format" is not entered when a model is written, + // TF seems to generate "data_format" field into a pb file + if (!plier::tf::has_attrs(node, {"T", "data_format"})) + return false; + + // TODO add type check + // type of input and bias should be same (except using quantization) + + // Note In case of TF.nn.bias_add, + // "value may have any number of dimensions." ... + // but "data_format: A string. 'NHWC' and 'NCHW' are supported." + // Not sure if value should be 4-D tensor. Let's skip this check for now. + + auto data_layout = plier::tf::get_string_attr(node, "data_format"); + if (!(data_layout == "NHWC" || data_layout == "NCHW")) + { + throw oops::UserExn("BiasAdd Unsupported data_format", node.name()); + } + + return true; +} + +void BiasAddGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // tensorflow data_format: one of NHWC or NCHW. + auto data_layout = plier::tf::get_string_attr(node, "data_format"); + auto tf_bias_add = graph->nodes()->create<TFBiasAdd>(); + tf_bias_add->name(node.name()); + tf_bias_add->data_layout(data_layout); + + // To set the input node of encode_node with biasAdd_name + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, tf_bias_add); + + std::vector<TensorName> input_names; + input_names.push_back(TensorName(node.input(0))); + input_names.push_back(TensorName(node.input(1))); + + auto update = stdex::make_unique<TFBiasAddGraphUpdate>(tf_bias_add, input_names); + updates->enroll(std::move(update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/BiasAdd.test.cpp b/compiler/moco/import/src/Nodes/BiasAdd.test.cpp new file mode 100644 index 000000000..626456d30 --- /dev/null +++ b/compiler/moco/import/src/Nodes/BiasAdd.test.cpp @@ -0,0 +1,112 @@ +/* + * 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/Import/Nodes/BiasAdd.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ + +// clang-format off +const char *bias_add_01_pbtxtdata = STRING_CONTENT( + name: "out" + op: "BiasAdd" + input: "val" + input: "bias" + attr { + key: "T" + value { type: DT_FLOAT } + } + attr { + key: "data_format" + value { s: "NHWC" } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, bias_add_01) +{ + TFNodeBuildTester tester; + moco::BiasAddGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(bias_add_01_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFBiasAdd + // - value() should not be nullptr + // - bias() should not be nullptr + // - data_layout should match + + tester.inputs({"val", "bias"}); + tester.output("out"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFBiasAdd *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_TRUE(test_node->data_layout() == "NHWC"); +} + +namespace +{ + +// clang-format off +const char *bias_add_NCHW_pbtxtdata = STRING_CONTENT( + name: "out" + op: "BiasAdd" + input: "val" + input: "bias" + attr { + key: "T" + value { type: DT_FLOAT } + } + attr { + key: "data_format" + value { s: "NCHW" } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, bias_add_NCHW_axis) +{ + TFNodeBuildTester tester; + moco::BiasAddGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(bias_add_NCHW_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFBiasAdd + // - value() should not be nullptr + // - bias() should not be nullptr + // - data_layout should match + + tester.inputs({"val", "bias"}); + tester.output("out"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFBiasAdd *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_TRUE(test_node->data_layout() == "NCHW"); +} diff --git a/compiler/moco/import/src/Nodes/Concat.cpp b/compiler/moco/import/src/Nodes/Concat.cpp new file mode 100644 index 000000000..8bf8a84b5 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Concat.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 "moco/Import/Nodes/Concat.h" + +#include <moco/IR/Nodes/TFConcatV2.h> + +#include <moco/Names.h> + +#include <loco.h> +#include <stdex/Memory.h> +#include <plier/tf/Convert.h> + +#include <cassert> + +namespace +{ + +using namespace moco; + +class TFConcatV2GraphUpdate final : public GraphUpdate +{ +public: + TFConcatV2GraphUpdate(TFConcatV2 *node, std::vector<TensorName> names) + : _node(node), _names(names) + { + } + + void input(const SymbolTable *) const override; + +private: + TFConcatV2 *_node; + std::vector<TensorName> _names; +}; + +void TFConcatV2GraphUpdate::input(const SymbolTable *tensor_names) const +{ + uint32_t num_values = _names.size() - 1; // exclude axis + assert(num_values >= 1); + + for (uint32_t i = 0; i < num_values; ++i) + { + auto input_node = tensor_names->node(_names[i]); + assert(input_node != nullptr); + _node->values(i, input_node); + } + auto axis_node = tensor_names->node(_names[num_values]); + assert(axis_node != nullptr); + _node->axis(axis_node); +} + +} // namespace + +namespace moco +{ + +bool ConcatV2GraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + if (!plier::tf::has_attrs(node, {"T", "N", "Tidx"})) + return false; + + // Concat node SHOULD have 3 or more inputs, that is 2 + axis + const int num_inputs = node.input_size() - 1; + return (num_inputs >= 2) && (num_inputs == plier::tf::get_int_attr(node, "N")); +} + +void ConcatV2GraphBuilder::build(const tensorflow::NodeDef &node, + GraphBuilderContext *context) const +{ + assert(context != nullptr); + + auto graph = context->graph(); + auto tensor_names = context->tensor_names(); + auto updates = context->updates(); + + const int num_inputs = node.input_size() - 1; + std::vector<TensorName> input_names; + auto concat_node = graph->nodes()->create<TFConcatV2>(num_inputs); + concat_node->name(node.name()); + + for (int ni = 0; ni < num_inputs; ++ni) + { + input_names.push_back(TensorName(node.input(ni))); + } + // last one is the axis + input_names.push_back(TensorName(node.input(num_inputs))); + + // register string-name to the last node as output of concat(s) + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, concat_node); + + auto update = stdex::make_unique<TFConcatV2GraphUpdate>(concat_node, input_names); + updates->enroll(std::move(update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Concat.test.cpp b/compiler/moco/import/src/Nodes/Concat.test.cpp new file mode 100644 index 000000000..c0986578b --- /dev/null +++ b/compiler/moco/import/src/Nodes/Concat.test.cpp @@ -0,0 +1,134 @@ +/* + * 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/Import/Nodes/Concat.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ + +// clang-format off +const char *concat_01_pbtxtdata = STRING_CONTENT( + name: "Concat" + op: "ConcatV2" + input: "Input01" + input: "Input02" + input: "Axis" + attr { + key: "N" + value { + i: 2 + } + } + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "Tidx" + value { + type: DT_INT32 + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, concat_01) +{ + TFNodeBuildTester tester; + moco::ConcatV2GraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(concat_01_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFConcatV2 + // - there should be two values + // - values(idx) should not be nullptr + // - axis() should not be nullptr + + tester.inputs({"Input01", "Input02", "Axis"}); + tester.output("Concat"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFConcatV2 *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->num_values(), 2); +} + +namespace +{ + +// clang-format off +const char *concat_02_pbtxtdata = STRING_CONTENT( + name: "Concat" + op: "ConcatV2" + input: "Input01" + input: "Input02" + input: "Input03" + input: "Axis" + attr { + key: "N" + value { + i: 3 + } + } + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "Tidx" + value { + type: DT_INT32 + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, concat_02) +{ + TFNodeBuildTester tester; + moco::ConcatV2GraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(concat_02_pbtxtdata, nodedef)); + + // what to test: TFConcatV2 has 3 inputs + // - there should exist TFConcatV2 + // - values(idx) should not be nullptr + // - axis() should not be nullptr + + tester.inputs({"Input01", "Input02", "Input03", "Axis"}); + tester.output("Concat"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFConcatV2 *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->num_values(), 3); +} diff --git a/compiler/moco/import/src/Nodes/Const.cpp b/compiler/moco/import/src/Nodes/Const.cpp new file mode 100644 index 000000000..15ea717db --- /dev/null +++ b/compiler/moco/import/src/Nodes/Const.cpp @@ -0,0 +1,242 @@ +/* + * 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/Import/Nodes/Const.h" + +#include <moco/Names.h> +#include <moco/IR/TFNodes.h> + +#include <loco.h> +#include <plier/tf/Convert.h> +#include <oops/UserExn.h> + +#include <cassert> +#include <stdexcept> +#include <string> + +namespace +{ + +using namespace moco; + +void read_value_int8(TFConst *const_node, int num_elements, + const tensorflow::TensorProto &input_tensor) +{ + const_node->size<loco::DataType::S8>(num_elements); + + int32_t input_elements = input_tensor.int_val_size(); + + if (input_tensor.tensor_content().size() == num_elements * sizeof(int8_t)) + { + const std::string &str_content = input_tensor.tensor_content(); + const int8_t *s8_ptr = reinterpret_cast<const int8_t *>(str_content.c_str()); + for (int32_t i = 0; i < num_elements; i++) + { + const_node->at<loco::DataType::S8>(i) = *(s8_ptr + i); + } + } + else if (0 < input_elements && input_elements <= num_elements) + { + for (int32_t i = 0; i < input_elements; i++) + { + const_node->at<loco::DataType::S8>(i) = input_tensor.int_val(i); + } + + for (int32_t i = input_elements; i < num_elements; i++) + { + const_node->at<loco::DataType::S8>(i) = input_tensor.int_val(input_elements - 1); + } + } + else + { + throw oops::UserExn("Invalid Const values", const_node->name()); + } +} + +void read_value_int32(TFConst *const_node, int num_elements, + const tensorflow::TensorProto &input_tensor) +{ + const_node->size<loco::DataType::S32>(num_elements); + + int32_t input_elements = input_tensor.int_val_size(); + + if (input_tensor.tensor_content().size() == num_elements * sizeof(int32_t)) + { + const std::string &str_content = input_tensor.tensor_content(); + const int32_t *s32_ptr = reinterpret_cast<const int32_t *>(str_content.c_str()); + for (int32_t i = 0; i < num_elements; i++) + { + const_node->at<loco::DataType::S32>(i) = *(s32_ptr + i); + } + } + else if (0 < input_elements && input_elements <= num_elements) + { + for (int32_t i = 0; i < input_elements; i++) + { + const_node->at<loco::DataType::S32>(i) = input_tensor.int_val(i); + } + + for (int32_t i = input_elements; i < num_elements; i++) + { + const_node->at<loco::DataType::S32>(i) = input_tensor.int_val(input_elements - 1); + } + } + else + { + throw oops::UserExn("Invalid Const values", const_node->name()); + } +} + +void read_value_float32(TFConst *const_node, int num_elements, + const tensorflow::TensorProto &input_tensor) +{ + const_node->size<loco::DataType::FLOAT32>(num_elements); + + int32_t input_elements = input_tensor.float_val_size(); + + if (input_tensor.tensor_content().size() == num_elements * sizeof(float)) + { + const std::string &str_content = input_tensor.tensor_content(); + const float *float_ptr = reinterpret_cast<const float *>(str_content.c_str()); + for (int32_t i = 0; i < num_elements; i++) + { + const_node->at<loco::DataType::FLOAT32>(i) = *(float_ptr + i); + } + } + else if (0 < input_elements && input_elements <= num_elements) + { + for (int32_t i = 0; i < input_elements; i++) + { + const_node->at<loco::DataType::FLOAT32>(i) = input_tensor.float_val(i); + } + + for (int32_t i = input_elements; i < num_elements; i++) + { + const_node->at<loco::DataType::FLOAT32>(i) = input_tensor.float_val(input_elements - 1); + } + } + else + { + throw oops::UserExn("Invalid Const values", const_node->name()); + } +} + +} // namespace + +namespace moco +{ + +bool ConstGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + if (!plier::tf::has_attrs(node, {"dtype", "value"})) + return false; + + const auto &input_tensor = plier::tf::get_tensor_attr(node, "value"); + const auto &input_shape = input_tensor.tensor_shape(); + const auto &input_dims = input_shape.dim(); + + if (!(input_shape.dim_size() <= 6)) + return false; + + for (auto &d : input_dims) + { + if (d.size() > std::numeric_limits<int>::max()) + throw oops::UserExn("Const Shape element overflows", node.name()); + + if (d.size() < 0) + throw oops::UserExn("Unknown dim size", node.name()); + } + + auto dtype = plier::tf::as_loco_datatype(plier::tf::get_datatype_attr(node, "dtype")); + if (!(dtype == loco::DataType::S32 || dtype == loco::DataType::FLOAT32 || + dtype == loco::DataType::S8)) + return false; + // TODO support other dtype + + return true; +} + +void ConstGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + + // Create a "TFConstant" node for Const + auto const_node = graph->nodes()->create<TFConst>(); + const_node->name(node.name()); + + // set dtype + auto dtype = plier::tf::as_loco_datatype(plier::tf::get_datatype_attr(node, "dtype")); + const_node->dtype(dtype); + + // import shape and value + const auto &input_tensor = plier::tf::get_tensor_attr(node, "value"); + const auto &input_shape = input_tensor.tensor_shape(); + const auto &input_dims = input_shape.dim(); + assert(input_shape.dim_size() <= 6); + const_node->rank(input_shape.dim_size()); + int index = 0; + bool zero_sized_shape = false; + for (auto &d : input_dims) + { + assert(d.size() <= std::numeric_limits<int>::max()); + if (d.size() == 0) + zero_sized_shape = true; + + assert(d.size() >= 0); + const_node->dim(index++) = d.size(); + } + + int num_elements = 1; + if (zero_sized_shape) + { + const_node->rank(0); + num_elements = 0; + } + else + { + for (uint32_t d = 0; d < const_node->rank(); d++) + { + num_elements *= const_node->dim(d).value(); + } + } + + switch (dtype) + { + case loco::DataType::S8: + read_value_int8(const_node, num_elements, input_tensor); + break; + + case loco::DataType::S32: + read_value_int32(const_node, num_elements, input_tensor); + break; + + case loco::DataType::FLOAT32: + read_value_float32(const_node, num_elements, input_tensor); + break; + + // TODO support other types + + default: + assert(false); + } + + // register string-name to node + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, const_node); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Const.test.cpp b/compiler/moco/import/src/Nodes/Const.test.cpp new file mode 100644 index 000000000..854499fe6 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Const.test.cpp @@ -0,0 +1,465 @@ +/* + * 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/Import/Nodes/Const.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ + +// Test case for "input_tensor.float_val_size() == num_elements" + +// clang-format off +const char *const_float_01_pbtxtdata = STRING_CONTENT( + name: "const/float" + op: "Const" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { + dim { + size: 2 + } + dim { + size: 3 + } + } + float_val: 1.1 + float_val: 2.2 + float_val: 3.3 + float_val: 4.4 + float_val: 5.5 + float_val: 6.6 + } + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, const_float_01) +{ + TFNodeBuildTester tester; + moco::ConstGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(const_float_01_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFConst + // - values should match + + tester.inputs({}); + tester.output("const/float"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFConst *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->size<loco::DataType::FLOAT32>(), 6); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(0), 1.1f); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(1), 2.2f); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(2), 3.3f); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(3), 4.4f); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(4), 5.5f); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(5), 6.6f); +} + +namespace +{ +// Test case for "input_tensor.float_val_size() == 1" + +// clang-format off +const char *const_float_02_pbtxtdata = STRING_CONTENT( + name: "const/float" + op: "Const" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { + dim { + size: 2 + } + dim { + size: 3 + } + } + float_val: 1.1 + } + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, const_float_02) +{ + TFNodeBuildTester tester; + moco::ConstGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(const_float_02_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFConst + // - values should match + + tester.inputs({}); + tester.output("const/float"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFConst *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->size<loco::DataType::FLOAT32>(), 6); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(0), 1.1f); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(1), 1.1f); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(2), 1.1f); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(3), 1.1f); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(4), 1.1f); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(5), 1.1f); +} + +namespace +{ +// Test case for "input_tensor.tensor_content().size() == num_elements * sizeof(float)" +// Generated with tfkit tool: "cat ./test.pbtxt | ./tfkit pack" + +// clang-format off +const char *const_float_03_pbtxtdata = STRING_CONTENT( + name: "const/float" + op: "Const" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { + dim { + size: 2 + } + dim { + size: 3 + } + } + tensor_content: "\315\314\214?\315\314\014@33S@\315\314\214@\000\000\260@33\323@" + } + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, const_float_03) +{ + TFNodeBuildTester tester; + moco::ConstGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(const_float_03_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFConst + // - values should match + + tester.inputs({}); + tester.output("const/float"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFConst *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->size<loco::DataType::FLOAT32>(), 6); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(0), 1.1f); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(1), 2.2f); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(2), 3.3f); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(3), 4.4f); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(4), 5.5f); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(5), 6.6f); +} + +namespace +{ +// Test case for "input_tensor.float_val_size() < num_elements" + +// clang-format off +const char *const_float_04_pbtxtdata = STRING_CONTENT( + name: "const/float" + op: "Const" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { + dim { + size: 2 + } + dim { + size: 3 + } + } + float_val: 1.1 + float_val: 2.2 + } + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, const_float_04) +{ + TFNodeBuildTester tester; + moco::ConstGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(const_float_04_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFConst + // - values should match + + tester.inputs({}); + tester.output("const/float"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFConst *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->size<loco::DataType::FLOAT32>(), 6); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(0), 1.1f); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(1), 2.2f); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(2), 2.2f); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(3), 2.2f); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(4), 2.2f); + ASSERT_EQ(test_node->at<loco::DataType::FLOAT32>(5), 2.2f); +} + +namespace +{ +// Test case for "input_tensor.int_val_size() < num_elements" + +// clang-format off +const char *const_int32_04_pbtxtdata = STRING_CONTENT( + name: "const/int" + op: "Const" + attr { + key: "dtype" + value { + type: DT_INT32 + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_INT32 + tensor_shape { + dim { + size: 2 + } + dim { + size: 3 + } + } + int_val: 1 + int_val: 2 + } + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, const_int32_04) +{ + TFNodeBuildTester tester; + moco::ConstGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(const_int32_04_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFConst + // - values should match + + tester.inputs({}); + tester.output("const/int"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFConst *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->size<loco::DataType::S32>(), 6); + ASSERT_EQ(test_node->at<loco::DataType::S32>(0), 1); + ASSERT_EQ(test_node->at<loco::DataType::S32>(1), 2); + ASSERT_EQ(test_node->at<loco::DataType::S32>(2), 2); + ASSERT_EQ(test_node->at<loco::DataType::S32>(3), 2); + ASSERT_EQ(test_node->at<loco::DataType::S32>(4), 2); + ASSERT_EQ(test_node->at<loco::DataType::S32>(5), 2); +} + +namespace +{ +// Test case for "scalar" + +// clang-format off +const char *const_int32_scalar_pbtxtdata = STRING_CONTENT( + name: "const/int" + op: "Const" + attr { + key: "dtype" + value { + type: DT_INT32 + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_INT32 + tensor_shape { + } + int_val: 3 + } + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, const_int32_scalar) +{ + TFNodeBuildTester tester; + moco::ConstGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(const_int32_scalar_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFConst + // - there should be one element and value should be 3 + + tester.inputs({}); + tester.output("const/int"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFConst *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->size<loco::DataType::S32>(), 1); + ASSERT_EQ(test_node->at<loco::DataType::S32>(0), 3); +} + +namespace +{ + +// clang-format off +const char *const_int8_01_pbtxtdata = STRING_CONTENT( + name: "const/int8" + op: "Const" + attr { + key: "dtype" + value { + type: DT_INT8 + } + } + attr { + key: "value" + value { + tensor { + dtype: DT_INT8 + tensor_shape { + dim { + size: 2 + } + dim { + size: 3 + } + } + int_val: 0 + int_val: -1 + int_val: 1 + int_val: 2 + int_val: 3 + int_val: 4 + } + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, const_int8_01) +{ + TFNodeBuildTester tester; + moco::ConstGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(const_int8_01_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFConst + // - values should match + + tester.inputs({}); + tester.output("const/int8"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFConst *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->size<loco::DataType::S8>(), 6); + ASSERT_EQ(test_node->at<loco::DataType::S8>(0), 0); + ASSERT_EQ(test_node->at<loco::DataType::S8>(1), -1); + ASSERT_EQ(test_node->at<loco::DataType::S8>(2), 1); + ASSERT_EQ(test_node->at<loco::DataType::S8>(3), 2); + ASSERT_EQ(test_node->at<loco::DataType::S8>(4), 3); + ASSERT_EQ(test_node->at<loco::DataType::S8>(5), 4); +} diff --git a/compiler/moco/import/src/Nodes/Conv2D.cpp b/compiler/moco/import/src/Nodes/Conv2D.cpp new file mode 100644 index 000000000..e6b98dcd1 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Conv2D.cpp @@ -0,0 +1,139 @@ +/* + * 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/Import/Nodes/Conv2D.h" + +#include <moco/IR/Nodes/TFConv2D.h> + +#include <moco/Names.h> + +#include "Convert.h" + +#include <loco.h> +#include <loco/IR/PermutingCodec.h> +#include <stdex/Memory.h> +#include <plier/tf/Convert.h> +#include <oops/UserExn.h> + +#include <cassert> +#include <stdexcept> +#include <algorithm> + +namespace +{ +using namespace moco; + +class TFConv2DGraphUpdate final : public GraphUpdate +{ +public: + TFConv2DGraphUpdate(TFConv2D *node, std::vector<TensorName> names) : _node(node), _names(names) {} + + void input(const SymbolTable *) const override; + +private: + TFConv2D *_node; + std::vector<TensorName> _names; +}; + +void TFConv2DGraphUpdate::input(const SymbolTable *node_table) const +{ + assert(_names.size() == 2); + + auto input_node = node_table->node(_names[0]); + auto filter_node = node_table->node(_names[1]); + assert(input_node != nullptr); + assert(filter_node != nullptr); + + _node->input(input_node); + _node->filter(filter_node); +} + +} // namespace + +namespace moco +{ + +bool Conv2DGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + if (node.input_size() != 2) + return false; + + // note: even though "data_format" is not entered when a model is written, + // TF seems to generate "data_format" field into a pb file + if (!plier::tf::has_attrs(node, {"T", "data_format", "padding", "strides"})) + return false; + + auto data_layout = plier::tf::get_string_attr(node, "data_format"); + if (!(data_layout == "NHWC" || data_layout == "NCHW")) + { + throw oops::UserExn("Conv2D Unsupported data_format", node.name()); + } + + // dilation attribute is not fully supported + if (plier::tf::has_attr(node, "dilations")) + { + // TODO Support non-default dilations + auto dilation = plier::tf::get_list_attr(node, "dilations").i(); + if (!std::all_of(dilation.begin(), dilation.end(), [](std::int64_t dil) { return dil == 1; })) + return false; + } + // Else, dilations are automatically set to default [1,1,1,1] which we assumes now + + return true; +} + +void Conv2DGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // name of loco nodes + std::string conv2d_name = node.name(); + + auto conv2d = graph->nodes()->create<TFConv2D>(); + conv2d->name(node.name()); + + // read attributes + auto data_layout = plier::tf::get_string_attr(node, "data_format"); + assert(data_layout == "NHWC" || data_layout == "NCHW"); + conv2d->data_layout(data_layout); + + auto tf_strides = plier::tf::get_list_attr(node, "strides"); + auto strides = plier::tf::as_int64_list(tf_strides); + conv2d->strides(strides); + + auto padding = moco::str_toupper(plier::tf::get_string_attr(node, "padding")); + assert(padding == "VALID" || padding == "SAME"); + conv2d->padding(padding); + + // save the name for graph link updates + TensorName output_name(conv2d_name, 0); + tensor_names->enroll(output_name, conv2d); + + std::vector<TensorName> input_names; + input_names.push_back(TensorName(node.input(0))); // input + input_names.push_back(TensorName(node.input(1))); // kernel + + // Record ifm inputs to featureEncode_node + auto tfconv2d_update = stdex::make_unique<TFConv2DGraphUpdate>(conv2d, input_names); + + updates->enroll(std::move(tfconv2d_update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Conv2D.test.cpp b/compiler/moco/import/src/Nodes/Conv2D.test.cpp new file mode 100644 index 000000000..ba006f489 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Conv2D.test.cpp @@ -0,0 +1,119 @@ +/* + * 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/Import/Nodes/Conv2D.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ +// clang-format off +const char *conv2d_01_pbtxtdata = STRING_CONTENT( + name: "conv2d" + op: "Conv2D" + input: "ifm" + input: "ker" + attr { key: "T" value { type: DT_FLOAT } } + attr { key: "data_format" value { s: "NHWC" } } + attr { key: "dilations" value { list { i: 1 i: 1 i: 1 i: 1 } } } + attr { key: "padding" value { s: "VALID" } } + attr { key: "strides" value { list { i: 1 i: 2 i: 3 i: 1 } } } +); +// clang-format on +} // namespace + +TEST(TensorFlowImport, Conv2D_01) +{ + TFNodeBuildTester tester; + moco::Conv2DGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(conv2d_01_pbtxtdata, nodedef)); + + // what to test: + // - Conv2D node should exist + // - ifm() should not be nullptr + // - ker() should not be nullptr + // - attribute values should match + + tester.inputs({"ifm", "ker"}); + tester.output("conv2d"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFConv2D *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->padding(), "VALID"); + ASSERT_EQ(test_node->data_layout(), "NHWC"); + auto strides = test_node->strides(); + ASSERT_EQ(strides.size(), 4); + // TODO add verify dilation +} + +namespace +{ +// clang-format off +const char *conv2d_inception_pbtxtdata = STRING_CONTENT( + name: "InceptionV3/InceptionV3/Conv2d_1a_3x3/Conv2D" + op: "Conv2D" + input: "input:0" + input: "InceptionV3/Conv2d_1a_3x3/weights/read/_3__cf__3" + attr { + key: "T" + value { type: DT_FLOAT } + } + attr { + key: "data_format" + value { s: "NHWC" } + } + attr { + key: "dilations" + value { + list { i: 1 i: 1 i: 1 i: 1 } + } + } + attr { + key: "padding" + value { s: "VALID" } + } + attr { + key: "strides" + value { + list { i: 1 i: 2 i: 2 i: 1 } + } + } +); +} // namespace + +TEST(TensorFlowImport, Conv2D_inception_indexed_tensor_name) +{ + TFNodeBuildTester tester; + moco::Conv2DGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(conv2d_inception_pbtxtdata, nodedef)); + + // what to test: name with ':0' should be treated correctly + // - Conv2D node should exist + // - ifm() should not be nullptr + // - ker() should not be nullptr + + tester.inputs({"input", "InceptionV3/Conv2d_1a_3x3/weights/read/_3__cf__3"}); + tester.output("InceptionV3/InceptionV3/Conv2d_1a_3x3/Conv2D"); + tester.run(nodedef, graphbuilder); +} diff --git a/compiler/moco/import/src/Nodes/Conv2DBackpropInput.cpp b/compiler/moco/import/src/Nodes/Conv2DBackpropInput.cpp new file mode 100644 index 000000000..74c6605ab --- /dev/null +++ b/compiler/moco/import/src/Nodes/Conv2DBackpropInput.cpp @@ -0,0 +1,140 @@ +/* + * 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/Import/Nodes/Conv2DBackpropInput.h" + +#include <moco/IR/Nodes/TFConv2DBackpropInput.h> + +#include "Convert.h" + +#include <loco.h> +#include <stdex/Memory.h> +#include <plier/tf/Convert.h> +#include <oops/UserExn.h> + +namespace +{ +using namespace moco; + +/// @brief GraphUpdate for Conv2DBackpropInput node +class Conv2DBackpropInputGraphUpdate final : public GraphUpdate +{ +public: + Conv2DBackpropInputGraphUpdate(TFConv2DBackpropInput *node, std::vector<TensorName> names) + : _node(node), _input_names(names) + { + // DO NOTHING + } + + void input(const SymbolTable *) const override; + +private: + TFConv2DBackpropInput *_node; + std::vector<TensorName> _input_names; +}; + +void Conv2DBackpropInputGraphUpdate::input(const SymbolTable *table) const +{ + assert(_input_names.size() == 3); + + auto input_sizes_node = table->node(_input_names[0]); + auto filter_node = table->node(_input_names[1]); + auto out_backprop_node = table->node(_input_names[2]); + + assert(input_sizes_node != nullptr); + assert(filter_node != nullptr); + assert(out_backprop_node != nullptr); + + _node->input_sizes(input_sizes_node); + _node->filter(filter_node); + _node->out_backprop(out_backprop_node); +} + +} // namespace + +namespace moco +{ + +bool Conv2DBackpropInputGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + if (node.input_size() != 3) + return false; + + if (!plier::tf::has_attrs(node, {"T", "data_format", "padding", "strides"})) + return false; + + auto data_layout = plier::tf::get_string_attr(node, "data_format"); + if (!(data_layout == "NHWC" || data_layout == "NCHW")) + { + throw oops::UserExn("Conv2DBackprop Unsupported data_format", node.name()); + } + + // dilation attribute is not fully supported + if (plier::tf::has_attr(node, "dilations")) + { + // TODO Support non-default dilations + auto dilation = plier::tf::get_list_attr(node, "dilations").i(); + if (!std::all_of(dilation.begin(), dilation.end(), [](std::int64_t dil) { return dil == 1; })) + return false; + } + // Else, dilations are automatically set to default [1,1,1,1] which we assumes now + + return true; +} + +void Conv2DBackpropInputGraphBuilder::build(const tensorflow::NodeDef &node, + GraphBuilderContext *context) const +{ + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // name of loco nodes + std::string conv2d_backprop_name = node.name(); + + auto conv2d_backprop = graph->nodes()->create<TFConv2DBackpropInput>(); + conv2d_backprop->name(node.name()); + + // read attributes + auto data_layout = plier::tf::get_string_attr(node, "data_format"); + assert(data_layout == "NHWC" || data_layout == "NCHW"); + conv2d_backprop->data_layout(data_layout); + + auto tf_strides = plier::tf::get_list_attr(node, "strides"); + auto strides = plier::tf::as_int64_list(tf_strides); + conv2d_backprop->strides(strides); + + auto padding = moco::str_toupper(plier::tf::get_string_attr(node, "padding")); + assert(padding == "VALID" || padding == "SAME"); + conv2d_backprop->padding(padding); + + // save the name for graph link updates + TensorName output_name(conv2d_backprop_name, 0); + tensor_names->enroll(output_name, conv2d_backprop); + + std::vector<TensorName> input_names; + input_names.push_back(TensorName(node.input(0))); // input_sizes + input_names.push_back(TensorName(node.input(1))); // filter + input_names.push_back(TensorName(node.input(2))); // out_backprop + + // update + auto conv2d_backprop_update = + stdex::make_unique<Conv2DBackpropInputGraphUpdate>(conv2d_backprop, input_names); + + updates->enroll(std::move(conv2d_backprop_update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Conv2DBackpropInput.test.cpp b/compiler/moco/import/src/Nodes/Conv2DBackpropInput.test.cpp new file mode 100644 index 000000000..8c462bc3b --- /dev/null +++ b/compiler/moco/import/src/Nodes/Conv2DBackpropInput.test.cpp @@ -0,0 +1,98 @@ +/* + * 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/Import/Nodes/Conv2DBackpropInput.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ +// clang-format off +const char *conv2d_backprop_input_01_pbtxtdata = STRING_CONTENT( + name: "ofm" + op: "Conv2DBackpropInput" + input: "outshape" + input: "weights" + input: "ifm" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "data_format" + value { + s: "NHWC" + } + } + attr { + key: "dilations" + value { + list { + i: 1 + i: 1 + i: 1 + i: 1 + } + } + } + attr { + key: "padding" + value { + s: "SAME" + } + } + attr { + key: "strides" + value { + list { + i: 1 + i: 2 + i: 2 + i: 1 + } + } + } +); +// clang-format on +} // namespace + +TEST(TensorFlowImport, conv2d_backprop_input_01) +{ + TFNodeBuildTester tester; + moco::Conv2DBackpropInputGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(conv2d_backprop_input_01_pbtxtdata, nodedef)); + + // what to test: + // - All node inputs are valid + // - All attributes are as expected + + tester.inputs({"outshape", "weights", "ifm"}); + tester.output("ofm"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFConv2DBackpropInput *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->padding(), "SAME"); + ASSERT_EQ(test_node->data_layout(), "NHWC"); + ASSERT_EQ(test_node->strides().size(), 4); +} diff --git a/compiler/moco/import/src/Nodes/DepthwiseConv2dNative.cpp b/compiler/moco/import/src/Nodes/DepthwiseConv2dNative.cpp new file mode 100644 index 000000000..3991a4d51 --- /dev/null +++ b/compiler/moco/import/src/Nodes/DepthwiseConv2dNative.cpp @@ -0,0 +1,148 @@ +/* + * 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/Import/Nodes/DepthwiseConv2dNative.h" + +#include <moco/IR/Nodes/TFDepthwiseConv2dNative.h> + +#include <moco/Names.h> + +#include "Convert.h" + +#include <plier/tf/Convert.h> +#include <loco/IR/PermutingCodec.h> +#include <stdex/Memory.h> +#include <oops/UserExn.h> + +#include <cassert> + +using namespace plier::tf; + +namespace +{ +using namespace moco; + +class TFDepthwiseConv2dNativeGraphUpdate final : public GraphUpdate +{ +public: + TFDepthwiseConv2dNativeGraphUpdate(TFDepthwiseConv2dNative *node, std::vector<TensorName> names) + : _node(node), _names(names) + { + } + + void input(const SymbolTable *) const override; + +private: + TFDepthwiseConv2dNative *_node; + std::vector<TensorName> _names; +}; + +void TFDepthwiseConv2dNativeGraphUpdate::input(const SymbolTable *node_table) const +{ + assert(_names.size() == 2); + + auto input_node = node_table->node(_names[0]); + auto filter_node = node_table->node(_names[1]); + assert(input_node != nullptr); + assert(filter_node != nullptr); + + _node->input(input_node); + _node->filter(filter_node); +} + +} // namespace + +namespace moco +{ + +bool DepthwiseConv2dNativeGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + if (node.input_size() != 2) + return false; + + // note: even though "data_format" and "dilations" are not entered when a model is written, + // TF seems to generate those field into a pb file. + if (!has_attrs(node, {"T", "data_format", "dilations", "padding", "strides"})) + return false; + + auto data_layout = plier::tf::get_string_attr(node, "data_format"); + if (!(data_layout == "NHWC" || data_layout == "NCHW")) + { + throw oops::UserExn("DepthwiseConv2dNative Unsupported data_format", node.name()); + } + + auto padding = moco::str_toupper(get_string_attr(node, "padding")); + if (!(padding == "VALID" || padding == "SAME")) + return false; + + auto tf_strides = get_list_attr(node, "strides"); + auto strides = as_int64_list(tf_strides); + if (!(strides.size() == 4)) + { + throw oops::UserExn("DepthwiseConv2dNative strides requires rank 4", node.name()); + } + auto stride_n = strides.at(0); + auto stride_h = strides.at(1); + auto stride_w = strides.at(2); + auto stride_c = strides.at(3); + if (!(stride_n == 1 && stride_c == 1) || !(stride_h == stride_w)) + { + // TODO this message may need to be refined + throw oops::UserExn("DepthwiseConv2dNative strides requires N=C=1, H=W", node.name()); + } + + return true; +} + +void DepthwiseConv2dNativeGraphBuilder::build(const tensorflow::NodeDef &node, + GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + auto depthwiseconv2d_native_node = graph->nodes()->create<TFDepthwiseConv2dNative>(); + depthwiseconv2d_native_node->name(node.name()); + + // read attributes + auto data_layout = get_string_attr(node, "data_format"); + depthwiseconv2d_native_node->data_layout(data_layout); + + auto tf_strides = get_list_attr(node, "strides"); + auto strides = as_int64_list(tf_strides); + depthwiseconv2d_native_node->strides(strides); + + auto padding = moco::str_toupper(get_string_attr(node, "padding")); + depthwiseconv2d_native_node->padding(padding); + + // save the name for graph link updates + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, depthwiseconv2d_native_node); + + std::vector<TensorName> input_names; + input_names.push_back(TensorName(node.input(0))); // input + input_names.push_back(TensorName(node.input(1))); // kernel + + // Record ifm inputs to featureEncode_node + auto tfdepthwiseconv2dnative_update = stdex::make_unique<TFDepthwiseConv2dNativeGraphUpdate>( + depthwiseconv2d_native_node, input_names); + + updates->enroll(std::move(tfdepthwiseconv2dnative_update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/DepthwiseConv2dNative.test.cpp b/compiler/moco/import/src/Nodes/DepthwiseConv2dNative.test.cpp new file mode 100644 index 000000000..c65283c1b --- /dev/null +++ b/compiler/moco/import/src/Nodes/DepthwiseConv2dNative.test.cpp @@ -0,0 +1,97 @@ +/* + * 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/Import/Nodes/DepthwiseConv2dNative.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ +// clang-format off +const char *depthwise_conv2d_native_01_pbtxtdata = STRING_CONTENT( + name: "depthwise" + op: "DepthwiseConv2dNative" + input: "input" + input: "filter" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "data_format" + value { + s: "NHWC" + } + } + attr { + key: "dilations" + value { + list { + i: 1 + i: 1 + i: 1 + i: 1 + } + } + } + attr { + key: "padding" + value { + s: "VALID" + } + } + attr { + key: "strides" + value { + list { + i: 1 + i: 1 + i: 1 + i: 1 + } + } + } +); +// clang-format on +} // namespace + +TEST(TensorFlowImport, Depthwise_conv2d_native) +{ + TFNodeBuildTester tester; + moco::DepthwiseConv2dNativeGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(depthwise_conv2d_native_01_pbtxtdata, nodedef)); + + // what to test: + // - All node inputs are valid + // - All attributes are as expected + + tester.inputs({"input", "filter"}); + tester.output("depthwise"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFDepthwiseConv2dNative *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->padding(), "VALID"); + ASSERT_EQ(test_node->data_layout(), "NHWC"); + ASSERT_EQ(test_node->strides().size(), 4); +} diff --git a/compiler/moco/import/src/Nodes/FakeQuantWithMinMaxVars.cpp b/compiler/moco/import/src/Nodes/FakeQuantWithMinMaxVars.cpp new file mode 100644 index 000000000..d2fa3d1eb --- /dev/null +++ b/compiler/moco/import/src/Nodes/FakeQuantWithMinMaxVars.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 "moco/Import/Nodes/FakeQuantWithMinMaxVars.h" + +#include <moco/IR/Nodes/TFFakeQuantWithMinMaxVars.h> + +#include <moco/Names.h> + +#include "Convert.h" + +#include <plier/tf/Convert.h> +#include <loco/IR/PermutingCodec.h> +#include <stdex/Memory.h> + +#include <cassert> + +using namespace plier::tf; + +namespace +{ +using namespace moco; + +class TFFakeQuantWithMinMaxVarsGraphUpdate final : public GraphUpdate +{ +public: + TFFakeQuantWithMinMaxVarsGraphUpdate(TFFakeQuantWithMinMaxVars *node, + std::vector<TensorName> names) + : _node(node), _names(names) + { + } + + void input(const SymbolTable *) const override; + +private: + TFFakeQuantWithMinMaxVars *_node; + std::vector<TensorName> _names; +}; + +void TFFakeQuantWithMinMaxVarsGraphUpdate::input(const SymbolTable *node_table) const +{ + assert(_names.size() == 3); + + auto inputs_node = node_table->node(_names[0]); + auto min_node = node_table->node(_names[1]); + auto max_node = node_table->node(_names[2]); + assert(inputs_node != nullptr); + assert(min_node != nullptr); + assert(max_node != nullptr); + + _node->inputs(inputs_node); + _node->min(min_node); + _node->max(max_node); +} + +} // namespace + +namespace moco +{ + +bool FakeQuantWithMinMaxVarsGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + if (node.input_size() != 3) + return false; + + // attrs "narrow_range", "num_bits" are optional + return true; +} + +void FakeQuantWithMinMaxVarsGraphBuilder::build(const tensorflow::NodeDef &node, + GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + auto fakequant_node = graph->nodes()->create<TFFakeQuantWithMinMaxVars>(); + fakequant_node->name(node.name()); + + // read optional attributes + if (has_attr(node, "num_bits")) + { + auto num_bits = get_int_attr(node, "num_bits"); + fakequant_node->num_bits(num_bits); + } + if (has_attr(node, "narrow_range")) + { + auto narrow_range = get_bool_attr(node, "narrow_range"); + fakequant_node->narrow_range(narrow_range); + } + + // save the name for graph link updates + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, fakequant_node); + + std::vector<TensorName> input_names; + input_names.push_back(TensorName(node.input(0))); // inputs + input_names.push_back(TensorName(node.input(1))); // min + input_names.push_back(TensorName(node.input(2))); // max + + // Record ifm inputs to featureEncode_node + auto tffakequant_update = + stdex::make_unique<TFFakeQuantWithMinMaxVarsGraphUpdate>(fakequant_node, input_names); + + updates->enroll(std::move(tffakequant_update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/FakeQuantWithMinMaxVars.test.cpp b/compiler/moco/import/src/Nodes/FakeQuantWithMinMaxVars.test.cpp new file mode 100644 index 000000000..40c494bb0 --- /dev/null +++ b/compiler/moco/import/src/Nodes/FakeQuantWithMinMaxVars.test.cpp @@ -0,0 +1,65 @@ +/* + * 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/Import/Nodes/FakeQuantWithMinMaxVars.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ +// clang-format off +const char *fakequant_01_pbtxtdata = STRING_CONTENT( + name: "FakeQuant" + op: "FakeQuantWithMinMaxVars" + input: "Input" + input: "FakeMin" + input: "FakeMax" + attr { + key: "narrow_range" + value { b: true } + } + attr { + key: "num_bits" + value { i: 16 } + } +); +// clang-format on +} // namespace + +TEST(TensorFlowImport, FakeQuantWithMinMaxVars) +{ + TFNodeBuildTester tester; + moco::FakeQuantWithMinMaxVarsGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(fakequant_01_pbtxtdata, nodedef)); + + // what to test: + // - All node inputs are valid + // - All attributes are as expected + + tester.inputs({"Input", "FakeMin", "FakeMax"}); + tester.output("FakeQuant"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFFakeQuantWithMinMaxVars *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->narrow_range(), true); + ASSERT_EQ(test_node->num_bits(), 16); +} diff --git a/compiler/moco/import/src/Nodes/FusedBatchNorm.cpp b/compiler/moco/import/src/Nodes/FusedBatchNorm.cpp new file mode 100644 index 000000000..59f98017c --- /dev/null +++ b/compiler/moco/import/src/Nodes/FusedBatchNorm.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 "moco/Import/Nodes/FusedBatchNorm.h" + +#include <moco/IR/Nodes/TFFusedBatchNorm.h> + +#include <loco.h> +#include <stdex/Memory.h> +#include <plier/tf/Convert.h> + +namespace +{ + +using namespace moco; + +/** + * @brief GraphUpdate for FusedBatchNorm node + */ +class FusedBatchNormGraphUpdate final : public GraphUpdate +{ +public: + FusedBatchNormGraphUpdate(TFFusedBatchNorm *node, std::vector<TensorName> names) + : _node(node), _names(names) + { + } + + void input(const SymbolTable *) const override; + +private: + TFFusedBatchNorm *_node; + std::vector<TensorName> _names; +}; + +void FusedBatchNormGraphUpdate::input(const SymbolTable *tensor_names) const +{ + assert(_names.size() == 5); + + _node->x(tensor_names->node(_names[0])); + _node->scale(tensor_names->node(_names[1])); + _node->offset(tensor_names->node(_names[2])); + _node->mean(tensor_names->node(_names[3])); + _node->variance(tensor_names->node(_names[4])); +} + +} // namespace + +namespace moco +{ + +bool FusedBatchNormGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + if (node.input_size() != 5) + return false; + + return plier::tf::has_attrs(node, {"epsilon"}); +} + +void FusedBatchNormGraphBuilder::build(const tensorflow::NodeDef &node, + GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + float epsilon = plier::tf::get_float_attr(node, "epsilon"); + + // creating TF dialect FusedBatchNorm node + auto tf_fbn = graph->nodes()->create<TFFusedBatchNorm>(); + tf_fbn->name(node.name()); + tf_fbn->epsilon(epsilon); + + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, tf_fbn); + + std::vector<TensorName> fbn_input_names; + fbn_input_names.push_back(TensorName(node.input(0))); // input + fbn_input_names.push_back(TensorName(node.input(1))); // scale + fbn_input_names.push_back(TensorName(node.input(2))); // offset + fbn_input_names.push_back(TensorName(node.input(3))); // mean + fbn_input_names.push_back(TensorName(node.input(4))); // variance + + auto tf_fbn_update = stdex::make_unique<FusedBatchNormGraphUpdate>(tf_fbn, fbn_input_names); + updates->enroll(std::move(tf_fbn_update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/FusedBatchNorm.test.cpp b/compiler/moco/import/src/Nodes/FusedBatchNorm.test.cpp new file mode 100644 index 000000000..0f2e037b8 --- /dev/null +++ b/compiler/moco/import/src/Nodes/FusedBatchNorm.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 "moco/Import/Nodes/FusedBatchNorm.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ +// clang-format off +const char *fbn_basic_pbtxt = STRING_CONTENT( + name: "FBN_01" + op: "FusedBatchNorm" + input: "input" + input: "gamma" + input: "beta" + input: "FBN_01/mean" + input: "FBN_01/variance" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "data_format" + value { + s: "NHWC" + } + } + attr { + key: "epsilon" + value { + f: 0.001 + } + } + attr { + key: "is_training" + value { + b: false + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, tf_fbn_basic) +{ + TFNodeBuildTester tester; + moco::FusedBatchNormGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(fbn_basic_pbtxt, nodedef)); + + // what to test: + // - there should exist a TFFusedBatchNorm + // - input() should not be nullptr + // - gamma() should not be nullptr + // - beta() should not be nullptr + // - mean() should not be nullptr + // - variance() should not be nullptr + // - epsilon() value should match + + tester.inputs({"input", "gamma", "beta", "FBN_01/mean", "FBN_01/variance"}); + tester.output("FBN_01"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFFusedBatchNorm *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->epsilon(), 0.001f); +} diff --git a/compiler/moco/import/src/Nodes/Identity.cpp b/compiler/moco/import/src/Nodes/Identity.cpp new file mode 100644 index 000000000..8ca0e2d01 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Identity.cpp @@ -0,0 +1,95 @@ +/* + * 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/Import/Nodes/Identity.h" + +#include <moco/IR/Nodes/TFIdentity.h> + +#include <moco/Names.h> +#include <loco.h> +#include <stdex/Memory.h> + +#include <vector> + +namespace +{ + +using namespace moco; + +class TFIdentityGraphUpdate final : public GraphUpdate +{ +public: + TFIdentityGraphUpdate(TFIdentity *node, const std::vector<TensorName> &names) + : _node(node), _names(names) + { + } + + void input(const SymbolTable *) const override; + +private: + TFIdentity *_node; + const std::vector<TensorName> _names; +}; + +void TFIdentityGraphUpdate::input(const SymbolTable *tensor_names) const +{ + for (auto &name : _names) + { + loco::Node *target = tensor_names->node(name); + _node->input(target); + } +} + +} // namespace + +namespace moco +{ + +bool IdentityGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + if (node.input_size() < 1) // from TensorFlow lite toco + return false; + + return true; +} + +void IdentityGraphBuilder::build(const tensorflow::NodeDef &node, + GraphBuilderContext *context) const +{ + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // Create a Identity node + auto identity_node = graph->nodes()->create<TFIdentity>(); + identity_node->name(node.name()); + + // register string-name to node + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, identity_node); + + // Queue node input update + // TODO: Check if we really need multiple input handlings + std::vector<TensorName> names; + for (int i = 0; i < node.input_size(); ++i) + { + names.emplace_back(TensorName(node.input(i))); + } + auto update = stdex::make_unique<TFIdentityGraphUpdate>(identity_node, names); + updates->enroll(std::move(update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/MaxPool.cpp b/compiler/moco/import/src/Nodes/MaxPool.cpp new file mode 100644 index 000000000..63275a3b8 --- /dev/null +++ b/compiler/moco/import/src/Nodes/MaxPool.cpp @@ -0,0 +1,145 @@ +/* + * 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/Import/Nodes/MaxPool.h" + +#include <moco/IR/Nodes/TFMaxPool.h> + +#include <moco/Names.h> + +#include "Convert.h" + +#include <loco.h> +#include <loco/IR/PermutingCodec.h> +#include <stdex/Memory.h> +#include <plier/tf/Convert.h> +#include <oops/UserExn.h> + +#include <cassert> +#include <stdexcept> + +namespace +{ + +using namespace moco; + +class TFMaxPoolGraphUpdate final : public GraphUpdate +{ +public: + TFMaxPoolGraphUpdate(TFMaxPool *node, const TensorName &name) + : _maxpool_node(node), _input_name(name) + { + } + + void input(const SymbolTable *) const override; + +private: + TFMaxPool *_maxpool_node; + const TensorName _input_name; +}; + +void TFMaxPoolGraphUpdate::input(const SymbolTable *node_table) const +{ + loco::Node *input_node = node_table->node(_input_name); + _maxpool_node->input(input_node); +} + +} // namespace + +namespace moco +{ + +bool MaxPoolGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + // note: even though "data_format" is not entered when a model is written, + // TF seems to generate "data_format" field into a pb file + if (!plier::tf::has_attrs(node, {"T", "data_format", "ksize", "padding", "strides"})) + return false; + + auto data_layout = plier::tf::get_string_attr(node, "data_format"); + if (!(data_layout == "NHWC" || data_layout == "NCHW")) + { + throw oops::UserExn("MaxPool Unsupported data_format", node.name()); + } + + auto tf_ksize = plier::tf::get_list_attr(node, "ksize"); + auto ksize = plier::tf::as_int64_list(tf_ksize); + if (ksize.size() != 4) + { + // TODO support ksize length for 1 and 2 + throw oops::UserExn("MaxPool ksize requires rank 4", node.name()); + } + + auto tf_strides = plier::tf::get_list_attr(node, "strides"); + auto strides = plier::tf::as_int64_list(tf_strides); + if (strides.size() != 4) + { + // TODO support strides length for 1 and 2 + throw oops::UserExn("MaxPool strides requires rank 4", node.name()); + } + + return true; +} + +void MaxPoolGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // name of loco nodes + ::std::string node_name = node.name(); + + // tensorflow data_format: one of NHWC or NCHW. + auto data_layout = plier::tf::get_string_attr(node, "data_format"); + auto maxPool_node = graph->nodes()->create<TFMaxPool>(); + maxPool_node->name(node.name()); + maxPool_node->data_layout(data_layout); + + // padding + auto padding = moco::str_toupper(plier::tf::get_string_attr(node, "padding")); + maxPool_node->padding(padding); + + // ksize + auto tf_ksize = plier::tf::get_list_attr(node, "ksize"); + auto ksize = plier::tf::as_int64_list(tf_ksize); + assert(ksize.size() == 4); + maxPool_node->ksize(ksize); + + // strides + auto tf_strides = plier::tf::get_list_attr(node, "strides"); + auto strides = plier::tf::as_int64_list(tf_strides); + assert(strides.size() == 4); + maxPool_node->strides(strides); + + // To set the input node of encode_node with node_name + TensorName output_name(node_name, 0); + tensor_names->enroll(output_name, maxPool_node); + + // Record ifm inputs to featureEncode_node + auto update = stdex::make_unique<TFMaxPoolGraphUpdate>(maxPool_node, TensorName(node.input(0))); + + updates->enroll(std::move(update)); +} + +} // namespace moco + +// TODO Consider a case when TF MaxPool is for 3D. +// MaxPool works for 2D and other Dimensions, such as 3D +// So, in future, some other GraphBuilder decide if MaxPoolGraphBuilder is used or +// other GraphBuilder is used for TF MaxPool diff --git a/compiler/moco/import/src/Nodes/MaxPool.test.cpp b/compiler/moco/import/src/Nodes/MaxPool.test.cpp new file mode 100644 index 000000000..a85e2027b --- /dev/null +++ b/compiler/moco/import/src/Nodes/MaxPool.test.cpp @@ -0,0 +1,98 @@ +/* + * 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/Import/Nodes/MaxPool.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ +// clang-format off +const char *maxpool_01_pbtxtdata = STRING_CONTENT( + name: "maxpool" + op: "MaxPool" + input: "const/float" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "data_format" + value { + s: "NHWC" + } + } + attr { + key: "ksize" + value { + list { + i: 1 + i: 2 + i: 3 + i: 1 + } + } + } + attr { + key: "padding" + value { + s: "VALID" + } + } + attr { + key: "strides" + value { + list { + i: 1 + i: 3 + i: 2 + i: 1 + } + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, MaxPool_01) +{ + TFNodeBuildTester tester; + moco::MaxPoolGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(maxpool_01_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFMaxPool + // - attributes value should match + + tester.inputs({"const/float"}); + tester.output("maxpool"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFMaxPool *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->data_layout(), "NHWC"); + ASSERT_EQ(test_node->padding(), "VALID"); + ASSERT_EQ(test_node->ksize(), std::vector<int64_t>({1, 2, 3, 1})); + ASSERT_EQ(test_node->strides(), std::vector<int64_t>({1, 3, 2, 1})); +} diff --git a/compiler/moco/import/src/Nodes/Maximum.cpp b/compiler/moco/import/src/Nodes/Maximum.cpp new file mode 100644 index 000000000..43bbbabe6 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Maximum.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "moco/Import/Nodes/Maximum.h" + +#include <moco/IR/Nodes/TFMaximum.h> + +#include <loco.h> +#include <stdex/Memory.h> + +namespace +{ + +using namespace moco; + +/** + * @brief GraphUpdate for TF Maximum node + */ +class TFMaximumGraphUpdate final : public GraphUpdate +{ +public: + TFMaximumGraphUpdate(TFMaximum *node, std::vector<TensorName> names) : _node(node), _names(names) + { + } + + void input(const SymbolTable *) const override; + +private: + TFMaximum *_node; + std::vector<TensorName> _names; +}; + +void TFMaximumGraphUpdate::input(const SymbolTable *tensor_names) const +{ + assert(_names.size() == 2); + + _node->x(tensor_names->node(_names[0])); + _node->y(tensor_names->node(_names[1])); +} + +} // namespace + +namespace moco +{ + +bool MaximumGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + return node.input_size() == 2; +} + +void MaximumGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // creating TF dialect Maximum node + auto tf_maximum = graph->nodes()->create<TFMaximum>(); + tf_maximum->name(node.name()); + + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, tf_maximum); + + std::vector<TensorName> add_input_names; + add_input_names.push_back(TensorName(node.input(0))); // x + add_input_names.push_back(TensorName(node.input(1))); // y + + auto tf_maximum_update = stdex::make_unique<TFMaximumGraphUpdate>(tf_maximum, add_input_names); + updates->enroll(std::move(tf_maximum_update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Maximum.test.cpp b/compiler/moco/import/src/Nodes/Maximum.test.cpp new file mode 100644 index 000000000..2a8b63622 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Maximum.test.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "moco/Import/Nodes/Maximum.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ +// clang-format off +const char *maximum_basic_pbtxt = STRING_CONTENT( + name: "MAXIMUM_01" + op: "Maximum" + input: "input_01" + input: "input_02" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, tf_maximum_basic) +{ + TFNodeBuildTester tester; + moco::MaximumGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(maximum_basic_pbtxt, nodedef)); + + // what to test: + // - TFMaximum node should exist + // - both inputs x() and y() should not be null + + tester.inputs({"input_01", "input_02"}); + tester.output("MAXIMUM_01"); + tester.run(nodedef, graphbuilder); +} diff --git a/compiler/moco/import/src/Nodes/Mean.cpp b/compiler/moco/import/src/Nodes/Mean.cpp new file mode 100644 index 000000000..30fb0f1f7 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Mean.cpp @@ -0,0 +1,99 @@ +/* + * 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/Import/Nodes/Mean.h" + +#include <moco/IR/Nodes/TFMean.h> + +#include <loco.h> +#include <stdex/Memory.h> +#include <plier/tf/Convert.h> + +namespace +{ +using namespace moco; + +/** + * @brief GraphUpdate for Mean node + */ +class MeanGraphUpdate final : public GraphUpdate +{ +public: + MeanGraphUpdate(TFMean *node, const TensorName &&input_name, + const TensorName &&reduction_indices_name) + : _node(node), _input_name(input_name), _reduction_indices_name(reduction_indices_name) + { + // DO NOTHING + } + + void input(const SymbolTable *) const override; + +private: + TFMean *_node; + const TensorName _input_name; + const TensorName _reduction_indices_name; +}; + +void MeanGraphUpdate::input(const SymbolTable *table) const +{ + loco::Node *input_node = table->node(_input_name); + loco::Node *reduction_indices_node = table->node(_reduction_indices_name); + _node->input(input_node); + _node->reduction_indices(reduction_indices_node); +} + +} // namespace + +namespace moco +{ + +bool MeanGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + if (node.input_size() != 2) + return false; + + if (!plier::tf::has_attrs(node, {"T", "Tidx", "keep_dims"})) + return false; + + auto dtype = plier::tf::get_datatype_attr(node, "Tidx"); + if (dtype != tensorflow::DataType::DT_INT32 && dtype != tensorflow::DataType::DT_INT64) + return false; + + return true; +} + +void MeanGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // creating TF dialect Mean node + auto tf_mean = graph->nodes()->create<TFMean>(); + tf_mean->name(node.name()); + tf_mean->keep_dims(plier::tf::get_bool_attr(node, "keep_dims")); + + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, tf_mean); + + auto update = stdex::make_unique<MeanGraphUpdate>(tf_mean, TensorName(node.input(0)), + TensorName(node.input(1))); + updates->enroll(std::move(update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Mean.test.cpp b/compiler/moco/import/src/Nodes/Mean.test.cpp new file mode 100644 index 000000000..6321fad16 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Mean.test.cpp @@ -0,0 +1,120 @@ +/* + * 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/Import/Nodes/Mean.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ + +// clang-format off +const char *mean_true_pbtxtdata = STRING_CONTENT( + name: "Mean" + op: "Mean" + input: "Placeholder" + input: "Const" + attr { + key: "T" + value { type: DT_FLOAT } + } + attr { + key: "Tidx" + value { type: DT_INT32 } + } + attr { + key: "keep_dims" + value { b: true } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, mean_true) +{ + TFNodeBuildTester tester; + moco::MeanGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(mean_true_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFMean + // - input node should not be nullptr + // - reduction_indeces node should not be nullptr + // - keep_dims attribute is set same as pbtxt + + tester.inputs({"Placeholder", "Const"}); + tester.output("Mean"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFMean *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->keep_dims(), true); +} + +namespace +{ + +// clang-format off +const char *mean_false_pbtxtdata = STRING_CONTENT( + name: "Mean" + op: "Mean" + input: "Placeholder" + input: "Const" + attr { + key: "T" + value { type: DT_FLOAT } + } + attr { + key: "Tidx" + value { type: DT_INT32 } + } + attr { + key: "keep_dims" + value { b: false } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, mean_false) +{ + TFNodeBuildTester tester; + moco::MeanGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(mean_false_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFMean + // - input node should not be nullptr + // - reduction_indeces node should not be nullptr + // - keep_dims attribute is set same as pbtxt + + tester.inputs({"Placeholder", "Const"}); + tester.output("Mean"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFMean *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->keep_dims(), false); +} diff --git a/compiler/moco/import/src/Nodes/Mul.cpp b/compiler/moco/import/src/Nodes/Mul.cpp new file mode 100644 index 000000000..ab926b59e --- /dev/null +++ b/compiler/moco/import/src/Nodes/Mul.cpp @@ -0,0 +1,85 @@ +/* + * 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/Import/Nodes/Mul.h" + +#include <moco/IR/Nodes/TFMul.h> + +#include <loco.h> +#include <stdex/Memory.h> + +namespace +{ + +using namespace moco; + +/** + * @brief GraphUpdate for TF Mul node + */ +class TFMulGraphUpdate final : public GraphUpdate +{ +public: + TFMulGraphUpdate(TFMul *node, std::vector<TensorName> names) : _node(node), _names(names) {} + + void input(const SymbolTable *) const override; + +private: + TFMul *_node; + std::vector<TensorName> _names; +}; + +void TFMulGraphUpdate::input(const SymbolTable *tensor_names) const +{ + assert(_names.size() == 2); + + _node->x(tensor_names->node(_names[0])); + _node->y(tensor_names->node(_names[1])); +} + +} // namespace + +namespace moco +{ + +bool MulGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + return node.input_size() == 2; +} + +void MulGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // creating TF dialect Mul node + auto tf_mul = graph->nodes()->create<TFMul>(); + tf_mul->name(node.name()); + + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, tf_mul); + + std::vector<TensorName> add_input_names; + add_input_names.push_back(TensorName(node.input(0))); // x + add_input_names.push_back(TensorName(node.input(1))); // y + + auto tf_mul_update = stdex::make_unique<TFMulGraphUpdate>(tf_mul, add_input_names); + updates->enroll(std::move(tf_mul_update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Mul.test.cpp b/compiler/moco/import/src/Nodes/Mul.test.cpp new file mode 100644 index 000000000..92730b377 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Mul.test.cpp @@ -0,0 +1,58 @@ +/* + * 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/Import/Nodes/Mul.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ +// clang-format off +const char *mul_basic_pbtxt = STRING_CONTENT( + name: "MUL_01" + op: "Mul" + input: "input_01" + input: "input_02" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, tf_mul_basic) +{ + TFNodeBuildTester tester; + moco::MulGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(mul_basic_pbtxt, nodedef)); + + // what to test: + // - TFMul node should exist + // - both inputs x() and y() should not be null + + tester.inputs({"input_01", "input_02"}); + tester.output("MUL_01"); + tester.run(nodedef, graphbuilder); +} diff --git a/compiler/moco/import/src/Nodes/Pack.cpp b/compiler/moco/import/src/Nodes/Pack.cpp new file mode 100644 index 000000000..45815a30e --- /dev/null +++ b/compiler/moco/import/src/Nodes/Pack.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 "moco/Import/Nodes/Pack.h" + +#include <moco/IR/Nodes/TFPack.h> +#include <moco/IR/Nodes/TFConst.h> + +#include <moco/Names.h> + +#include <loco.h> +#include <loco/IR/NodeShape.h> +#include <stdex/Memory.h> +#include <plier/tf/Convert.h> + +#include <cassert> + +namespace +{ + +using namespace moco; + +class TFPackGraphUpdate final : public GraphUpdate +{ +public: + TFPackGraphUpdate(TFPack *node, std::vector<TensorName> names) : _node(node), _names(names) {} + + void input(const SymbolTable *) const override; + +private: + TFPack *_node; + std::vector<TensorName> _names; +}; + +void TFPackGraphUpdate::input(const SymbolTable *tensor_names) const +{ + uint32_t num_values = _names.size(); + assert(num_values >= 1); + + for (uint32_t i = 0; i < num_values; ++i) + { + auto input_node = tensor_names->node(_names[i]); + assert(input_node != nullptr); + _node->values(i, input_node); + } +} + +} // namespace + +namespace moco +{ + +bool PackGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + if (!plier::tf::has_attrs(node, {"T", "N", "axis"})) + return false; + + const int num_inputs = node.input_size(); + return (num_inputs >= 1) && (num_inputs == plier::tf::get_int_attr(node, "N")); +} + +void PackGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + auto graph = context->graph(); + auto tensor_names = context->tensor_names(); + auto updates = context->updates(); + + const int num_inputs = node.input_size(); + std::vector<TensorName> input_names; + auto pack_node = graph->nodes()->create<TFPack>(num_inputs); + pack_node->name(node.name()); + + for (int ni = 0; ni < num_inputs; ++ni) + { + input_names.push_back(TensorName(node.input(ni))); + } + + pack_node->axis(plier::tf::get_int_attr(node, "axis")); + + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, pack_node); + + auto update = stdex::make_unique<TFPackGraphUpdate>(pack_node, input_names); + updates->enroll(std::move(update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Pack.test.cpp b/compiler/moco/import/src/Nodes/Pack.test.cpp new file mode 100644 index 000000000..01774a906 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Pack.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/Import/Nodes/Pack.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ + +// clang-format off +const char *pack_01_pbtxtdata = STRING_CONTENT( + name: "Pack" + op: "Pack" + input: "input_1" + input: "input_2" + input: "input_3" + input: "input_4" + attr { + key: "N" + value { + i: 4 + } + } + attr { + key: "T" + value { + type: DT_INT32 + } + } + attr { + key: "axis" + value { + i: 0 + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, tf_pack_basic) +{ + TFNodeBuildTester tester; + moco::PackGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(pack_01_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFPack + // - there should be four values + // - values(idx) should not be nullptr + // - axis() should be 0 + + tester.inputs({"input_1", "input_2", "input_3", "input_4"}); + tester.output("Pack"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFPack *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->N(), 4); + ASSERT_NE(test_node->values(0), nullptr); + ASSERT_NE(test_node->values(1), nullptr); + ASSERT_NE(test_node->values(2), nullptr); + ASSERT_NE(test_node->values(3), nullptr); + ASSERT_EQ(test_node->axis(), 0); +} diff --git a/compiler/moco/import/src/Nodes/Pad.cpp b/compiler/moco/import/src/Nodes/Pad.cpp new file mode 100644 index 000000000..262a68fa0 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Pad.cpp @@ -0,0 +1,91 @@ +/* + * 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/Import/Nodes/Pad.h" + +#include <moco/IR/Nodes/TFPad.h> + +#include <loco.h> +#include <stdex/Memory.h> +#include <plier/tf/Convert.h> + +namespace +{ + +using namespace moco; + +/** + * @brief GraphUpdate for TF Pad node + */ +class TFPadGraphUpdate final : public GraphUpdate +{ +public: + TFPadGraphUpdate(TFPad *node, std::vector<TensorName> names) : _node(node), _names(names) {} + + void input(const SymbolTable *) const override; + +private: + TFPad *_node; + std::vector<TensorName> _names; +}; + +void TFPadGraphUpdate::input(const SymbolTable *table) const +{ + assert(_names.size() == 2); + + _node->input(table->node(_names[0])); + _node->paddings(table->node(_names[1])); +} + +} // namespace + +namespace moco +{ + +bool PadGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + if (node.input_size() != 2) + return false; + + return plier::tf::has_attrs(node, {"T", "Tpaddings"}); +} + +void PadGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // creating TF dialect Pad node + auto tf_pad = graph->nodes()->create<TFPad>(); + tf_pad->name(node.name()); + + // register string-name to node + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, tf_pad); + + std::vector<TensorName> add_input_names; + add_input_names.push_back(TensorName(node.input(0))); // input + add_input_names.push_back(TensorName(node.input(1))); // paddings + + // Queue node input update + auto tf_pad_update = stdex::make_unique<TFPadGraphUpdate>(tf_pad, add_input_names); + updates->enroll(std::move(tf_pad_update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Pad.test.cpp b/compiler/moco/import/src/Nodes/Pad.test.cpp new file mode 100644 index 000000000..19769cf6b --- /dev/null +++ b/compiler/moco/import/src/Nodes/Pad.test.cpp @@ -0,0 +1,65 @@ +/* + * 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/Import/Nodes/Pad.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ +// clang-format off +const char *pad_basic_pbtxt = STRING_CONTENT( + name: "Pad" + op: "Pad" + input: "input" + input: "paddings" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "Tpaddings" + value { + type: DT_INT32 + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, tf_pad_basic) +{ + TFNodeBuildTester tester; + moco::PadGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(pad_basic_pbtxt, nodedef)); + + // what to test: + // - TFPad node should exist + // - input input() should not be null + // - input paddings() should not be null + + tester.inputs({"input", "paddings"}); + tester.output("Pad"); + tester.run(nodedef, graphbuilder); +} diff --git a/compiler/moco/import/src/Nodes/Placeholder.cpp b/compiler/moco/import/src/Nodes/Placeholder.cpp new file mode 100644 index 000000000..0033f664b --- /dev/null +++ b/compiler/moco/import/src/Nodes/Placeholder.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 "moco/Import/Nodes/Placeholder.h" + +#include <moco/IR/Nodes/TFPlaceholder.h> + +#include <moco/Names.h> +#include <plier/tf/Convert.h> + +#include <cassert> +#include <stdexcept> + +namespace moco +{ + +bool PlaceholderGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + if (!plier::tf::has_attrs(node, {"dtype", "shape"})) + return false; + + loco::DataType dtype = plier::tf::as_loco_datatype(plier::tf::get_datatype_attr(node, "dtype")); + if (dtype != loco::DataType::FLOAT32) + return false; + // TODO support other types + + return true; +} + +void PlaceholderGraphBuilder::build(const tensorflow::NodeDef &node, + GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + + loco::DataType dtype = plier::tf::as_loco_datatype(plier::tf::get_datatype_attr(node, "dtype")); + const auto &shape = plier::tf::get_shape_attr(node, "shape"); + // TODO handle for unknown rank + assert(!shape.unknown_rank()); + int64_t num_dims = shape.dim_size(); + + // TODO support other types + assert(dtype == loco::DataType::FLOAT32); + + // Create a "Placeholder" node as an input + auto placeholder_node = graph->nodes()->create<moco::TFPlaceholder>(); + placeholder_node->name(node.name()); + placeholder_node->dtype(dtype); + + // Setting shape info. + placeholder_node->rank(num_dims); + for (int64_t d = 0; d < num_dims; d++) + { + assert(shape.dim(d).size() < std::numeric_limits<uint32_t>::max()); + int64_t dim_value = shape.dim(d).size(); + if (dim_value >= 0) + { + uint32_t dim_value32 = static_cast<uint32_t>(dim_value); + placeholder_node->dim(d) = dim_value32; + } + else + { + placeholder_node->dim(d).unset(); + // TODO Remove assert() and do implement + // NOTE Current implementation assumes dim is all know + assert(false); + } + } + + // register string-name to node + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, placeholder_node); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Placeholder.test.cpp b/compiler/moco/import/src/Nodes/Placeholder.test.cpp new file mode 100644 index 000000000..80488ce39 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Placeholder.test.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 "moco/Import/Nodes/Placeholder.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ +// clang-format off +const char *known_batch_pbtxt = STRING_CONTENT( + name: "placeholder" + op: "Placeholder" + attr { + key: "dtype" value { type: DT_FLOAT } + } + attr { + key: "shape" + value { + shape { + dim { size: 1024 } + dim { size: 2 } + dim { size: 3 } + dim { size: 4 } + } + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, placeholder_knwon_batch) +{ + TFNodeBuildTester tester; + moco::PlaceholderGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(known_batch_pbtxt, nodedef)); + + // what to test: + // - TFPlaceholder node should exist + // - shape attribute should match + + tester.inputs({}); + tester.output("placeholder"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFPlaceholder *>(tester.output()); + assert(test_node != nullptr); + ASSERT_TRUE(test_node->dim(0).known() && test_node->dim(0).value() == 1024); + ASSERT_TRUE(test_node->dim(1).known() && test_node->dim(1).value() == 2); + ASSERT_TRUE(test_node->dim(2).known() && test_node->dim(2).value() == 3); + ASSERT_TRUE(test_node->dim(3).known() && test_node->dim(3).value() == 4); +} diff --git a/compiler/moco/import/src/Nodes/RealDiv.cpp b/compiler/moco/import/src/Nodes/RealDiv.cpp new file mode 100644 index 000000000..de3d57673 --- /dev/null +++ b/compiler/moco/import/src/Nodes/RealDiv.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 "moco/Import/Nodes/RealDiv.h" + +#include <moco/IR/Nodes/TFRealDiv.h> + +#include <loco.h> +#include <stdex/Memory.h> + +namespace +{ + +using namespace moco; + +/** + * @brief GraphUpdate for TF RealDiv node + */ +class TFRealDivGraphUpdate final : public GraphUpdate +{ +public: + TFRealDivGraphUpdate(TFRealDiv *node, std::vector<TensorName> names) : _node(node), _names(names) + { + } + + void input(const SymbolTable *) const override; + +private: + TFRealDiv *_node; + std::vector<TensorName> _names; +}; + +void TFRealDivGraphUpdate::input(const SymbolTable *tensor_names) const +{ + assert(_names.size() == 2); + + _node->x(tensor_names->node(_names[0])); + _node->y(tensor_names->node(_names[1])); +} + +} // namespace + +namespace moco +{ +bool RealDivGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + return node.input_size() == 2; +} + +void RealDivGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // creating TF dialect RealDiv node + auto tf_div = graph->nodes()->create<TFRealDiv>(); + tf_div->name(node.name()); + + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, tf_div); + + std::vector<TensorName> div_input_names; + div_input_names.push_back(TensorName(node.input(0))); // x + div_input_names.push_back(TensorName(node.input(1))); // y + + auto tf_div_update = stdex::make_unique<TFRealDivGraphUpdate>(tf_div, div_input_names); + updates->enroll(std::move(tf_div_update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/RealDiv.test.cpp b/compiler/moco/import/src/Nodes/RealDiv.test.cpp new file mode 100644 index 000000000..cda2d3738 --- /dev/null +++ b/compiler/moco/import/src/Nodes/RealDiv.test.cpp @@ -0,0 +1,58 @@ +/* + * 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/Import/Nodes/RealDiv.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ +// clang-format off +const char *div_basic_pbtxt = STRING_CONTENT( + name: "DIV_01" + op: "RealDiv" + input: "input_01" + input: "input_02" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, tf_div_basic) +{ + TFNodeBuildTester tester; + moco::RealDivGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(div_basic_pbtxt, nodedef)); + + // what to test: + // - TFRealDiv node should exist + // - both inputs x() and y() should not be null + + tester.inputs({"input_01", "input_02"}); + tester.output("DIV_01"); + tester.run(nodedef, graphbuilder); +} diff --git a/compiler/moco/import/src/Nodes/Relu.cpp b/compiler/moco/import/src/Nodes/Relu.cpp new file mode 100644 index 000000000..eedc8155d --- /dev/null +++ b/compiler/moco/import/src/Nodes/Relu.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 "moco/Import/Nodes/Relu.h" + +#include <moco/IR/Nodes/TFRelu.h> + +#include <moco/Names.h> +#include <loco.h> +#include <stdex/Memory.h> + +#include <cassert> +#include <stdexcept> + +namespace +{ + +using namespace moco; + +class TFReluGraphUpdate final : public GraphUpdate +{ +public: + TFReluGraphUpdate(TFRelu *node, const TensorName &&name) : _node(node), _name(name) {} + + void input(const SymbolTable *) const override; + +private: + TFRelu *_node; + const TensorName _name; +}; + +void TFReluGraphUpdate::input(const SymbolTable *table) const +{ + loco::Node *target = table->node(_name); + _node->features(target); +} + +} // namespace + +namespace moco +{ + +bool ReluGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + // ReLU node SHOULD have only one input + if (node.input_size() != 1) + return false; + + return true; +} + +void ReluGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // Create a "TFRelu" node for Relu + auto relu_node = graph->nodes()->create<TFRelu>(); + relu_node->name(node.name()); + + // register string-name to node + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, relu_node); + + // Queue node input update + auto update = stdex::make_unique<TFReluGraphUpdate>(relu_node, TensorName(node.input(0))); + updates->enroll(std::move(update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Relu.test.cpp b/compiler/moco/import/src/Nodes/Relu.test.cpp new file mode 100644 index 000000000..a20ee081d --- /dev/null +++ b/compiler/moco/import/src/Nodes/Relu.test.cpp @@ -0,0 +1,58 @@ +/* + * 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/Import/Nodes/Relu.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ + +// clang-format off +const char *relu_01_pbtxtdata = STRING_CONTENT( + name: "ReLU" + op: "Relu" + input: "Placeholder" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, relu_01) +{ + TFNodeBuildTester tester; + moco::ReluGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(relu_01_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFRelu + // - features node should not be nullptr + + tester.inputs({"Placeholder"}); + tester.output("ReLU"); + tester.run(nodedef, graphbuilder); +} diff --git a/compiler/moco/import/src/Nodes/Relu6.cpp b/compiler/moco/import/src/Nodes/Relu6.cpp new file mode 100644 index 000000000..4700ba408 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Relu6.cpp @@ -0,0 +1,80 @@ +/* + * 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/Import/Nodes/Relu6.h" + +#include <moco/IR/Nodes/TFRelu6.h> + +#include <stdex/Memory.h> + +namespace +{ + +using namespace moco; + +class TFRelu6GraphUpdate final : public GraphUpdate +{ +public: + TFRelu6GraphUpdate(TFRelu6 *node, const TensorName &&name) : _node(node), _name(name) {} + + void input(const SymbolTable *) const override; + +private: + TFRelu6 *_node; + const TensorName _name; +}; + +void TFRelu6GraphUpdate::input(const SymbolTable *table) const +{ + loco::Node *target = table->node(_name); + _node->features(target); +} + +} // namespace + +namespace moco +{ + +bool Relu6GraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + // ReLU6 node SHOULD have only one input + if (node.input_size() != 1) + return false; + return true; +} + +void Relu6GraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // Create a "TFRelu6" node for Relu + auto relu_node = graph->nodes()->create<TFRelu6>(); + relu_node->name(node.name()); + + // register string-name to node + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, relu_node); + + // Queue node input update + auto update = stdex::make_unique<TFRelu6GraphUpdate>(relu_node, TensorName(node.input(0))); + updates->enroll(std::move(update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Relu6.test.cpp b/compiler/moco/import/src/Nodes/Relu6.test.cpp new file mode 100644 index 000000000..26beb6c17 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Relu6.test.cpp @@ -0,0 +1,58 @@ +/* + * 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/Import/Nodes/Relu6.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ + +// clang-format off +const char *relu6_01_pbtxtdata = STRING_CONTENT( + name: "ReLU6" + op: "Relu6" + input: "Placeholder" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, relu6_01) +{ + TFNodeBuildTester tester; + moco::Relu6GraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(relu6_01_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFRelu6 + // - features node should not be null + + tester.inputs({"Placeholder"}); + tester.output("ReLU6"); + tester.run(nodedef, graphbuilder); +} diff --git a/compiler/moco/import/src/Nodes/Reshape.cpp b/compiler/moco/import/src/Nodes/Reshape.cpp new file mode 100644 index 000000000..26e22513f --- /dev/null +++ b/compiler/moco/import/src/Nodes/Reshape.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 "moco/Import/Nodes/Reshape.h" + +#include <moco/IR/Nodes/TFReshape.h> + +#include <moco/Names.h> +#include <plier/tf/Convert.h> +#include <loco.h> +#include <stdex/Memory.h> + +#include <cassert> +#include <stdexcept> + +namespace +{ +using namespace moco; + +class ReshapeGraphUpdate final : public GraphUpdate +{ +public: + ReshapeGraphUpdate(TFReshape *node, std::vector<TensorName> names) : _node(node), _names(names) {} + + void input(const SymbolTable *) const override; + +private: + TFReshape *_node; + std::vector<TensorName> _names; +}; + +void ReshapeGraphUpdate::input(const SymbolTable *node_table) const +{ + assert(_names.size() == 2); + + auto tensor_node = node_table->node(_names[0]); + auto shape_node = node_table->node(_names[1]); + + assert(tensor_node != nullptr); + assert(shape_node != nullptr); + + _node->tensor(tensor_node); + _node->shape(shape_node); +} + +} // namespace + +namespace moco +{ + +bool ReshapeGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + // Tensorflow Reshape has 2 inputs: tensor & shape + if (node.input_size() != 2) + return false; + + // TODO Assert Tshape value is DT_INT32? + return plier::tf::has_attrs(node, {"T", "Tshape"}); +} + +void ReshapeGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // name of loco nodes + std::string reshape_name = node.name(); + + auto reshape = graph->nodes()->create<TFReshape>(); + reshape->name(node.name()); + + // save the name for graph link updates + TensorName output_name(reshape_name, 0); + tensor_names->enroll(output_name, reshape); + + std::vector<TensorName> input_names; + input_names.push_back(TensorName(node.input(0))); // tensor + input_names.push_back(TensorName(node.input(1))); // shape + + // Queue node input update + auto update = stdex::make_unique<ReshapeGraphUpdate>(reshape, input_names); + + updates->enroll(std::move(update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Reshape.test.cpp b/compiler/moco/import/src/Nodes/Reshape.test.cpp new file mode 100644 index 000000000..c406bf47b --- /dev/null +++ b/compiler/moco/import/src/Nodes/Reshape.test.cpp @@ -0,0 +1,61 @@ +/* + * 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/Import/Nodes/Reshape.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ + +// clang-format off +const char *reshape_01_pbtxtdata = STRING_CONTENT( + name: "reshape" + op: "Reshape" + input: "placeholder" + input: "shape" + attr { + key: "T" + value { type: DT_FLOAT } + } + attr { + key: "Tshape" + value { type: DT_INT32 } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, reshape_01) +{ + TFNodeBuildTester tester; + moco::ReshapeGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(reshape_01_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFReshape + // - input nodes should not be null + + tester.inputs({"placeholder", "shape"}); + tester.output("reshape"); + tester.run(nodedef, graphbuilder); +} diff --git a/compiler/moco/import/src/Nodes/Rsqrt.cpp b/compiler/moco/import/src/Nodes/Rsqrt.cpp new file mode 100644 index 000000000..979ac90c9 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Rsqrt.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 "moco/Import/Nodes/Rsqrt.h" + +#include <moco/IR/Nodes/TFRsqrt.h> + +#include <loco.h> +#include <stdex/Memory.h> + +namespace +{ + +using namespace moco; + +/** + * @brief GraphUpdate for TF Rsqrt node + */ +class TFRsqrtGraphUpdate final : public GraphUpdate +{ +public: + TFRsqrtGraphUpdate(TFRsqrt *node, TensorName &&name) : _node(node), _name(name) {} + + void input(const SymbolTable *) const override; + +private: + TFRsqrt *_node; + TensorName _name; +}; + +void TFRsqrtGraphUpdate::input(const SymbolTable *table) const +{ + loco::Node *target = table->node(_name); + _node->x(target); +} + +} // namespace + +namespace moco +{ + +bool RsqrtGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + return node.input_size() == 1; +} + +void RsqrtGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // creating TF dialect Rsqrt node + auto tf_rsqrt = graph->nodes()->create<TFRsqrt>(); + tf_rsqrt->name(node.name()); + + // register string-name to node + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, tf_rsqrt); + + // Queue node input update + auto tf_rsqrt_update = + stdex::make_unique<TFRsqrtGraphUpdate>(tf_rsqrt, TensorName(node.input(0))); + updates->enroll(std::move(tf_rsqrt_update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Rsqrt.test.cpp b/compiler/moco/import/src/Nodes/Rsqrt.test.cpp new file mode 100644 index 000000000..2750725bc --- /dev/null +++ b/compiler/moco/import/src/Nodes/Rsqrt.test.cpp @@ -0,0 +1,57 @@ +/* + * 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/Import/Nodes/Rsqrt.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ +// clang-format off +const char *rsqrt_basic_pbtxt = STRING_CONTENT( + name: "RSQRT_01" + op: "Rsqrt" + input: "Placeholder" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, tf_rsqrt_basic) +{ + TFNodeBuildTester tester; + moco::RsqrtGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(rsqrt_basic_pbtxt, nodedef)); + + // what to test: + // - TFRsqrt node should exist + // - input x() should not be null + + tester.inputs({"Placeholder"}); + tester.output("RSQRT_01"); + tester.run(nodedef, graphbuilder); +} diff --git a/compiler/moco/import/src/Nodes/Shape.cpp b/compiler/moco/import/src/Nodes/Shape.cpp new file mode 100644 index 000000000..1e112ebb0 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Shape.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 "moco/Import/Nodes/Shape.h" + +#include <moco/IR/Nodes/TFShape.h> + +#include <loco.h> +#include <stdex/Memory.h> +#include <plier/tf/Convert.h> + +namespace +{ +using namespace moco; + +/** + * @brief GraphUpdate for Shape node + */ +class ShapeGraphUpdate final : public GraphUpdate +{ +public: + ShapeGraphUpdate(TFShape *node, const TensorName &&input_name) + : _node(node), _input_name(input_name) + { + // DO NOTHING + } + + void input(const SymbolTable *) const override; + +private: + TFShape *_node; + const TensorName _input_name; +}; + +void ShapeGraphUpdate::input(const SymbolTable *table) const +{ + loco::Node *input_node = table->node(_input_name); + _node->input(input_node); +} + +} // namespace + +namespace moco +{ + +bool ShapeGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + if (node.input_size() != 1) + return false; + + return plier::tf::has_attrs(node, {"T"}); +} + +void ShapeGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // create TF dialect Shape node + auto tf_shape = graph->nodes()->create<TFShape>(); + tf_shape->name(node.name()); + + if (plier::tf::has_attrs(node, {"out_type"})) + { + auto dtype = plier::tf::as_loco_datatype(plier::tf::get_datatype_attr(node, "out_type")); + // TODO Support other dtype like S64 + assert(dtype == loco::DataType::S32); + + tf_shape->dtype(dtype); + } + else + { + // Set to S32, TF-documented default value for 'out_type' + tf_shape->dtype(loco::DataType::S32); + } + + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, tf_shape); + + auto update = stdex::make_unique<ShapeGraphUpdate>(tf_shape, TensorName(node.input(0))); + updates->enroll(std::move(update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Shape.test.cpp b/compiler/moco/import/src/Nodes/Shape.test.cpp new file mode 100644 index 000000000..4aaf66c6f --- /dev/null +++ b/compiler/moco/import/src/Nodes/Shape.test.cpp @@ -0,0 +1,65 @@ +/* + * 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/Import/Nodes/Shape.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ + +// clang-format off +const char *shape_000_pbtxtdata = STRING_CONTENT( + name: "Shape" + op: "Shape" + input: "Placeholder" + attr { + key: "T" + value { type: DT_FLOAT } + } + attr { + key: "out_type" + value { type: DT_INT32 } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, shape_000) +{ + TFNodeBuildTester tester; + moco::ShapeGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(shape_000_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFShape + // - input node should not be null + // - dtype attribute is set same as out_type attribute of pbtxt + + tester.inputs({"Placeholder"}); + tester.output("Shape"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFShape *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->dtype(), loco::DataType::S32); +} diff --git a/compiler/moco/import/src/Nodes/Softmax.cpp b/compiler/moco/import/src/Nodes/Softmax.cpp new file mode 100644 index 000000000..6f2c609ff --- /dev/null +++ b/compiler/moco/import/src/Nodes/Softmax.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 "moco/Import/Nodes/Softmax.h" + +#include <moco/IR/Nodes/TFSoftmax.h> + +#include <loco.h> +#include <stdex/Memory.h> +#include <plier/tf/Convert.h> + +namespace +{ +using namespace moco; + +/** +* @brief GraphUpdate for Softmax node +*/ +class SoftmaxGraphUpdate final : public GraphUpdate +{ +public: + SoftmaxGraphUpdate(TFSoftmax *node, const TensorName &&input_name) + : _node(node), _input_name(input_name) + { + // DO NOTHING + } + + void input(const SymbolTable *) const override; + +private: + TFSoftmax *_node; + const TensorName _input_name; +}; + +void SoftmaxGraphUpdate::input(const SymbolTable *table) const +{ + loco::Node *input_node = table->node(_input_name); + _node->logits(input_node); +} + +} // namespace + +namespace moco +{ + +bool SoftmaxGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + if (node.input_size() != 1) + return false; + + return plier::tf::has_attrs(node, {"T"}); +} + +void SoftmaxGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // creating TF dialect Softmax node + auto tf_softmax = graph->nodes()->create<TFSoftmax>(); + tf_softmax->name(node.name()); + + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, tf_softmax); + + auto update = stdex::make_unique<SoftmaxGraphUpdate>(tf_softmax, TensorName(node.input(0))); + updates->enroll(std::move(update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Softmax.test.cpp b/compiler/moco/import/src/Nodes/Softmax.test.cpp new file mode 100644 index 000000000..b7c0797bb --- /dev/null +++ b/compiler/moco/import/src/Nodes/Softmax.test.cpp @@ -0,0 +1,58 @@ +/* + * 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/Import/Nodes/Softmax.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ + +// clang-format off +const char *softmax_2d_pbtxtdata = STRING_CONTENT( + name: "Softmax" + op: "Softmax" + input: "Placeholder" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, softmax_2d) +{ + TFNodeBuildTester tester; + moco::SoftmaxGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(softmax_2d_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFSoftmax + // - logits node should not be null + + tester.inputs({"Placeholder"}); + tester.output("Softmax"); + tester.run(nodedef, graphbuilder); +} diff --git a/compiler/moco/import/src/Nodes/Sqrt.cpp b/compiler/moco/import/src/Nodes/Sqrt.cpp new file mode 100644 index 000000000..f891e48f6 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Sqrt.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 "moco/Import/Nodes/Sqrt.h" + +#include <moco/IR/Nodes/TFSqrt.h> + +#include <loco.h> +#include <stdex/Memory.h> + +namespace +{ + +using namespace moco; + +/** + * @brief GraphUpdate for TF Sqrt node + */ +class TFSqrtGraphUpdate final : public GraphUpdate +{ +public: + TFSqrtGraphUpdate(TFSqrt *node, TensorName &&name) : _node(node), _name(name) {} + + void input(const SymbolTable *) const override; + +private: + TFSqrt *_node; + TensorName _name; +}; + +void TFSqrtGraphUpdate::input(const SymbolTable *table) const +{ + loco::Node *target = table->node(_name); + _node->x(target); +} + +} // namespace + +namespace moco +{ + +bool SqrtGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + return node.input_size() == 1; +} + +void SqrtGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // creating TF dialect Sqrt node + auto tf_sqrt = graph->nodes()->create<TFSqrt>(); + tf_sqrt->name(node.name()); + + // register string-name to node + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, tf_sqrt); + + // Queue node input update + auto tf_sqrt_update = stdex::make_unique<TFSqrtGraphUpdate>(tf_sqrt, TensorName(node.input(0))); + updates->enroll(std::move(tf_sqrt_update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Sqrt.test.cpp b/compiler/moco/import/src/Nodes/Sqrt.test.cpp new file mode 100644 index 000000000..427d4df0f --- /dev/null +++ b/compiler/moco/import/src/Nodes/Sqrt.test.cpp @@ -0,0 +1,57 @@ +/* + * 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/Import/Nodes/Sqrt.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ +// clang-format off +const char *sqrt_basic_pbtxt = STRING_CONTENT( + name: "SQRT_01" + op: "Sqrt" + input: "Placeholder" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, tf_sqrt_basic) +{ + TFNodeBuildTester tester; + moco::SqrtGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(sqrt_basic_pbtxt, nodedef)); + + // what to test: + // - TFSqrt node should exist + // - input x() should not be null + + tester.inputs({"Placeholder"}); + tester.output("SQRT_01"); + tester.run(nodedef, graphbuilder); +} diff --git a/compiler/moco/import/src/Nodes/SquaredDifference.cpp b/compiler/moco/import/src/Nodes/SquaredDifference.cpp new file mode 100644 index 000000000..17a1fe93d --- /dev/null +++ b/compiler/moco/import/src/Nodes/SquaredDifference.cpp @@ -0,0 +1,92 @@ +/* + * 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/Import/Nodes/SquaredDifference.h" + +#include <moco/IR/Nodes/TFSquaredDifference.h> + +#include <loco.h> +#include <stdex/Memory.h> + +namespace +{ + +using namespace moco; + +/** + * @brief GraphUpdate for TF SquaredDifference node + */ +class TFSquaredDifferenceGraphUpdate final : public GraphUpdate +{ +public: + TFSquaredDifferenceGraphUpdate(TFSquaredDifference *node, std::vector<TensorName> names) + : _node(node), _names(names) + { + } + + void input(const SymbolTable *) const override; + +private: + TFSquaredDifference *_node; + std::vector<TensorName> _names; +}; + +void TFSquaredDifferenceGraphUpdate::input(const SymbolTable *table) const +{ + assert(_names.size() == 2); + + _node->x(table->node(_names[0])); + _node->y(table->node(_names[1])); +} + +} // namespace + +namespace moco +{ + +bool SquaredDifferenceGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + return node.input_size() == 2; +} + +void SquaredDifferenceGraphBuilder::build(const tensorflow::NodeDef &node, + GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // creating TF dialect SquaredDifference node + auto tf_sqdiff = graph->nodes()->create<TFSquaredDifference>(); + tf_sqdiff->name(node.name()); + + // register string-name to node + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, tf_sqdiff); + + std::vector<TensorName> add_input_names; + add_input_names.push_back(TensorName(node.input(0))); // x + add_input_names.push_back(TensorName(node.input(1))); // y + + // Queue node input update + auto tf_sqrt_update = + stdex::make_unique<TFSquaredDifferenceGraphUpdate>(tf_sqdiff, add_input_names); + updates->enroll(std::move(tf_sqrt_update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/SquaredDifference.test.cpp b/compiler/moco/import/src/Nodes/SquaredDifference.test.cpp new file mode 100644 index 000000000..336ab1358 --- /dev/null +++ b/compiler/moco/import/src/Nodes/SquaredDifference.test.cpp @@ -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. + */ + +#include "moco/Import/Nodes/SquaredDifference.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ +// clang-format off +const char *sqdiff_basic_pbtxt = STRING_CONTENT( + name: "squared_difference" + op: "SquaredDifference" + input: "input_01" + input: "input_02" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, tf_squdiff_basic) +{ + TFNodeBuildTester tester; + moco::SquaredDifferenceGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(sqdiff_basic_pbtxt, nodedef)); + + // what to test: + // - TFSquaredDifference node should exist + // - input x() should not be null + // - input y() should not be null + + tester.inputs({"input_01", "input_02"}); + tester.output("squared_difference"); + tester.run(nodedef, graphbuilder); +} diff --git a/compiler/moco/import/src/Nodes/Squeeze.cpp b/compiler/moco/import/src/Nodes/Squeeze.cpp new file mode 100644 index 000000000..1b4ebae6f --- /dev/null +++ b/compiler/moco/import/src/Nodes/Squeeze.cpp @@ -0,0 +1,112 @@ +/* + * 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/Import/Nodes/Squeeze.h" + +#include <moco/IR/Nodes/TFSqueeze.h> + +#include <moco/Names.h> + +#include <loco.h> +#include <stdex/Memory.h> +#include <plier/tf/Convert.h> +#include <oops/UserExn.h> + +namespace +{ +using namespace moco; + +/** + * @brief GraphUpdate for Squeeze node + */ +class SqueezeGraphUpdate final : public GraphUpdate +{ +public: + SqueezeGraphUpdate(TFSqueeze *node, const TensorName &&input_name) + : _node(node), _input_name(input_name) + { + // DO NOTHING + } + + void input(const SymbolTable *) const override; + +private: + TFSqueeze *_node; + const TensorName _input_name; +}; + +void SqueezeGraphUpdate::input(const SymbolTable *table) const +{ + loco::Node *input_node = table->node(_input_name); + _node->input(input_node); +} + +} // namespace + +namespace moco +{ + +bool SqueezeGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + if (node.input_size() != 1) + return false; + + if (!plier::tf::has_attrs(node, {"T"})) + return false; + + if (plier::tf::has_attrs(node, {"axis"})) + { + // TODO support 'axis' attribute + oops::UserExn("Squeeze: Unsupported 'axis' attribute", node.name()); + } + + return true; +} + +void SqueezeGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // TODO support 'axis' attribute + assert(!plier::tf::has_attrs(node, {"axis"})); + + std::vector<int64_t> squeeze_dims; + if (plier::tf::has_attrs(node, {"squeeze_dims"})) + { + auto squeeze_dim_list = plier::tf::get_list_attr(node, {"squeeze_dims"}); + // TODO assert squeeze_dims are mutually different? + squeeze_dims = plier::tf::as_int64_list(squeeze_dim_list); + } + // Note that it is possible that NodeDef does not have squeeze_dims attribute. + // In that case, TFSqueeze also has empty squeeze_dims, + + // creating TF dialect Squeeze node + auto tf_squeeze = graph->nodes()->create<TFSqueeze>(); + tf_squeeze->name(node.name()); + tf_squeeze->squeeze_dims(squeeze_dims); + + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, tf_squeeze); + + auto update = stdex::make_unique<SqueezeGraphUpdate>(tf_squeeze, TensorName(node.input(0))); + updates->enroll(std::move(update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Squeeze.test.cpp b/compiler/moco/import/src/Nodes/Squeeze.test.cpp new file mode 100644 index 000000000..e8188f98b --- /dev/null +++ b/compiler/moco/import/src/Nodes/Squeeze.test.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 "moco/Import/Nodes/Squeeze.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ + +// clang-format off +const char *squeeze_all_pbtxtdata = STRING_CONTENT( + name: "Squeeze" + op: "Squeeze" + input: "Placeholder" + attr { + key: "T" + value { type: DT_FLOAT } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, squeeze_all) +{ + TFNodeBuildTester tester; + moco::SqueezeGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(squeeze_all_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFSqueeze + // - input node should not be null + // - squeeze_dims attribute is set same as pbtxt + + tester.inputs({"Placeholder"}); + tester.output("Squeeze"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFSqueeze *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->squeeze_dims().size(), 0); +} + +namespace +{ + +// clang-format off +const char *squeeze_some_pbtxtdata = STRING_CONTENT( + name: "Squeeze" + op: "Squeeze" + input: "Placeholder" + attr { + key: "T" + value { type: DT_FLOAT } + } + attr { + key: "squeeze_dims" + value { + list { i: 1 } + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, squeeze_some) +{ + TFNodeBuildTester tester; + moco::SqueezeGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(squeeze_some_pbtxtdata, nodedef)); + + // what to test: + // - there should exist TFSqueeze + // - input node should not be null + // - squeeze_dims attribute is set same as pbtxt + + tester.inputs({"Placeholder"}); + tester.output("Squeeze"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFSqueeze *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->squeeze_dims().size(), 1); + ASSERT_EQ(test_node->squeeze_dims().at(0), 1); +} + +// TODO Add test case for negative squeeze dim diff --git a/compiler/moco/import/src/Nodes/StopGradient.cpp b/compiler/moco/import/src/Nodes/StopGradient.cpp new file mode 100644 index 000000000..9caec6943 --- /dev/null +++ b/compiler/moco/import/src/Nodes/StopGradient.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 "moco/Import/Nodes/StopGradient.h" + +#include <moco/IR/Nodes/TFStopGradient.h> + +#include <loco.h> +#include <plier/tf/Convert.h> +#include <stdex/Memory.h> + +namespace +{ + +using namespace moco; + +/** + * @brief GraphUpdate for TF StopGradient node + */ +class TFStopGradientGraphUpdate final : public GraphUpdate +{ +public: + TFStopGradientGraphUpdate(TFStopGradient *node, TensorName &&name) : _node(node), _name(name) {} + + void input(const SymbolTable *) const override; + +private: + TFStopGradient *_node; + TensorName _name; +}; + +void TFStopGradientGraphUpdate::input(const SymbolTable *table) const +{ + loco::Node *target = table->node(_name); + _node->input(target); +} + +} // namespace + +namespace moco +{ + +bool StopGradientGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + if (node.input_size() != 1) + return false; + + return plier::tf::has_attrs(node, {"T"}); +} + +void StopGradientGraphBuilder::build(const tensorflow::NodeDef &node, + GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // creating TF dialect StopGradient node + auto tf_stopgradient = graph->nodes()->create<TFStopGradient>(); + tf_stopgradient->name(node.name()); + + // register string-name to node + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, tf_stopgradient); + + // Queue node input update + auto tf_stopgradient_update = + stdex::make_unique<TFStopGradientGraphUpdate>(tf_stopgradient, TensorName(node.input(0))); + updates->enroll(std::move(tf_stopgradient_update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/StopGradient.test.cpp b/compiler/moco/import/src/Nodes/StopGradient.test.cpp new file mode 100644 index 000000000..0bf70ebcc --- /dev/null +++ b/compiler/moco/import/src/Nodes/StopGradient.test.cpp @@ -0,0 +1,57 @@ +/* + * 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/Import/Nodes/StopGradient.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ +// clang-format off +const char *stopgradient_basic_pbtxt = STRING_CONTENT( + name: "StopGradient_01" + op: "StopGradient" + input: "Placeholder" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, tf_stopgradient_basic) +{ + TFNodeBuildTester tester; + moco::StopGradientGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(stopgradient_basic_pbtxt, nodedef)); + + // what to test: + // - TFStopGradient node should exist + // - input() should not be null + + tester.inputs({"Placeholder"}); + tester.output("StopGradient_01"); + tester.run(nodedef, graphbuilder); +} diff --git a/compiler/moco/import/src/Nodes/StridedSlice.cpp b/compiler/moco/import/src/Nodes/StridedSlice.cpp new file mode 100644 index 000000000..06d388be0 --- /dev/null +++ b/compiler/moco/import/src/Nodes/StridedSlice.cpp @@ -0,0 +1,187 @@ +/* + * 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/Import/Nodes/StridedSlice.h" + +#include <moco/IR/Nodes/TFStridedSlice.h> +#include <moco/IR/Nodes/TFConst.h> + +#include <moco/Names.h> + +#include "Convert.h" + +#include <loco.h> +#include <stdex/Memory.h> +#include <plier/tf/Convert.h> +#include <oops/UserExn.h> + +namespace +{ +using namespace moco; + +class TFStridedSliceGraphUpdate final : public GraphUpdate +{ +public: + TFStridedSliceGraphUpdate(TFStridedSlice *node, std::vector<TensorName> names) + : _node(node), _names(names) + { + } + + void input(const SymbolTable *) const override; + +private: + TFStridedSlice *_node; + std::vector<TensorName> _names; +}; + +void TFStridedSliceGraphUpdate::input(const SymbolTable *node_table) const +{ + // TODO support size 3 where strides is None + assert(_names.size() == 4); + + auto input_node = node_table->node(_names[0]); + auto begin_node = node_table->node(_names[1]); + auto end_node = node_table->node(_names[2]); + auto strides_node = node_table->node(_names[3]); + assert(input_node != nullptr); + assert(begin_node != nullptr); + assert(end_node != nullptr); + assert(strides_node != nullptr); + + _node->input(input_node); + _node->begin(begin_node); + _node->end(end_node); + _node->strides(strides_node); + + // TODO move validation codes to some suitable place + // Run basic validation + + // TODO support full mask features + if (_node->begin_mask() != 0 || _node->end_mask() != 0 || _node->ellipsis_mask() != 0 || + _node->new_axis_mask() != 0 || _node->shrink_axis_mask() != 1) + { + throw oops::UserExn("Mask attributes are not supported for now: ", _node->name()); + } + + // Only Const are supported for now + auto const_input = dynamic_cast<moco::TFConst *>(_node->input()); + auto const_begin = dynamic_cast<moco::TFConst *>(_node->begin()); + auto const_end = dynamic_cast<moco::TFConst *>(_node->end()); + auto const_strides = dynamic_cast<moco::TFConst *>(_node->strides()); + if (const_input == nullptr || const_begin == nullptr || const_end == nullptr || + const_strides == nullptr) + { + throw oops::UserExn("Only Const inputs are supported for now: ", _node->name()); + } + + // TODO support S64 + if (const_begin->dtype() != loco::DataType::S32 || const_end->dtype() != loco::DataType::S32 || + const_strides->dtype() != loco::DataType::S32) + { + throw oops::UserExn("Only Const types of INT32 are supported for begin/end/strides for now: ", + _node->name()); + } + + // Input Rank should match number of elements of the begin/end/strides + auto rin = const_input->rank(); + if (rin != const_begin->size<loco::DataType::S32>() || + rin != const_end->size<loco::DataType::S32>() || + rin != const_strides->size<loco::DataType::S32>()) + { + throw oops::UserExn("Ranks for inputs should be same: ", _node->name()); + } + + // TODO support strides type of S64 + // TODO support other strides value + // Only support stride 1 for now + uint32_t elements = const_strides->size<loco::DataType::S32>(); + for (uint32_t e = 0; e < elements; ++e) + { + if (const_strides->at<loco::DataType::S32>(e) != 1) + { + throw oops::UserExn("Only stride 1 is supported for now: ", _node->name()); + } + } +} + +} // namespace + +namespace moco +{ + +bool StridedSliceGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + // TODO support node.input_size() == 3 where strides is None + if (node.input_size() != 4) + return false; + + if (!plier::tf::has_attrs(node, {"T", "Index", "begin_mask", "end_mask", "ellipsis_mask", + "new_axis_mask", "shrink_axis_mask"})) + return false; + + return true; +} + +void StridedSliceGraphBuilder::build(const tensorflow::NodeDef &node, + GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + std::string node_name = node.name(); + + auto stridedslice = graph->nodes()->create<TFStridedSlice>(); + stridedslice->name(node_name); + + // read attributes + auto begin_mask = plier::tf::get_int_attr(node, "begin_mask"); + auto end_mask = plier::tf::get_int_attr(node, "end_mask"); + auto ellipsis_mask = plier::tf::get_int_attr(node, "ellipsis_mask"); + auto new_axis_mask = plier::tf::get_int_attr(node, "new_axis_mask"); + auto shrink_axis_mask = plier::tf::get_int_attr(node, "shrink_axis_mask"); + + stridedslice->begin_mask(begin_mask); + stridedslice->end_mask(end_mask); + stridedslice->ellipsis_mask(ellipsis_mask); + stridedslice->new_axis_mask(new_axis_mask); + stridedslice->shrink_axis_mask(shrink_axis_mask); + + // TODO support general mask values: we support only this limited case for now + assert(begin_mask == 0); + assert(end_mask == 0); + assert(ellipsis_mask == 0); + assert(new_axis_mask == 0); + assert(shrink_axis_mask == 1); + + // save the name for graph link updates + TensorName output_name(node_name, 0); + tensor_names->enroll(output_name, stridedslice); + + std::vector<TensorName> input_names; + input_names.push_back(TensorName(node.input(0))); // input + input_names.push_back(TensorName(node.input(1))); // begin + input_names.push_back(TensorName(node.input(2))); // end + input_names.push_back(TensorName(node.input(3))); // strides + + auto tfconv2d_update = stdex::make_unique<TFStridedSliceGraphUpdate>(stridedslice, input_names); + + updates->enroll(std::move(tfconv2d_update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/StridedSlice.test.cpp b/compiler/moco/import/src/Nodes/StridedSlice.test.cpp new file mode 100644 index 000000000..b6959d7ab --- /dev/null +++ b/compiler/moco/import/src/Nodes/StridedSlice.test.cpp @@ -0,0 +1,107 @@ +/* + * 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/Import/Nodes/StridedSlice.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ +// clang-format off +const char *stridedslice_basic_pbtxt = STRING_CONTENT( + name: "StridedSlice" + op: "StridedSlice" + input: "input" + input: "begin" + input: "end" + input: "strides" + attr { + key: "Index" + value { + type: DT_INT32 + } + } + attr { + key: "T" + value { + type: DT_INT32 + } + } + attr { + key: "begin_mask" + value { + i: 0 + } + } + attr { + key: "ellipsis_mask" + value { + i: 0 + } + } + attr { + key: "end_mask" + value { + i: 0 + } + } + attr { + key: "new_axis_mask" + value { + i: 0 + } + } + attr { + key: "shrink_axis_mask" + value { + i: 1 + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, tf_stridedslice_basic) +{ + TFNodeBuildTester tester; + moco::StridedSliceGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(stridedslice_basic_pbtxt, nodedef)); + + // what to test: + // - TFStridedSlice node should exist + // - inputs should not be nullptr + // - attributes should match the values + + tester.inputs({"input", "begin", "end", "strides"}, loco::DataType::S32); + tester.output("StridedSlice"); + tester.run(nodedef, graphbuilder); + + auto test_node = dynamic_cast<moco::TFStridedSlice *>(tester.output()); + ASSERT_NE(test_node, nullptr); + ASSERT_EQ(test_node->begin_mask(), 0); + ASSERT_EQ(test_node->end_mask(), 0); + ASSERT_EQ(test_node->ellipsis_mask(), 0); + ASSERT_EQ(test_node->new_axis_mask(), 0); + ASSERT_EQ(test_node->shrink_axis_mask(), 1); +} + +// TODO add test where strides is None diff --git a/compiler/moco/import/src/Nodes/Sub.cpp b/compiler/moco/import/src/Nodes/Sub.cpp new file mode 100644 index 000000000..bdad81d67 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Sub.cpp @@ -0,0 +1,85 @@ +/* + * 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/Import/Nodes/Sub.h" + +#include <moco/IR/Nodes/TFSub.h> + +#include <loco.h> +#include <stdex/Memory.h> + +namespace +{ + +using namespace moco; + +/** + * @brief GraphUpdate for TF Sub node + */ +class TFSubGraphUpdate final : public GraphUpdate +{ +public: + TFSubGraphUpdate(TFSub *node, std::vector<TensorName> names) : _node(node), _names(names) {} + + void input(const SymbolTable *) const override; + +private: + TFSub *_node; + std::vector<TensorName> _names; +}; + +void TFSubGraphUpdate::input(const SymbolTable *tensor_names) const +{ + assert(_names.size() == 2); + + _node->x(tensor_names->node(_names[0])); + _node->y(tensor_names->node(_names[1])); +} + +} // namespace + +namespace moco +{ + +bool SubGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + return node.input_size() == 2; +} + +void SubGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // creating TF dialect Sub node + auto tf_sub = graph->nodes()->create<TFSub>(); + tf_sub->name(node.name()); + + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, tf_sub); + + std::vector<TensorName> sub_input_names; + sub_input_names.push_back(TensorName(node.input(0))); // x + sub_input_names.push_back(TensorName(node.input(1))); // y + + auto tf_sub_update = stdex::make_unique<TFSubGraphUpdate>(tf_sub, sub_input_names); + updates->enroll(std::move(tf_sub_update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Sub.test.cpp b/compiler/moco/import/src/Nodes/Sub.test.cpp new file mode 100644 index 000000000..05f1fb0d6 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Sub.test.cpp @@ -0,0 +1,58 @@ +/* + * 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/Import/Nodes/Sub.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ +// clang-format off +const char *sub_basic_pbtxt = STRING_CONTENT( + name: "SUB_01" + op: "Sub" + input: "input_01" + input: "input_02" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, tf_sub_basic) +{ + TFNodeBuildTester tester; + moco::SubGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(sub_basic_pbtxt, nodedef)); + + // what to test: + // - TFSub node should exist + // - both inputs x() and y() should not be null + + tester.inputs({"input_01", "input_02"}); + tester.output("SUB_01"); + tester.run(nodedef, graphbuilder); +} diff --git a/compiler/moco/import/src/Nodes/Tanh.cpp b/compiler/moco/import/src/Nodes/Tanh.cpp new file mode 100644 index 000000000..c89fa862a --- /dev/null +++ b/compiler/moco/import/src/Nodes/Tanh.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 "moco/Import/Nodes/Tanh.h" + +#include <moco/IR/Nodes/TFTanh.h> + +#include <loco.h> +#include <stdex/Memory.h> + +namespace +{ + +using namespace moco; + +/** + * @brief GraphUpdate for TF Tanh node + */ +class TFTanhGraphUpdate final : public GraphUpdate +{ +public: + TFTanhGraphUpdate(TFTanh *node, TensorName &&name) : _node(node), _name(name) {} + + void input(const SymbolTable *) const override; + +private: + TFTanh *_node; + TensorName _name; +}; + +void TFTanhGraphUpdate::input(const SymbolTable *table) const +{ + loco::Node *target = table->node(_name); + _node->x(target); +} + +} // namespace + +namespace moco +{ + +bool TanhGraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + return node.input_size() == 1; +} + +void TanhGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // creating TF dialect Tanh node + auto tf_tanh = graph->nodes()->create<TFTanh>(); + tf_tanh->name(node.name()); + + // register string-name to node + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, tf_tanh); + + // Queue node input update + auto tf_tanh_update = stdex::make_unique<TFTanhGraphUpdate>(tf_tanh, TensorName(node.input(0))); + updates->enroll(std::move(tf_tanh_update)); +} + +} // namespace moco diff --git a/compiler/moco/import/src/Nodes/Tanh.test.cpp b/compiler/moco/import/src/Nodes/Tanh.test.cpp new file mode 100644 index 000000000..20ebd15b2 --- /dev/null +++ b/compiler/moco/import/src/Nodes/Tanh.test.cpp @@ -0,0 +1,57 @@ +/* + * 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/Import/Nodes/Tanh.h" +#include "TestHelper.h" + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ +// clang-format off +const char *tanh_basic_pbtxt = STRING_CONTENT( + name: "output/tanh" + op: "Tanh" + input: "Placeholder" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, tf_tanh_basic) +{ + TFNodeBuildTester tester; + moco::TanhGraphBuilder graphbuilder; + tensorflow::NodeDef nodedef; + + EXPECT_TRUE(plier::tf::parse_nodedef(tanh_basic_pbtxt, nodedef)); + + // what to test: + // - TFTanh node should exist + // - input x() should not be null + + tester.inputs({"Placeholder"}); + tester.output("output/tanh"); + tester.run(nodedef, graphbuilder); +} diff --git a/compiler/moco/import/src/TestHelper.h b/compiler/moco/import/src/TestHelper.h new file mode 100644 index 000000000..54ca45b4a --- /dev/null +++ b/compiler/moco/import/src/TestHelper.h @@ -0,0 +1,83 @@ +/* + * 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 "moco/Import/GraphBuilder.h" + +#include <moco/IR/TFNode.h> +#include <loco.h> +#include <plier/tf/TestHelper.h> + +#include <tensorflow/core/framework/graph.pb.h> + +#define STRING_CONTENT(content) #content + +namespace moco +{ +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; +} + +} // namespace test +} // namespace moco + +namespace moco +{ +namespace test +{ + +class TFNodeBuildTester +{ +public: + TFNodeBuildTester(); + +public: + void inputs(const std::vector<std::string> &names); + void inputs(const std::vector<std::string> &names, const loco::DataType dtype); + 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 moco + +#endif // __TEST_HELPER_H__ diff --git a/compiler/moco/import/src/TestHelper.test.cpp b/compiler/moco/import/src/TestHelper.test.cpp new file mode 100644 index 000000000..06c3dd372 --- /dev/null +++ b/compiler/moco/import/src/TestHelper.test.cpp @@ -0,0 +1,101 @@ +/* + * 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 <moco/IR/Nodes/TFConst.h> +#include <stdex/Memory.h> + +#include <gtest/gtest.h> + +namespace moco +{ +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::inputs(const std::vector<std::string> &names, const loco::DataType dtype) +{ + for (auto name : names) + { + auto input = _graph->nodes()->create<moco::TFConst>(); + input->dtype(dtype); + 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); + ASSERT_STREQ(tfnode->name().c_str(), _output); + + int idx = 0; + ASSERT_EQ(tfnode->arity(), _inputs.size()); + for (auto input : _inputs) + { + ASSERT_EQ(tfnode->arg(idx++), input); + } +} + +} // namespace test +} // namespace moco diff --git a/compiler/moco/lang/CMakeLists.txt b/compiler/moco/lang/CMakeLists.txt new file mode 100644 index 000000000..a64fdf92a --- /dev/null +++ b/compiler/moco/lang/CMakeLists.txt @@ -0,0 +1,21 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(moco_lang SHARED ${SOURCES}) +target_include_directories(moco_lang PRIVATE src) +target_include_directories(moco_lang PUBLIC include) +target_link_libraries(moco_lang PUBLIC loco) +target_link_libraries(moco_lang PRIVATE nncc_common) +target_link_libraries(moco_lang PRIVATE stdex) +install(TARGETS moco_lang DESTINATION lib) # moco_tf_frontend requires moco_lang + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(moco_lang_test ${TESTS}) +target_include_directories(moco_lang_test PRIVATE src) +target_link_libraries(moco_lang_test moco_lang) diff --git a/compiler/moco/lang/README.md b/compiler/moco/lang/README.md new file mode 100644 index 000000000..6ee3fc660 --- /dev/null +++ b/compiler/moco/lang/README.md @@ -0,0 +1,3 @@ +# lang + +`lang` provides TensorFlow Dialect IR diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFAdd.h b/compiler/moco/lang/include/moco/IR/Nodes/TFAdd.h new file mode 100644 index 000000000..13b064fba --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFAdd.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_IR_TFADD_H__ +#define __MOCO_IR_TFADD_H__ + +#include "moco/IR/TFNodeDecl.h" + +namespace moco +{ + +/// @note TFAdd corresponds to the following GraphDef +/* +node { + name: "add" + op: "Add" + input: "x" + input: "y" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +*/ + +class TFAdd final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Add>> +{ +public: + TFAdd() = default; + +public: + Node *x(void) const { return at(0)->node(); } + void x(Node *node) { at(0)->node(node); } + + Node *y(void) const { return at(1)->node(); } + void y(Node *node) { at(1)->node(node); } +}; + +} // namespace moco + +#endif // __MOCO_IR_TFADD_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFAvgPool.h b/compiler/moco/lang/include/moco/IR/Nodes/TFAvgPool.h new file mode 100644 index 000000000..74c91b5fb --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFAvgPool.h @@ -0,0 +1,101 @@ +/* + * 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_IR_TFAVGPOOL_H__ +#define __MOCO_IR_TFAVGPOOL_H__ + +#include "moco/IR/TFNodeDecl.h" + +#include <vector> + +namespace moco +{ + +/// @note TFAvgPool corresponds to the following GraphDef +/* +node { + name: "avgpool" + op: "AvgPool" + input: "placeholder" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "data_format" + value { + s: "NHWC" + } + } + attr { + key: "ksize" + value { + list { + i: 1 i: 3 i: 3 i: 1 + } + } + } + attr { + key: "padding" + value { + s: "SAME" + } + } + attr { + key: "strides" + value { + list { + i: 1 i: 1 i: 1 i: 1 + } + } + } +} +*/ + +class TFAvgPool final : public FixedArityNode<1, TFNodeImpl<TFOpcode::AvgPool>> +{ +public: + TFAvgPool() = default; + +public: + Node *value(void) const { return at(0)->node(); } + void value(Node *node) { return at(0)->node(node); } + +public: + const TFDataLayout &data_layout(void) const { return _data_layout; } + void data_layout(const TFDataLayout &data_layout) { _data_layout = data_layout; } + + const TFPadding &padding(void) const { return _padding; } + void padding(const TFPadding &padding) { _padding = padding; } + + const std::vector<int64_t> &ksize(void) const { return _ksize; } + void ksize(const std::vector<int64_t> &ksize) { _ksize = ksize; } + + const std::vector<int64_t> &strides(void) const { return _strides; } + void strides(const std::vector<int64_t> &strides) { _strides = strides; } + +private: + TFDataLayout _data_layout; + TFPadding _padding; + std::vector<int64_t> _ksize; + std::vector<int64_t> _strides; +}; + +} // namespace moco + +#endif // __MOCO_IR_TFAVGPOOL_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFBiasAdd.h b/compiler/moco/lang/include/moco/IR/Nodes/TFBiasAdd.h new file mode 100644 index 000000000..11e309caa --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFBiasAdd.h @@ -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. + */ + +#ifndef __MOCO_IR_TFBIASADD_H__ +#define __MOCO_IR_TFBIASADD_H__ + +#include "moco/IR/TFNodeDecl.h" + +namespace moco +{ + +/// @note TFBiasAdd corresponds to the following GraphDef +/* +node { + name: "bias_add_01" + op: "BiasAdd" + input: "input_01" + input: "bias_add_01/bias" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "data_format" + value { + s: "NHWC" + } + } +} +*/ + +class TFBiasAdd final : public FixedArityNode<2, TFNodeImpl<TFOpcode::BiasAdd>> +{ +public: + TFBiasAdd() = default; + +public: + Node *value(void) const { return at(0)->node(); } + void value(Node *node) { return at(0)->node(node); } + + Node *bias(void) const { return at(1)->node(); } + void bias(Node *node) { return at(1)->node(node); } + + const TFDataLayout data_layout(void) const { return _data_layout; } + void data_layout(const TFDataLayout &data_layout) { _data_layout = data_layout; } + +private: + TFDataLayout _data_layout; +}; + +} // namespace moco + +#endif // __MOCO_IR_TFBIASADD_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFConcatV2.h b/compiler/moco/lang/include/moco/IR/Nodes/TFConcatV2.h new file mode 100644 index 000000000..7f0d32697 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFConcatV2.h @@ -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. + */ + +#ifndef __MOCO_IR_TFCONCATV2_H__ +#define __MOCO_IR_TFCONCATV2_H__ + +#include "moco/IR/TFNodeDecl.h" +#include "moco/IR/VariadicArityNode.h" + +namespace moco +{ + +/// @note TFConcatV2 corresponds to the following GraphDef +/* +node { + name: "Concat" + op: "ConcatV2" + input: "Input01" + input: "Input02" + input: "Axis" + attr { + key: "N" + value { + i: 2 + } + } + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "Tidx" + value { + type: DT_INT32 + } + } +} +*/ + +class TFConcatV2 final : public VariadicArityNode<TFNodeImpl<TFOpcode::ConcatV2>> +{ +public: + TFConcatV2(uint32_t arity) : VariadicArityNode<TFNodeImpl<TFOpcode::ConcatV2>>(arity + 1) + { + // we add +1 for axis of VariadicArityNode ctor + // at least one value is required + assert(arity >= 1); + } + +public: + uint32_t num_values(void) const + { + // last one is for axis + return arity() - 1; + } + +public: + Node *values(uint32_t index) const + { + assert(index < num_values()); + return at(index)->node(); + } + void values(uint32_t index, Node *node) + { + assert(index < num_values()); + at(index)->node(node); + } + + Node *axis(void) const { return at(num_values())->node(); } + void axis(Node *node) { at(num_values())->node(node); } +}; + +} // namespace moco + +#endif // __MOCO_IR_TFCONCATV2_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFConst.h b/compiler/moco/lang/include/moco/IR/Nodes/TFConst.h new file mode 100644 index 000000000..7c2595fcb --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFConst.h @@ -0,0 +1,94 @@ +/* + * 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_IR_TFCONSTANT_H__ +#define __MOCO_IR_TFCONSTANT_H__ + +#include "moco/IR/TFNodeDecl.h" + +#include <loco/IR/DataTypeTraits.h> +#include <loco/IR/NodeMixins.h> +#include <loco/IR/TensorShape.h> + +#include <vector> + +namespace moco +{ + +/// @note TFConst corresponds to the following GraphDef +/* +node { + name: "val" + op: "Const" + attr { + key: "dtype" + value { type: DT_FLOAT } + } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { + dim { size: 1 } + dim { size: 3 } + dim { size: 4 } + dim { size: 4 } + } + float_val: 2.1 + } + } + } +} +*/ + +/** + * @brief IR for tf.constant + * + * @note Implementation for this class came from Canonical ConstGen + * Read comments in loco::ConstGen for details + */ +class TFConst final : public FixedArityNode<0, TFNodeImpl<TFOpcode::Const>>, + public loco::NodeMixin<loco::NodeTrait::DataType>, + public loco::NodeMixin<loco::NodeTrait::TensorShape> +{ +public: + TFConst() = default; + +public: + template <loco::DataType DT> uint32_t size(void) const; + template <loco::DataType DT> void size(uint32_t size); + + template <loco::DataType DT> const typename loco::DataTypeImpl<DT>::Type &at(uint32_t n) const; + template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &at(uint32_t n); + +private: + std::vector<uint8_t> _data; +}; + +} // namespace moco + +namespace moco +{ + +loco::TensorShape tensor_shape(const TFConst *node); + +uint32_t num_elements(const TFConst *tfconst); +bool same_shape(const TFConst *lhs, const TFConst *rhs); + +} // namespace moco + +#endif // __MOCO_IR_TFCONSTANT_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFConv2D.h b/compiler/moco/lang/include/moco/IR/Nodes/TFConv2D.h new file mode 100644 index 000000000..0d5a17879 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFConv2D.h @@ -0,0 +1,55 @@ +/* + * 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_IR_TFCONV2D_H__ +#define __MOCO_IR_TFCONV2D_H__ + +#include "moco/IR/TFNodeDecl.h" + +#include <vector> + +namespace moco +{ + +class TFConv2D final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Conv2D>> +{ +public: + loco::Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + + loco::Node *filter(void) const { return at(1)->node(); } + void filter(Node *node) { at(1)->node(node); } + +public: + const TFPadding &padding(void) const { return _padding; } + void padding(const TFPadding &padding) { _padding = padding; } + + const TFDataLayout &data_layout(void) const { return _data_layout; } + void data_layout(const TFDataLayout &data_layout) { _data_layout = data_layout; } + + const std::vector<int64_t> &strides(void) const { return _strides; } + void strides(const std::vector<int64_t> &strides) { _strides = strides; } + +private: + TFPadding _padding; + TFDataLayout _data_layout; + std::vector<int64_t> _strides; + // TODO Support "Dilation" +}; + +} // namespace moco + +#endif // __MOCO_IR_TFCONV2D_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFConv2DBackpropInput.h b/compiler/moco/lang/include/moco/IR/Nodes/TFConv2DBackpropInput.h new file mode 100644 index 000000000..43e620d24 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFConv2DBackpropInput.h @@ -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. + */ + +#ifndef __MOCO_IR_TFCONV2DBACKPROPINPUT_H__ +#define __MOCO_IR_TFCONV2DBACKPROPINPUT_H__ + +#include "moco/IR/TFNodeDecl.h" + +#include <vector> + +namespace moco +{ + +/// @note TFConv2DBackpropInput corresponds to the following GraphDef +/* +node { + name: "conv2d_backprop_input" + op: "Conv2DBackpropInput" + input: "input_sizes" + input: "filter" + input: "out_backprop" + attr { + key: "T" + value { type: DT_FLOAT } + } + attr { + key: "data_format" + value { s: "NHWC" } + } + attr { + key: "dilations" + value { + list { i: 1 i: 1 i: 1 i: 1 } + } + } + attr { + key: "padding" + value { s: "SAME" } + } + attr { + key: "strides" + value { + list { i: 1 i: 2 i: 2 i: 1 } + } + } +} +*/ + +/** + * @note For Tensorflow Conv2DBackpropInput, 'input' refers actual output of the + * node, and 'input' refers actual input. The reasone of this is, as name + * suggests, because it is inspired from backpropagation of convolution. + * For example, 'out_backprop' of Conv2DBackpropInput is its actual input + * feature map, and 'input_sizes' means desired output node's size. + * Note that this convention is against loco canonical's convention. + */ +class TFConv2DBackpropInput final + : public FixedArityNode<3, TFNodeImpl<TFOpcode::Conv2DBackpropInput>> +{ +public: + loco::Node *input_sizes(void) const { return at(0)->node(); } + void input_sizes(Node *node) { at(0)->node(node); } + + loco::Node *filter(void) const { return at(1)->node(); } + void filter(Node *node) { at(1)->node(node); } + + loco::Node *out_backprop(void) const { return at(2)->node(); } + void out_backprop(Node *node) { at(2)->node(node); } + +public: + const TFPadding &padding(void) const { return _padding; } + void padding(const TFPadding &padding) { _padding = padding; } + + const TFDataLayout &data_layout(void) const { return _data_layout; } + void data_layout(const TFDataLayout &data_layout) { _data_layout = data_layout; } + + const std::vector<int64_t> &strides(void) const { return _strides; } + void strides(const std::vector<int64_t> &strides) { _strides = strides; } + +private: + TFPadding _padding; + TFDataLayout _data_layout; + std::vector<int64_t> _strides; + // TODO Support "Dilation" +}; + +} // namespace moco + +#endif // __MOCO_IR_TFCONV2DBACKPROPINPUT_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFDepthwiseConv2dNative.h b/compiler/moco/lang/include/moco/IR/Nodes/TFDepthwiseConv2dNative.h new file mode 100644 index 000000000..aefc0b5d9 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFDepthwiseConv2dNative.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_IR_TFDEPTHWISECONV2DNATIVE_H__ +#define __MOCO_IR_TFDEPTHWISECONV2DNATIVE_H__ + +#include "moco/IR/TFNodeDecl.h" + +#include <vector> + +namespace moco +{ + +class TFDepthwiseConv2dNative final + : public FixedArityNode<2, TFNodeImpl<TFOpcode::DepthwiseConv2dNative>> +{ +public: + loco::Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + + loco::Node *filter(void) const { return at(1)->node(); } + void filter(Node *node) { at(1)->node(node); } + +public: + const TFPadding &padding(void) const { return _padding; } + void padding(const TFPadding &padding) { _padding = padding; } + + const TFDataLayout &data_layout(void) const { return _data_layout; } + void data_layout(const TFDataLayout &data_layout) { _data_layout = data_layout; } + + const std::vector<int64_t> &strides(void) const { return _strides; } + void strides(const std::vector<int64_t> &strides) { _strides = strides; } + +private: + TFPadding _padding; + TFDataLayout _data_layout; + std::vector<int64_t> _strides; + // TODO Support "Dilation" +}; + +} // namespace moco + +#endif // __MOCO_IR_TFDEPTHWISECONV2DNATIVE_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFFakeQuantWithMinMaxVars.h b/compiler/moco/lang/include/moco/IR/Nodes/TFFakeQuantWithMinMaxVars.h new file mode 100644 index 000000000..ec54da596 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFFakeQuantWithMinMaxVars.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_IR_TFFAKEQUANTWITHMINMAXVARS_H__ +#define __MOCO_IR_TFFAKEQUANTWITHMINMAXVARS_H__ + +#include "moco/IR/TFNodeDecl.h" + +#include <vector> + +namespace moco +{ + +class TFFakeQuantWithMinMaxVars final + : public FixedArityNode<3, TFNodeImpl<TFOpcode::FakeQuantWithMinMaxVars>> +{ +public: + loco::Node *inputs(void) const { return at(0)->node(); } + void inputs(Node *node) { at(0)->node(node); } + + loco::Node *min(void) const { return at(1)->node(); } + void min(Node *node) { at(1)->node(node); } + + loco::Node *max(void) const { return at(2)->node(); } + void max(Node *node) { at(2)->node(node); } + +public: + const int64_t &num_bits(void) const { return _num_bits; } + void num_bits(const int64_t &num_bits) { _num_bits = num_bits; } + + const bool &narrow_range(void) const { return _narrow_range; } + void narrow_range(const bool &narrow_range) { _narrow_range = narrow_range; } + +private: + int64_t _num_bits{8}; + bool _narrow_range{false}; +}; + +} // namespace moco + +#endif // __MOCO_IR_TFFAKEQUANTWITHMINMAXVARS_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFFusedBatchNorm.h b/compiler/moco/lang/include/moco/IR/Nodes/TFFusedBatchNorm.h new file mode 100644 index 000000000..5b980e3b2 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFFusedBatchNorm.h @@ -0,0 +1,55 @@ +/* + * 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_IR_TFFUSEDBATCHNORM_H__ +#define __MOCO_IR_TFFUSEDBATCHNORM_H__ + +#include "moco/IR/TFNodeDecl.h" + +namespace moco +{ + +class TFFusedBatchNorm final : public FixedArityNode<5, TFNodeImpl<TFOpcode::FusedBatchNorm>> +{ +public: + TFFusedBatchNorm() = default; + +public: + Node *x(void) const { return at(0)->node(); } + void x(Node *node) { at(0)->node(node); } + + Node *scale(void) const { return at(1)->node(); } // gamma + void scale(Node *node) { at(1)->node(node); } + + Node *offset(void) const { return at(2)->node(); } // beta + void offset(Node *node) { at(2)->node(node); } + + Node *mean(void) const { return at(3)->node(); } + void mean(Node *node) { at(3)->node(node); } + + Node *variance(void) const { return at(4)->node(); } + void variance(Node *node) { at(4)->node(node); } + + float epsilon(void) const { return _epsilon; } + void epsilon(float epsilon) { _epsilon = epsilon; } + +private: + float _epsilon = 0.001f; +}; + +} // namespace moco + +#endif // __MOCO_IR_TFFUSEDBATCHNORM_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFIdentity.h b/compiler/moco/lang/include/moco/IR/Nodes/TFIdentity.h new file mode 100644 index 000000000..26a1a36bf --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFIdentity.h @@ -0,0 +1,52 @@ +/* + * 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_IR_TFIDENTITY_H__ +#define __MOCO_IR_TFIDENTITY_H__ + +#include "moco/IR/TFNodeDecl.h" + +namespace moco +{ + +/// @note TFIdentity corresponds to the following GraphDef +/* +node { + name: "identity" + op: "Identity" + input: "Placeholder" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +*/ + +class TFIdentity final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Identity>> +{ +public: + TFIdentity() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } +}; + +} // namespace moco + +#endif // __MOCO_IR_TFIDENTITY_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFMaxPool.h b/compiler/moco/lang/include/moco/IR/Nodes/TFMaxPool.h new file mode 100644 index 000000000..a66b4044e --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFMaxPool.h @@ -0,0 +1,101 @@ +/* + * 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_IR_TFMAXPOOL_H__ +#define __MOCO_IR_TFMAXPOOL_H__ + +#include "moco/IR/TFNodeDecl.h" + +#include <vector> + +namespace moco +{ + +/// @note TFMaxPool corresponds to the following GraphDef +/* +node { + name: "maxpool2d" + op: "MaxPool" + input: "placeholder" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "data_format" + value { + s: "NHWC" + } + } + attr { + key: "ksize" + value { + list { + i: 1 i: 2 i: 2 i: 1 + } + } + } + attr { + key: "padding" + value { + s: "VALID" + } + } + attr { + key: "strides" + value { + list { + i: 1 i: 1 i: 1 i: 1 + } + } + } +} +*/ + +class TFMaxPool final : public FixedArityNode<1, TFNodeImpl<TFOpcode::MaxPool>> +{ +public: + TFMaxPool() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { return at(0)->node(node); } + +public: + const TFDataLayout &data_layout(void) const { return _data_layout; } + void data_layout(const TFDataLayout &data_layout) { _data_layout = data_layout; } + + const TFPadding &padding(void) const { return _padding; } + void padding(const TFPadding &padding) { _padding = padding; } + + const std::vector<int64_t> &ksize(void) const { return _ksize; } + void ksize(const std::vector<int64_t> &ksize) { _ksize = ksize; } + + const std::vector<int64_t> &strides(void) const { return _strides; } + void strides(const std::vector<int64_t> &strides) { _strides = strides; } + +private: + TFDataLayout _data_layout; + TFPadding _padding; + std::vector<int64_t> _ksize; + std::vector<int64_t> _strides; +}; + +} // namespace moco + +#endif // __MOCO_IR_TFMAXPOOL_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFMaximum.h b/compiler/moco/lang/include/moco/IR/Nodes/TFMaximum.h new file mode 100644 index 000000000..346dbebe8 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFMaximum.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_IR_TFMAXIMUM_H__ +#define __MOCO_IR_TFMAXIMUM_H__ + +#include "moco/IR/TFNodeDecl.h" + +namespace moco +{ + +/// @note TFMaximum corresponds to the following GraphDef +/* +node { + name: "maximum" + op: "Maximum" + input: "x" + input: "y" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +*/ + +class TFMaximum final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Maximum>> +{ +public: + TFMaximum() = default; + +public: + Node *x(void) const { return at(0)->node(); } + void x(Node *node) { at(0)->node(node); } + + Node *y(void) const { return at(1)->node(); } + void y(Node *node) { at(1)->node(node); } +}; + +} // namespace moco + +#endif // __MOCO_IR_TFMAXIMUM_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFMean.h b/compiler/moco/lang/include/moco/IR/Nodes/TFMean.h new file mode 100644 index 000000000..abcd21c49 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFMean.h @@ -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. + */ + +#ifndef __MOCO_IR_TFMEAN_H__ +#define __MOCO_IR_TFMEAN_H__ + +#include "moco/IR/TFNodeDecl.h" + +#include <vector> + +namespace moco +{ + +/// @note TFMean corresponds to the following GraphDef +/* +node { + name: "Mean" + op: "Mean" + input: "Placeholder" + input: "Mean/reduction_indices" + attr { + key: "T" + value { type: DT_FLOAT } + } + attr { + key: "Tidx" + value { type: DT_INT32 } + } + attr { + key: "keep_dims" + value { b: true } + } +} +*/ + +class TFMean final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Mean>> +{ +public: + TFMean() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + + Node *reduction_indices(void) const { return at(1)->node(); } + void reduction_indices(Node *node) { at(1)->node(node); } + +public: + bool keep_dims(void) const { return _keep_dims; } + void keep_dims(bool keep_dims) { _keep_dims = keep_dims; } + +private: + bool _keep_dims = false; +}; + +} // namespace moco + +#endif // __MOCO_IR_TFMEAN_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFMul.h b/compiler/moco/lang/include/moco/IR/Nodes/TFMul.h new file mode 100644 index 000000000..4692838cb --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFMul.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_IR_TFMUL_H__ +#define __MOCO_IR_TFMUL_H__ + +#include "moco/IR/TFNodeDecl.h" + +namespace moco +{ + +/// @note TFMul corresponds to the following GraphDef +/* +node { + name: "mul" + op: "Mul" + input: "x" + input: "y" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +*/ + +class TFMul final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Mul>> +{ +public: + TFMul() = default; + +public: + Node *x(void) const { return at(0)->node(); } + void x(Node *node) { at(0)->node(node); } + + Node *y(void) const { return at(1)->node(); } + void y(Node *node) { at(1)->node(node); } +}; + +} // namespace moco + +#endif // __MOCO_IR_TFMUL_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFPack.h b/compiler/moco/lang/include/moco/IR/Nodes/TFPack.h new file mode 100644 index 000000000..1046a18ed --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFPack.h @@ -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. + */ + +#ifndef __MOCO_IR_TFPACK_H__ +#define __MOCO_IR_TFPACK_H__ + +#include "moco/IR/TFNodeDecl.h" +#include "moco/IR/VariadicArityNode.h" + +namespace moco +{ +/// @note TFPack corresponds to the following GraphDef +/* +node { + name: "Pack" + op: "Pack" + input: "input_1" + input: "input_2" + attr { + key: "N" + value { + i: 2 + } + } + attr { + key: "T" + value { + type: DT_INT32 + } + } + attr { + key: "axis" + value { + i: 0 + } + } +} +*/ + +class TFPack final : public VariadicArityNode<TFNodeImpl<TFOpcode::Pack>> +{ +public: + TFPack(uint32_t arity) : VariadicArityNode<TFNodeImpl<TFOpcode::Pack>>(arity) + { + // at least one item should exist + assert(arity >= 1); + } + +public: + Node *values(uint32_t index) const + { + assert(index < arity()); + return at(index)->node(); + } + void values(uint32_t index, Node *node) + { + assert(index < arity()); + at(index)->node(node); + } + +public: + uint32_t N(void) const { return arity(); } + + int32_t axis(void) const { return _axis; } + void axis(int32_t axis) { _axis = axis; } + +private: + int32_t _axis{0}; +}; + +} // namespace moco + +#endif // __MOCO_IR_TFPACK_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFPad.h b/compiler/moco/lang/include/moco/IR/Nodes/TFPad.h new file mode 100644 index 000000000..dae4741d6 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFPad.h @@ -0,0 +1,61 @@ +/* + * 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_IR_TFPAD_H__ +#define __MOCO_IR_TFPAD_H__ + +#include "moco/IR/TFNodeDecl.h" + +namespace moco +{ +/// @note TFPad corresponds to the following GraphDef +/* +node { + name: "Pad" + op: "Pad" + input: "Const_tensor" + input: "Const_paddings" + attr { + key: "T" + value { + type: DT_INT32 + } + } + attr { + key: "Tpaddings" + value { + type: DT_INT32 + } + } +} +*/ + +class TFPad final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Pad>> +{ +public: + TFPad() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + + Node *paddings(void) const { return at(1)->node(); } + void paddings(Node *node) { at(1)->node(node); } +}; + +} // namespace moco + +#endif // __MOCO_IR_TFPAD_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFPlaceholder.h b/compiler/moco/lang/include/moco/IR/Nodes/TFPlaceholder.h new file mode 100644 index 000000000..65a78e665 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFPlaceholder.h @@ -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. + */ + +#ifndef __MOCO_IR_TFPLACEHOLDER_H__ +#define __MOCO_IR_TFPLACEHOLDER_H__ + +#include "moco/IR/TFNodeDecl.h" + +#include <loco/IR/DataTypeTraits.h> +#include <loco/IR/NodeMixins.h> +#include <loco/IR/GraphInputIndex.h> +#include <loco/IR/TensorShape.h> + +namespace moco +{ + +/// @note TFPlaceholder corresponds to the following GraphDef +/* +node { + name: "placeholder" + op: "Placeholder" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "shape" + value { + shape { + dim { + size: 1 + } + dim { + size: 3 + } + dim { + size: 3 + } + dim { + size: 1 + } + } + } + } +} +*/ + +/** + * @brief IR for tf.placeholder + */ +class TFPlaceholder final : public FixedArityNode<0, TFNodeImpl<TFOpcode::Placeholder>>, + public loco::NodeMixin<loco::NodeTrait::DataType>, + public loco::NodeMixin<loco::NodeTrait::TensorShape> +{ +public: + TFPlaceholder() = default; + + // TODO Update unkown shape information. tensorflow::NodeDef may not have "shape" attr. +}; + +} // namespace moco + +namespace moco +{ + +bool indexed(const TFPlaceholder *node); +loco::GraphInputIndex index(const TFPlaceholder *node); +void index(TFPlaceholder *node, const loco::GraphInputIndex index); +loco::TensorShape tensor_shape(const TFPlaceholder *node); + +TFPlaceholder *placeholder_node(loco::Graph *g, const loco::GraphInputIndex &idx); + +} // namespace moco + +#endif // __MOCO_IR_TFPLACEHOLDER_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFPush.h b/compiler/moco/lang/include/moco/IR/Nodes/TFPush.h new file mode 100644 index 000000000..e45804252 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFPush.h @@ -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. + */ + +#ifndef __MOCO_IR_TFPUSH_H__ +#define __MOCO_IR_TFPUSH_H__ + +#include "moco/IR/TFNodeDecl.h" + +#include <loco.h> + +namespace moco +{ + +/** + * @brief Make a value visible to user + * + * @note TFPush is a virtual node that does not corresponds to real TensorFlow node + * Why this node is introduced: + * - Any TensorFlow Nodes can be an output. + * - So let any TFNode type can provide OutputIndex using Annotation. + * - Problem comes when in transformation, output node can be replaced. + * - This causes that OutputIndex Annotation should be copied to new node. + * - This makes every transformation in any Dialect code change. + * - And even worse, this makes every new transformation follow this rule. + * - Which is not good. + * - Thus, like loco Canonical does, follow loco::Push. + */ +class TFPush /* to user */ final : public FixedArityNode<1, TFNodeImpl<TFOpcode::TFPush>> +{ +public: + TFPush() = default; + +public: + loco::Node *from(void) const { return at(0)->node(); } + void from(loco::Node *node) { at(0)->node(node); } + +public: + void index(const loco::GraphOutputIndex &index); + + /** + * @brief Get associated output index + * + * The behavior of this method is undefined when "index" is not set before. + * + * NOTE This method intentionally returns "GraphOutputIndex" instead of "const GraphOutputIndex &" + * not to expose the internal implementation details. + */ + loco::GraphOutputIndex index(void) const; + + /** + * @brief Check whether index is initialized + * + * NOTE "indexed" method does not validate whether index is in a valid range + */ + bool indexed(void) const { return _index != -1; } + + /** + * @brief Reset output index + */ + void index_reset(void) { _index = -1; } + +private: + int64_t _index = -1; // Uninitialized +}; + +/// @brief Find a TFPush node with a given output index +TFPush *push_node(loco::Graph *g, const loco::GraphOutputIndex &index); + +} // namespace moco + +#endif // __MOCO_IR_TFPUSH_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFRealDiv.h b/compiler/moco/lang/include/moco/IR/Nodes/TFRealDiv.h new file mode 100644 index 000000000..8d61b3d13 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFRealDiv.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_IR_TFREALDIV_H__ +#define __MOCO_IR_TFREALDIV_H__ + +#include "moco/IR/TFNodeDecl.h" + +namespace moco +{ + +/// @note TFRealDiv corresponds to the following GraphDef +/* +node { + name: "div" + op: "RealDiv" + input: "x" + input: "y" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +*/ + +class TFRealDiv final : public FixedArityNode<2, TFNodeImpl<TFOpcode::RealDiv>> +{ +public: + TFRealDiv() = default; + +public: + Node *x(void) const { return at(0)->node(); } + void x(Node *node) { at(0)->node(node); } + + Node *y(void) const { return at(1)->node(); } + void y(Node *node) { at(1)->node(node); } +}; + +} // namespace moco + +#endif // __MOCO_IR_TFREALDIV_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFRelu.h b/compiler/moco/lang/include/moco/IR/Nodes/TFRelu.h new file mode 100644 index 000000000..90e121e5e --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFRelu.h @@ -0,0 +1,52 @@ +/* + * 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_IR_TFRELU_H__ +#define __MOCO_IR_TFRELU_H__ + +#include "moco/IR/TFNodeDecl.h" + +namespace moco +{ + +/// @note TFRelu corresponds to the following GraphDef +/* +node { + name: "output/relu" + op: "Relu" + input: "Placeholder" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +*/ + +class TFRelu final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Relu>> +{ +public: + TFRelu() = default; + +public: + Node *features(void) const { return at(0)->node(); } + void features(Node *node) { at(0)->node(node); } +}; + +} // namespace moco + +#endif // __MOCO_IR_TFRELU_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFRelu6.h b/compiler/moco/lang/include/moco/IR/Nodes/TFRelu6.h new file mode 100644 index 000000000..bb705b782 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFRelu6.h @@ -0,0 +1,50 @@ +/* + * 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_IR_TFRELU6_H__ +#define __MOCO_IR_TFRELU6_H__ + +#include "moco/IR/TFNodeDecl.h" + +namespace moco +{ + +/// @note TFRelu6 corresponds to the following GraphDef +/* +node { + name: "Relu6" + op: "Relu6" + input: "Placeholder" + attr { + key: "T" + value { type: DT_FLOAT } + } +} +*/ + +class TFRelu6 final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Relu6>> +{ +public: + TFRelu6() = default; + +public: + Node *features(void) const { return at(0)->node(); } + void features(Node *node) { at(0)->node(node); } +}; + +} // namespace moco + +#endif // __MOCO_IR_TFRELU6_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFReshape.h b/compiler/moco/lang/include/moco/IR/Nodes/TFReshape.h new file mode 100644 index 000000000..1f743565d --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFReshape.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_IR_TFRESHAPE_H__ +#define __MOCO_IR_TFRESHAPE_H__ + +#include "moco/IR/TFNodeDecl.h" + +namespace moco +{ + +/// @note TFReshape corresponds to the following GraphDef +/* +node { + name: "reshape" + op: "Reshape" + input: "tensor" + input: "shape" + attr { + key: "T" + value { type: DT_FLOAT } + } +} +*/ + +class TFReshape final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Reshape>> +{ +public: + TFReshape() = default; + +public: + Node *tensor(void) const { return at(0)->node(); } + void tensor(Node *node) { at(0)->node(node); } + + Node *shape(void) const { return at(1)->node(); } + void shape(Node *node) { at(1)->node(node); } +}; + +} // namespace moco + +#endif // __MOCO_IR_TFRESHAPE_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFRsqrt.h b/compiler/moco/lang/include/moco/IR/Nodes/TFRsqrt.h new file mode 100644 index 000000000..c71a5b98c --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFRsqrt.h @@ -0,0 +1,52 @@ +/* + * 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_IR_TFRSQRT_H__ +#define __MOCO_IR_TFRSQRT_H__ + +#include "moco/IR/TFNodeDecl.h" + +namespace moco +{ + +/// @note TFRsqrt corresponds to the following GraphDef +/* +node { + name: "Rsqrt" + op: "Rsqrt" + input: "Placeholder" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +*/ + +class TFRsqrt final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Rsqrt>> +{ +public: + TFRsqrt() = default; + +public: + Node *x(void) const { return at(0)->node(); } + void x(Node *node) { at(0)->node(node); } +}; + +} // namespace moco + +#endif // __MOCO_IR_TFRSQRT_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFShape.h b/compiler/moco/lang/include/moco/IR/Nodes/TFShape.h new file mode 100644 index 000000000..36f0f1e69 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFShape.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 __MOCO_IR_TFSHAPE_H__ +#define __MOCO_IR_TFSHAPE_H__ + +#include "moco/IR/TFNodeDecl.h" + +#include <loco/IR/NodeMixins.h> + +#include <vector> + +namespace moco +{ + +/// @note TFShape corresponds to the following GraphDef +/* +node { + name: "Shape" + op: "Shape" + input: "some_input" + attr { + key: "T" + value { type: DT_FLOAT } + } + attr { + key: "out_type" + value { type: DT_INT32 } + } +} +*/ + +/// @note Mixed in dtype() is for 'out_type' attribute +class TFShape final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Shape>>, + public loco::NodeMixin<loco::NodeTrait::DataType> +{ +public: + TFShape() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } +}; + +} // namespace moco + +#endif // __MOCO_IR_TFSHAPE_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFSoftmax.h b/compiler/moco/lang/include/moco/IR/Nodes/TFSoftmax.h new file mode 100644 index 000000000..c98df1d82 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFSoftmax.h @@ -0,0 +1,37 @@ +/* + * 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_IR_TFSOFTMAX_H__ +#define __MOCO_IR_TFSOFTMAX_H__ + +#include "moco/IR/TFNodeDecl.h" + +namespace moco +{ + +class TFSoftmax final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Softmax>> +{ +public: + TFSoftmax() = default; + +public: + Node *logits(void) const { return at(0)->node(); } + void logits(Node *node) { at(0)->node(node); } +}; + +} // namespace moco + +#endif // __MOCO_IR_TFSOFTMAX_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFSqrt.h b/compiler/moco/lang/include/moco/IR/Nodes/TFSqrt.h new file mode 100644 index 000000000..273b5d49b --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFSqrt.h @@ -0,0 +1,52 @@ +/* + * 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_IR_TFSQRT_H__ +#define __MOCO_IR_TFSQRT_H__ + +#include "moco/IR/TFNodeDecl.h" + +namespace moco +{ + +/// @note TFSqrt corresponds to the following GraphDef +/* +node { + name: "Sqrt" + op: "Sqrt" + input: "Placeholder" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +*/ + +class TFSqrt final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Sqrt>> +{ +public: + TFSqrt() = default; + +public: + Node *x(void) const { return at(0)->node(); } + void x(Node *node) { at(0)->node(node); } +}; + +} // namespace moco + +#endif // __MOCO_IR_TFSQRT_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFSquaredDifference.h b/compiler/moco/lang/include/moco/IR/Nodes/TFSquaredDifference.h new file mode 100644 index 000000000..4e0a929d3 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFSquaredDifference.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_IR_TFSQUAREDDIFFERENCE_H__ +#define __MOCO_IR_TFSQUAREDDIFFERENCE_H__ + +#include "moco/IR/TFNodeDecl.h" + +namespace moco +{ + +/// @note TFSquaredDifference corresponds to the following GraphDef +/* +node { + name: "SquaredDifference" + op: "SquaredDifference" + input: "input_x" + input: "input_y" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +*/ + +class TFSquaredDifference final : public FixedArityNode<2, TFNodeImpl<TFOpcode::SquaredDifference>> +{ +public: + TFSquaredDifference() = default; + +public: + Node *x(void) const { return at(0)->node(); } + void x(Node *node) { at(0)->node(node); } + + Node *y(void) const { return at(1)->node(); } + void y(Node *node) { at(1)->node(node); } +}; + +} // namespace moco + +#endif // __MOCO_IR_TFSQUAREDDIFFERENCE_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFSqueeze.h b/compiler/moco/lang/include/moco/IR/Nodes/TFSqueeze.h new file mode 100644 index 000000000..612497ee7 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFSqueeze.h @@ -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. + */ + +#ifndef __MOCO_IR_TFSQUEEZE_H__ +#define __MOCO_IR_TFSQUEEZE_H__ + +#include "moco/IR/TFNodeDecl.h" + +#include <vector> + +namespace moco +{ + +/// @note TFSqueeze corresponds to the following GraphDef +/* +node { + name: "squeeze" + op: "Squeeze" + input: "x" + attr { + key: "T" + value { + type: DT_FLOAT + } + } + attr { + key: "squeeze_dims" + value { + list { + i: a + i: b + .. + } + } + } +} +*/ + +class TFSqueeze final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Squeeze>> +{ +public: + TFSqueeze() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + +public: + const std::vector<int64_t> &squeeze_dims(void) const { return _squeeze_dims; } + void squeeze_dims(const std::vector<int64_t> &squeeze_dims) { _squeeze_dims = squeeze_dims; } + +private: + std::vector<int64_t> _squeeze_dims; +}; + +} // namespace moco + +#endif // __MOCO_IR_TFSQUEEZE_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFStopGradient.h b/compiler/moco/lang/include/moco/IR/Nodes/TFStopGradient.h new file mode 100644 index 000000000..cfebd92a9 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFStopGradient.h @@ -0,0 +1,52 @@ +/* + * 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_IR_TFSTOPGRADIENT_H__ +#define __MOCO_IR_TFSTOPGRADIENT_H__ + +#include "moco/IR/TFNodeDecl.h" + +namespace moco +{ + +/// @note TFStopGradient corresponds to the following GraphDef +/* +node { + name: "StopGradient" + op: "StopGradient" + input: "Placeholder" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +*/ + +class TFStopGradient final : public FixedArityNode<1, TFNodeImpl<TFOpcode::StopGradient>> +{ +public: + TFStopGradient() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } +}; + +} // namespace moco + +#endif // __MOCO_IR_TFSTOPGRADIENT_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFStridedSlice.h b/compiler/moco/lang/include/moco/IR/Nodes/TFStridedSlice.h new file mode 100644 index 000000000..75012b219 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFStridedSlice.h @@ -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. + */ + +#ifndef __MOCO_IR_TFSTRIDEDSLICE_H__ +#define __MOCO_IR_TFSTRIDEDSLICE_H__ + +#include "moco/IR/TFNodeDecl.h" + +namespace moco +{ + +/// @note TFStridedSlice corresponds to the following GraphDef +/* +node { + name: "StridedSlice" + op: "StridedSlice" + input: "input" + input: "begin" + input: "end" + input: "stride" + attr { + key: "Index" + value { + type: DT_INT32 + } + } + attr { + key: "T" + value { + type: DT_INT32 + } + } + attr { + key: "begin_mask" + value { + i: 0 + } + } + attr { + key: "ellipsis_mask" + value { + i: 0 + } + } + attr { + key: "end_mask" + value { + i: 0 + } + } + attr { + key: "new_axis_mask" + value { + i: 0 + } + } + attr { + key: "shrink_axis_mask" + value { + i: 0 + } + } +} +*/ + +class TFStridedSlice final : public FixedArityNode<4, TFNodeImpl<TFOpcode::StridedSlice>> +{ +public: + TFStridedSlice() = default; + +public: + Node *input(void) const { return at(0)->node(); } + void input(Node *node) { at(0)->node(node); } + + Node *begin(void) const { return at(1)->node(); } + void begin(Node *node) { at(1)->node(node); } + + Node *end(void) const { return at(2)->node(); } + void end(Node *node) { at(2)->node(node); } + + Node *strides(void) const { return at(3)->node(); } + void strides(Node *node) { at(3)->node(node); } + +public: + int32_t begin_mask(void) const { return _begin_mask; } + void begin_mask(int32_t begin_mask) { _begin_mask = begin_mask; } + + int32_t end_mask(void) const { return _end_mask; } + void end_mask(int32_t end_mask) { _end_mask = end_mask; } + + int32_t ellipsis_mask(void) const { return _ellipsis_mask; } + void ellipsis_mask(int32_t ellipsis_mask) { _ellipsis_mask = ellipsis_mask; } + + int32_t new_axis_mask(void) const { return _new_axis_mask; } + void new_axis_mask(int32_t new_axis_mask) { _new_axis_mask = new_axis_mask; } + + int32_t shrink_axis_mask(void) const { return _shrink_axis_mask; } + void shrink_axis_mask(int32_t shrink_axis_mask) { _shrink_axis_mask = shrink_axis_mask; } + +private: + int32_t _begin_mask{0}; + int32_t _end_mask{0}; + int32_t _ellipsis_mask{0}; + int32_t _new_axis_mask{0}; + int32_t _shrink_axis_mask{0}; +}; + +} // namespace moco + +#endif // __MOCO_IR_TFSTRIDEDSLICE_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFSub.h b/compiler/moco/lang/include/moco/IR/Nodes/TFSub.h new file mode 100644 index 000000000..27905cbdb --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFSub.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_IR_TFSUB_H__ +#define __MOCO_IR_TFSUB_H__ + +#include "moco/IR/TFNodeDecl.h" + +namespace moco +{ + +/// @note TFSub corresponds to the following GraphDef +/* +node { + name: "sub" + op: "Sub" + input: "x" + input: "y" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +*/ + +class TFSub final : public FixedArityNode<2, TFNodeImpl<TFOpcode::Sub>> +{ +public: + TFSub() = default; + +public: + Node *x(void) const { return at(0)->node(); } + void x(Node *node) { at(0)->node(node); } + + Node *y(void) const { return at(1)->node(); } + void y(Node *node) { at(1)->node(node); } +}; + +} // namespace moco + +#endif // __MOCO_IR_TFSUB_H__ diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFTanh.h b/compiler/moco/lang/include/moco/IR/Nodes/TFTanh.h new file mode 100644 index 000000000..4543c62f3 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFTanh.h @@ -0,0 +1,37 @@ +/* + * 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_IR_TFTANH_H__ +#define __MOCO_IR_TFTANH_H__ + +#include "moco/IR/TFNodeDecl.h" + +namespace moco +{ + +class TFTanh final : public FixedArityNode<1, TFNodeImpl<TFOpcode::Tanh>> +{ +public: + TFTanh() = default; + +public: + Node *x(void) const { return at(0)->node(); } + void x(Node *node) { at(0)->node(node); } +}; + +} // namespace moco + +#endif // __MOCO_IR_TFTANH_H__ diff --git a/compiler/moco/lang/include/moco/IR/TFDataLayout.h b/compiler/moco/lang/include/moco/IR/TFDataLayout.h new file mode 100644 index 000000000..f0edfacd5 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/TFDataLayout.h @@ -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. + */ + +#ifndef __MOCO_IR_TFDATALAYOUT_H__ +#define __MOCO_IR_TFDATALAYOUT_H__ + +#include <string> + +namespace moco +{ + +using TFDataLayout = std::string; + +} // namespace moco + +#endif // __MOCO_IR_TFDATALAYOUT_H__ diff --git a/compiler/moco/lang/include/moco/IR/TFDialect.h b/compiler/moco/lang/include/moco/IR/TFDialect.h new file mode 100644 index 000000000..847bc527f --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/TFDialect.h @@ -0,0 +1,43 @@ +/* + * 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_IR_TFDIALECT_H__ +#define __MOCO_IR_TFDIALECT_H__ + +#include <loco/IR/Dialect.h> + +namespace moco +{ + +/** + * @brief A singleton for TensorFlow Dialect + */ +class TFDialect final : public loco::Dialect +{ +private: + TFDialect(); + +public: + TFDialect(const TFDialect &) = delete; + TFDialect(TFDialect &&) = delete; + +public: + static loco::Dialect *get(void); +}; + +} // namespace moco + +#endif // __MOCO_IR_TFDIALECT_H__ diff --git a/compiler/moco/lang/include/moco/IR/TFNode.h b/compiler/moco/lang/include/moco/IR/TFNode.h new file mode 100644 index 000000000..e3d900ba3 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/TFNode.h @@ -0,0 +1,23 @@ +/* + * 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_IR_TFNODE_H__ +#define __MOCO_IR_TFNODE_H__ + +#include "moco/IR/TFNodeDecl.h" +#include "moco/IR/TFNodeImpl.h" + +#endif // __MOCO_IR_TFNODE_H__ diff --git a/compiler/moco/lang/include/moco/IR/TFNodeDecl.h b/compiler/moco/lang/include/moco/IR/TFNodeDecl.h new file mode 100644 index 000000000..68d7161b6 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/TFNodeDecl.h @@ -0,0 +1,104 @@ +/* + * 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_IR_TFNODE_DECL_H__ +#define __MOCO_IR_TFNODE_DECL_H__ + +#include <loco/IR/Node.h> +#include <loco/IR/Dialect.h> + +#include "moco/IR/TFOpcode.h" +#include "moco/IR/TFNodeVisitor.forward.h" + +#include "moco/IR/TFDataLayout.h" +#include "moco/IR/TFPadding.h" + +#include <array> +#include <string> + +namespace moco +{ + +/** + * @note NodeName is string name of the Node without ':#' prefix like ':0' or ':1' + */ +using NodeName = std::string; + +struct TFNode : public loco::Node +{ + virtual ~TFNode() = default; + + const loco::Dialect *dialect(void) const final; + virtual TFOpcode opcode(void) const = 0; + + template <typename T> T accept(TFNodeVisitorBase<T> *) const; + template <typename T> T accept(TFNodeMutableVisitorBase<T> *); + + NodeName name(void) const { return _name; } + void name(const NodeName &name) { _name = name; } + +private: + NodeName _name; +}; + +template <TFOpcode Code> struct TFNodeImpl : public TFNode +{ + virtual ~TFNodeImpl() = default; + + uint32_t opnum(void) const final { return static_cast<uint32_t>(Code); } + TFOpcode opcode(void) const final { return Code; } +}; + +/** + * @brief Nodes with the fixed number of inputs + */ +template <unsigned N, typename Base> class FixedArityNode : public Base +{ +public: + FixedArityNode() + { + for (uint32_t n = 0; n < N; ++n) + { + _args[n] = std::unique_ptr<loco::Use>{new loco::Use{this}}; + } + } + + virtual ~FixedArityNode() = default; + +public: + unsigned arity(void) const final { return N; } + + loco::Node *arg(uint32_t n) const final { return _args.at(n)->node(); } + + void drop(void) final + { + for (uint32_t n = 0; n < N; ++n) + { + _args.at(n)->node(nullptr); + } + } + +protected: + // This API allows inherited classes to access "_args" field. + loco::Use *at(unsigned n) const { return _args.at(n).get(); } + +private: + std::array<std::unique_ptr<loco::Use>, N> _args{}; +}; + +} // namespace moco + +#endif // __MOCO_IR_TFNODE_DECL_H__ diff --git a/compiler/moco/lang/include/moco/IR/TFNodeImpl.h b/compiler/moco/lang/include/moco/IR/TFNodeImpl.h new file mode 100644 index 000000000..afc306031 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/TFNodeImpl.h @@ -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. + */ + +#ifndef __MOCO_IR_TFNODE_IMPL_H__ +#define __MOCO_IR_TFNODE_IMPL_H__ + +#include "moco/IR/TFNodes.h" +#include "moco/IR/TFNodeVisitor.h" + +#include <stdexcept> + +namespace moco +{ + +template <typename T> T TFNode::accept(TFNodeVisitorBase<T> *v) const +{ + switch (this->opcode()) + { +#define TENSORFLOW_NODE(OPCODE, CLASS) \ + case TFOpcode::OPCODE: \ + return v->visit(dynamic_cast<const CLASS *>(this)); + +#include "TFNodes.lst" +#undef TENSORFLOW_NODE + default: + break; + } + + // TODO including oops will make oops dependent to modules that include this + // postpone decision to this or not + throw std::runtime_error{"Unsupported Node"}; +} + +template <typename T> T TFNode::accept(TFNodeMutableVisitorBase<T> *v) +{ + switch (this->opcode()) + { +#define TENSORFLOW_NODE(OPCODE, CLASS) \ + case TFOpcode::OPCODE: \ + return v->visit(dynamic_cast<CLASS *>(this)); + +#include "TFNodes.lst" +#undef TENSORFLOW_NODE + default: + break; + } + + // TODO including oops will make oops dependent to modules that include this + // postpone decision to this or not + throw std::runtime_error{"Unsupported Node"}; +} + +} // namespace moco + +#endif // __MOCO_IR_TFNODE_IMPL_H__ diff --git a/compiler/moco/lang/include/moco/IR/TFNodeVisitor.forward.h b/compiler/moco/lang/include/moco/IR/TFNodeVisitor.forward.h new file mode 100644 index 000000000..1eb86871c --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/TFNodeVisitor.forward.h @@ -0,0 +1,30 @@ +/* + * 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_IR_TFNODE_VISITOR_FORWARD_H__ +#define __MOCO_IR_TFNODE_VISITOR_FORWARD_H__ + +namespace moco +{ + +// NOTE These forward declarations SHOULD BE aligned with Node delcarations in +// "TFNodeVisitor.h" +template <typename T> struct TFNodeVisitorBase; +template <typename T> struct TFNodeMutableVisitorBase; + +} // namespace moco + +#endif // __MOCO_IR_TFNODE_VISITOR_FORWARD_H__ diff --git a/compiler/moco/lang/include/moco/IR/TFNodeVisitor.h b/compiler/moco/lang/include/moco/IR/TFNodeVisitor.h new file mode 100644 index 000000000..8d23e447d --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/TFNodeVisitor.h @@ -0,0 +1,83 @@ +/* + * 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_IR_TFNODE_VISITOR_H__ +#define __MOCO_IR_TFNODE_VISITOR_H__ + +#include "moco/IR/TFNodes.h" + +#include <stdexcept> + +namespace moco +{ + +/** + * DO NOT use this class. Use TFNodeVisitor instead. + */ +template <typename T> struct TFNodeVisitorBase +{ + virtual ~TFNodeVisitorBase() = default; + +#define TENSORFLOW_NODE(OPCODE, CLASS) virtual T visit(const CLASS *) = 0; +#include "TFNodes.lst" +#undef TENSORFLOW_NODE +}; + +template <typename T> struct TFNodeVisitor : public TFNodeVisitorBase<T> +{ + virtual ~TFNodeVisitor() = default; + +#define TENSORFLOW_NODE(OPCODE, CLASS) \ + virtual T visit(const CLASS *node) { return visit(static_cast<const TFNode *>(node)); } +#include "TFNodes.lst" +#undef TENSORFLOW_NODE + + // TODO including oops will make oops dependent to modules that include this + // postpone decision to this or not + /// @brief Default fallback + virtual T visit(const TFNode *) { throw std::runtime_error{"Unsupported Node"}; } +}; + +/** + * DO NOT use this class. Use TFNodeMutableVisitor instead. + */ +template <typename T> struct TFNodeMutableVisitorBase +{ + virtual ~TFNodeMutableVisitorBase() = default; + +#define TENSORFLOW_NODE(OPCODE, CLASS) virtual T visit(CLASS *) = 0; +#include "TFNodes.lst" +#undef TENSORFLOW_NODE +}; + +template <typename T> struct TFNodeMutableVisitor : public TFNodeMutableVisitorBase<T> +{ + virtual ~TFNodeMutableVisitor() = default; + +#define TENSORFLOW_NODE(OPCODE, CLASS) \ + virtual T visit(CLASS *node) { return visit(static_cast<TFNode *>(node)); } +#include "TFNodes.lst" +#undef TENSORFLOW_NODE + + // TODO including oops will make oops dependent to modules that include this + // postpone decision to this or not + /// @brief Default fallback + virtual T visit(TFNode *) { throw std::runtime_error{"Unsupported Node"}; } +}; + +} // namespace moco + +#endif // __MOCO_IR_TFNODE_VISITOR_H__ diff --git a/compiler/moco/lang/include/moco/IR/TFNodes.h b/compiler/moco/lang/include/moco/IR/TFNodes.h new file mode 100644 index 000000000..ad54dfdf3 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/TFNodes.h @@ -0,0 +1,55 @@ +/* + * 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_IR_TFNODES_H__ +#define __MOCO_IR_TFNODES_H__ + +#include "moco/IR/Nodes/TFAdd.h" +#include "moco/IR/Nodes/TFAvgPool.h" +#include "moco/IR/Nodes/TFBiasAdd.h" +#include "moco/IR/Nodes/TFConcatV2.h" +#include "moco/IR/Nodes/TFConst.h" +#include "moco/IR/Nodes/TFConv2D.h" +#include "moco/IR/Nodes/TFConv2DBackpropInput.h" +#include "moco/IR/Nodes/TFDepthwiseConv2dNative.h" +#include "moco/IR/Nodes/TFFakeQuantWithMinMaxVars.h" +#include "moco/IR/Nodes/TFFusedBatchNorm.h" +#include "moco/IR/Nodes/TFIdentity.h" +#include "moco/IR/Nodes/TFMaximum.h" +#include "moco/IR/Nodes/TFMaxPool.h" +#include "moco/IR/Nodes/TFMean.h" +#include "moco/IR/Nodes/TFMul.h" +#include "moco/IR/Nodes/TFPack.h" +#include "moco/IR/Nodes/TFPad.h" +#include "moco/IR/Nodes/TFPlaceholder.h" +#include "moco/IR/Nodes/TFRealDiv.h" +#include "moco/IR/Nodes/TFRelu.h" +#include "moco/IR/Nodes/TFRelu6.h" +#include "moco/IR/Nodes/TFReshape.h" +#include "moco/IR/Nodes/TFRsqrt.h" +#include "moco/IR/Nodes/TFShape.h" +#include "moco/IR/Nodes/TFSoftmax.h" +#include "moco/IR/Nodes/TFSqrt.h" +#include "moco/IR/Nodes/TFSquaredDifference.h" +#include "moco/IR/Nodes/TFSqueeze.h" +#include "moco/IR/Nodes/TFStopGradient.h" +#include "moco/IR/Nodes/TFStridedSlice.h" +#include "moco/IR/Nodes/TFSub.h" +#include "moco/IR/Nodes/TFTanh.h" +// For virtual node(s) +#include "moco/IR/Nodes/TFPush.h" + +#endif // __MOCO_IR_TFNODES_H__ diff --git a/compiler/moco/lang/include/moco/IR/TFNodes.lst b/compiler/moco/lang/include/moco/IR/TFNodes.lst new file mode 100644 index 000000000..8373d2b8d --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/TFNodes.lst @@ -0,0 +1,48 @@ +#ifndef TENSORFLOW_NODE +#error "Define TENSORFLOW_NODE" +#endif // TENSORFLOW_NODE + +// +// PLEASE SORT NODE DECLS IN ALPHABETICAL ORDER +// +// Naming rule: Follow names in TensorFlow C++ source +// ex) for AvgPool, tensorflow/core/ops/nn_ops.cc +// REGISTER_OP("AvgPool") <-- OPCODE: AvgPool. Prefix `TF` for CLASS name +// .Input("value: T") <-- Input name is 'value' +// + +// TENSORFLOW_NODE(OPCODE, CLASS) +TENSORFLOW_NODE(Add, TFAdd) +TENSORFLOW_NODE(AvgPool, TFAvgPool) +TENSORFLOW_NODE(BiasAdd, TFBiasAdd) +TENSORFLOW_NODE(ConcatV2, TFConcatV2) +TENSORFLOW_NODE(Const, TFConst) +TENSORFLOW_NODE(Conv2D, TFConv2D) +TENSORFLOW_NODE(Conv2DBackpropInput, TFConv2DBackpropInput) +TENSORFLOW_NODE(DepthwiseConv2dNative, TFDepthwiseConv2dNative) +TENSORFLOW_NODE(FakeQuantWithMinMaxVars, TFFakeQuantWithMinMaxVars) +TENSORFLOW_NODE(FusedBatchNorm, TFFusedBatchNorm) +TENSORFLOW_NODE(Identity, TFIdentity) +TENSORFLOW_NODE(Maximum, TFMaximum) +TENSORFLOW_NODE(MaxPool, TFMaxPool) +TENSORFLOW_NODE(Mean, TFMean) +TENSORFLOW_NODE(Mul, TFMul) +TENSORFLOW_NODE(Pack, TFPack) +TENSORFLOW_NODE(Pad, TFPad) +TENSORFLOW_NODE(Placeholder, TFPlaceholder) +TENSORFLOW_NODE(RealDiv, TFRealDiv) +TENSORFLOW_NODE(Relu, TFRelu) +TENSORFLOW_NODE(Relu6, TFRelu6) +TENSORFLOW_NODE(Reshape, TFReshape) +TENSORFLOW_NODE(Rsqrt, TFRsqrt) +TENSORFLOW_NODE(Shape, TFShape) +TENSORFLOW_NODE(Softmax, TFSoftmax) +TENSORFLOW_NODE(Sqrt, TFSqrt) +TENSORFLOW_NODE(SquaredDifference, TFSquaredDifference) +TENSORFLOW_NODE(Squeeze, TFSqueeze) +TENSORFLOW_NODE(StopGradient, TFStopGradient) +TENSORFLOW_NODE(StridedSlice, TFStridedSlice) +TENSORFLOW_NODE(Sub, TFSub) +TENSORFLOW_NODE(Tanh, TFTanh) +// For virtual node(s) +TENSORFLOW_NODE(TFPush, TFPush) diff --git a/compiler/moco/lang/include/moco/IR/TFOpcode.h b/compiler/moco/lang/include/moco/IR/TFOpcode.h new file mode 100644 index 000000000..7524dcce4 --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/TFOpcode.h @@ -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. + */ + +#ifndef __MOCO_IR_TFOPCODE_H__ +#define __MOCO_IR_TFOPCODE_H__ + +namespace moco +{ + +/** + * @brief TensorFlow Node Opcode + */ +enum class TFOpcode +{ +#define TENSORFLOW_NODE(OPCODE, CLASS) OPCODE, +#include "TFNodes.lst" +#undef TENSORFLOW_NODE +}; + +} // namespace moco + +#endif // __MOCO_IR_TFOPCODE_H__ diff --git a/compiler/moco/lang/include/moco/IR/TFPadding.h b/compiler/moco/lang/include/moco/IR/TFPadding.h new file mode 100644 index 000000000..c75b3f2ce --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/TFPadding.h @@ -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. + */ + +#ifndef __MOCO_IR_TFPADDING_H__ +#define __MOCO_IR_TFPADDING_H__ + +#include <string> + +namespace moco +{ + +using TFPadding = std::string; + +} // namespace moco + +#endif // __MOCO_IR_TFPADDING_H__ diff --git a/compiler/moco/lang/include/moco/IR/VariadicArityNode.h b/compiler/moco/lang/include/moco/IR/VariadicArityNode.h new file mode 100644 index 000000000..7df0f7dec --- /dev/null +++ b/compiler/moco/lang/include/moco/IR/VariadicArityNode.h @@ -0,0 +1,77 @@ +/* + * 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_IR_VARIADIC_ARITY_NODE_H__ +#define __MOCO_IR_VARIADIC_ARITY_NODE_H__ + +#include <loco/IR/Node.h> +#include <loco/IR/Use.h> + +#include <vector> +#include <memory> +#include <cassert> + +namespace moco +{ + +/** + * @brief Nodes with the variadic inputs + */ +template <typename Base> class VariadicArityNode : public Base +{ +public: + VariadicArityNode(uint32_t arity) + { + for (uint32_t n = 0; n < arity; ++n) + { + _args.emplace_back(std::move(std::unique_ptr<loco::Use>{new loco::Use{this}})); + } + }; + + virtual ~VariadicArityNode() = default; + +public: + uint32_t arity(void) const final { return _args.size(); } + + loco::Node *arg(uint32_t n) const final + { + assert(n < _args.size()); + return _args.at(n)->node(); + } + + void drop(void) final + { + for (uint32_t n = 0; n < _args.size(); ++n) + { + _args.at(n)->node(nullptr); + } + } + +protected: + // This API allows inherited classes to access "_args" field. + loco::Use *at(uint32_t n) const + { + assert(n < _args.size()); + return _args.at(n).get(); + } + +private: + std::vector<std::unique_ptr<loco::Use>> _args; +}; + +} // namespace moco + +#endif // __MOCO_IR_VARIADIC_ARITY_NODE_H__ diff --git a/compiler/moco/lang/include/moco/Names.h b/compiler/moco/lang/include/moco/Names.h new file mode 100644 index 000000000..1addc812b --- /dev/null +++ b/compiler/moco/lang/include/moco/Names.h @@ -0,0 +1,96 @@ +/* + * 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_NAMES_H__ +#define __MOCO_NAMES_H__ + +#include <string> +#include <stdexcept> + +namespace moco +{ + +struct TensorName final +{ +public: + /** + * @brief Constructor + * + * @note If tensor_name does not have ":index", this constructor adds ":0" by default + */ + explicit TensorName(const std::string &tensor_name) + { + if (tensor_name.find(":") != std::string::npos) // tensor_name is a form of letter:0 + { + _name.assign(tensor_name); + } + else + { + _name.assign(tensor_name + ":0"); // if it does not have ":index", adds ":0" by default + } + } + + explicit TensorName(const std::string &node_name, const int tensor_index) + { + if (node_name.find(":") != std::string::npos) // tensor_name is already a form of name:0 + { + // TODO including oops will make oops dependent to modules that include this + // postpone decision to this or not + throw std::runtime_error("Error: Node name has already tensor index:" + node_name); + } + else + { + _name.assign(node_name + ":" + std::to_string(tensor_index)); + } + } + + const std::string &name() const { return _name; } + + /** + * @brief Returns node name from tensor name by removing, e.g., ":0" + */ + const std::string nodeName() const + { + auto index = _name.find(":"); + + if (index != std::string::npos) + return _name.substr(0, index); + else + { + // TODO including oops will make oops dependent to modules that include this + // postpone decision to this or not + throw std::runtime_error{"Error: Tensor name should be a 'name:number' format: " + _name}; + } + }; + +private: + std::string _name; +}; + +/** + * @brief To use TensorName as a key in std::map, this struct defines how to compare two TensorNames + */ +struct TensorNameCompare +{ + bool operator()(const TensorName &lhs, const TensorName &rhs) const + { + return lhs.name() < rhs.name(); + } +}; + +} // namespace moco + +#endif // __MOCO_NAMES_H__ diff --git a/compiler/moco/lang/src/IR/Nodes/TFAdd.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFAdd.test.cpp new file mode 100644 index 000000000..d2cfb6ac4 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFAdd.test.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 "moco/IR/Nodes/TFAdd.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFAddTest, constructor) +{ + moco::TFAdd add_node; + + ASSERT_EQ(add_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(add_node.opcode(), moco::TFOpcode::Add); + + ASSERT_EQ(add_node.x(), nullptr); + ASSERT_EQ(add_node.y(), nullptr); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFAvgPool.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFAvgPool.test.cpp new file mode 100644 index 000000000..32a27ffa0 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFAvgPool.test.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 "moco/IR/Nodes/TFAvgPool.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFAvgPoolTest, constructor) +{ + moco::TFAvgPool avgpool; + + ASSERT_EQ(avgpool.dialect(), moco::TFDialect::get()); + ASSERT_EQ(avgpool.opcode(), moco::TFOpcode::AvgPool); + + ASSERT_EQ(avgpool.value(), nullptr); + ASSERT_EQ(avgpool.data_layout(), ""); + ASSERT_EQ(avgpool.padding(), ""); + ASSERT_EQ(avgpool.ksize(), std::vector<int64_t>({})); + ASSERT_EQ(avgpool.strides(), std::vector<int64_t>({})); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFBiasAdd.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFBiasAdd.test.cpp new file mode 100644 index 000000000..4a15a4981 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFBiasAdd.test.cpp @@ -0,0 +1,32 @@ +/* + * 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/IR/Nodes/TFBiasAdd.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFBiasAddTest, constructor) +{ + moco::TFBiasAdd bias_add; + + ASSERT_EQ(bias_add.dialect(), moco::TFDialect::get()); + ASSERT_EQ(bias_add.opcode(), moco::TFOpcode::BiasAdd); + + ASSERT_EQ(bias_add.value(), nullptr); + ASSERT_EQ(bias_add.bias(), nullptr); + ASSERT_EQ(bias_add.data_layout(), ""); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFConcatV2.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFConcatV2.test.cpp new file mode 100644 index 000000000..8f7df92d0 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFConcatV2.test.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 "moco/IR/Nodes/TFConcatV2.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFConcatV2Test, constructor) +{ + moco::TFConcatV2 concatv2_node(3); // num of values + + ASSERT_EQ(concatv2_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(concatv2_node.opcode(), moco::TFOpcode::ConcatV2); + + ASSERT_EQ(concatv2_node.num_values(), 3); + ASSERT_EQ(concatv2_node.values(0), nullptr); + ASSERT_EQ(concatv2_node.values(1), nullptr); + ASSERT_EQ(concatv2_node.values(2), nullptr); + ASSERT_EQ(concatv2_node.axis(), nullptr); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFConst.cpp b/compiler/moco/lang/src/IR/Nodes/TFConst.cpp new file mode 100644 index 000000000..5c8c08ec0 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFConst.cpp @@ -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. + */ + +#include "moco/IR/Nodes/TFConst.h" + +#include <cassert> + +namespace moco +{ + +template <loco::DataType DT> uint32_t TFConst::size(void) const +{ + assert(dtype() == DT); + assert(_data.size() % sizeof(typename loco::DataTypeImpl<DT>::Type) == 0); + return _data.size() / sizeof(typename loco::DataTypeImpl<DT>::Type); +} + +template <loco::DataType DT> void TFConst::size(uint32_t l) +{ + assert(dtype() == DT); + _data.resize(l * sizeof(typename loco::DataTypeImpl<DT>::Type)); +} + +template <loco::DataType DT> +const typename loco::DataTypeImpl<DT>::Type &TFConst::at(uint32_t n) const +{ + assert(dtype() == DT); + assert(n < size<DT>()); + return *(reinterpret_cast<const typename loco::DataTypeImpl<DT>::Type *>(_data.data()) + n); +} + +template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &TFConst::at(uint32_t n) +{ + assert(dtype() == DT); + assert(n < size<DT>()); + return *(reinterpret_cast<typename loco::DataTypeImpl<DT>::Type *>(_data.data()) + n); +} + +#define INSTANTIATE(DT) \ + template uint32_t TFConst::size<DT>(void) const; \ + template void TFConst::size<DT>(uint32_t); \ + template const typename loco::DataTypeImpl<DT>::Type &TFConst::at<DT>(uint32_t) const; \ + template typename loco::DataTypeImpl<DT>::Type &TFConst::at<DT>(uint32_t); + +INSTANTIATE(loco::DataType::S8); +INSTANTIATE(loco::DataType::S32); +INSTANTIATE(loco::DataType::FLOAT32); + +#undef INSTANTIATE + +loco::TensorShape tensor_shape(const TFConst *node) +{ + assert(node != nullptr); + + loco::TensorShape shape; + + uint32_t rank = node->rank(); + shape.rank(rank); + for (uint32_t index = 0; index < rank; ++index) + { + assert(node->dim(index).known()); + shape.dim(index) = node->dim(index).value(); + } + + return shape; +} + +uint32_t num_elements(const TFConst *tfconst) +{ + assert(tfconst != nullptr); + + uint32_t num_elements = 1; + for (uint32_t index = 0; index < tfconst->rank(); ++index) + { + assert(tfconst->dim(index).known()); + uint32_t dim = tfconst->dim(index).value(); + num_elements = num_elements * dim; + } + return num_elements; +} + +bool same_shape(const TFConst *lhs, const TFConst *rhs) +{ + assert(lhs != nullptr); + assert(rhs != nullptr); + + if (lhs->rank() != rhs->rank()) + return false; + + for (uint32_t index = 0; index < lhs->rank(); ++index) + { + assert(lhs->dim(index).known()); + assert(rhs->dim(index).known()); + if (lhs->dim(index).value() != rhs->dim(index).value()) + return false; + } + return true; +} + +} // namespace moco diff --git a/compiler/moco/lang/src/IR/Nodes/TFConst.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFConst.test.cpp new file mode 100644 index 000000000..259966e33 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFConst.test.cpp @@ -0,0 +1,95 @@ +/* + * 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/IR/Nodes/TFConst.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFConstantTest, constructor) +{ + moco::TFConst constant; + + ASSERT_EQ(constant.dialect(), moco::TFDialect::get()); + ASSERT_EQ(constant.opcode(), moco::TFOpcode::Const); + + ASSERT_EQ(constant.dtype(), loco::DataType::Unknown); + ASSERT_EQ(constant.rank(), 0); + + constant.dtype(loco::DataType::FLOAT32); + ASSERT_EQ(constant.dtype(), loco::DataType::FLOAT32); + + constant.rank(2); + ASSERT_EQ(constant.rank(), 2); + + constant.dim(0) = 2; + constant.dim(1) = 3; + + ASSERT_TRUE(constant.dim(0).known()); + ASSERT_TRUE(constant.dim(1).known()); + + ASSERT_EQ(constant.dim(0), 2); + ASSERT_EQ(constant.dim(1), 3); + + constant.size<loco::DataType::FLOAT32>(6); + + ASSERT_EQ(constant.size<loco::DataType::FLOAT32>(), 6); + + constant.at<loco::DataType::FLOAT32>(0) = 0.0f; // Set 0,0 + constant.at<loco::DataType::FLOAT32>(1) = 1.0f; // Set 0,1 + constant.at<loco::DataType::FLOAT32>(2) = 2.0f; // Set 0,2 + constant.at<loco::DataType::FLOAT32>(3) = 3.0f; // Set 1,0 + constant.at<loco::DataType::FLOAT32>(4) = 4.0f; // Set 1,1 + constant.at<loco::DataType::FLOAT32>(5) = 5.0f; // Set 1,2 + + ASSERT_EQ(constant.at<loco::DataType::FLOAT32>(0), 0.0f); + ASSERT_EQ(constant.at<loco::DataType::FLOAT32>(1), 1.0f); + ASSERT_EQ(constant.at<loco::DataType::FLOAT32>(2), 2.0f); + ASSERT_EQ(constant.at<loco::DataType::FLOAT32>(3), 3.0f); + ASSERT_EQ(constant.at<loco::DataType::FLOAT32>(4), 4.0f); + ASSERT_EQ(constant.at<loco::DataType::FLOAT32>(5), 5.0f); +} + +TEST(TFConstantTest, datatype_s8) +{ + moco::TFConst constant; + + ASSERT_EQ(constant.dialect(), moco::TFDialect::get()); + ASSERT_EQ(constant.opcode(), moco::TFOpcode::Const); + + ASSERT_EQ(constant.dtype(), loco::DataType::Unknown); + ASSERT_EQ(constant.rank(), 0); + + constant.dtype(loco::DataType::S8); + ASSERT_EQ(constant.dtype(), loco::DataType::S8); + + constant.rank(1); + ASSERT_EQ(constant.rank(), 1); + + constant.dim(0) = 3; + ASSERT_TRUE(constant.dim(0).known()); + ASSERT_EQ(constant.dim(0), 3); + constant.size<loco::DataType::S8>(3); + ASSERT_EQ(constant.size<loco::DataType::S8>(), 3); + + constant.at<loco::DataType::S8>(0) = -1; + constant.at<loco::DataType::S8>(1) = 1; + constant.at<loco::DataType::S8>(2) = 0; + + ASSERT_EQ(constant.at<loco::DataType::S8>(0), -1); + ASSERT_EQ(constant.at<loco::DataType::S8>(1), 1); + ASSERT_EQ(constant.at<loco::DataType::S8>(2), 0); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFConv2D.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFConv2D.test.cpp new file mode 100644 index 000000000..3e3453db0 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFConv2D.test.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 "moco/IR/Nodes/TFConv2D.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFConv2DTest, constructor) +{ + moco::TFConv2D conv2d_node; + + ASSERT_EQ(conv2d_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(conv2d_node.opcode(), moco::TFOpcode::Conv2D); + + ASSERT_EQ(conv2d_node.input(), nullptr); + ASSERT_EQ(conv2d_node.filter(), nullptr); + ASSERT_EQ(conv2d_node.padding(), ""); + ASSERT_EQ(conv2d_node.data_layout(), ""); + ASSERT_EQ(conv2d_node.strides().size(), 0); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFConv2DBackpropInput.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFConv2DBackpropInput.test.cpp new file mode 100644 index 000000000..f7ad4ce67 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFConv2DBackpropInput.test.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 "moco/IR/Nodes/TFConv2DBackpropInput.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFConv2DBackpropInputTest, constructor) +{ + moco::TFConv2DBackpropInput conv2dbi_node; + + ASSERT_EQ(conv2dbi_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(conv2dbi_node.opcode(), moco::TFOpcode::Conv2DBackpropInput); + + ASSERT_EQ(conv2dbi_node.input_sizes(), nullptr); + ASSERT_EQ(conv2dbi_node.filter(), nullptr); + ASSERT_EQ(conv2dbi_node.out_backprop(), nullptr); + ASSERT_EQ(conv2dbi_node.padding(), ""); + ASSERT_EQ(conv2dbi_node.data_layout(), ""); + ASSERT_EQ(conv2dbi_node.strides().size(), 0); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFDepthwiseConv2dNative.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFDepthwiseConv2dNative.test.cpp new file mode 100644 index 000000000..2562997c2 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFDepthwiseConv2dNative.test.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 "moco/IR/Nodes/TFDepthwiseConv2dNative.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFDepthwiseConv2dNativeTest, constructor) +{ + moco::TFDepthwiseConv2dNative depthwiseConv2dnative_node; + + ASSERT_EQ(depthwiseConv2dnative_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(depthwiseConv2dnative_node.opcode(), moco::TFOpcode::DepthwiseConv2dNative); + + ASSERT_EQ(depthwiseConv2dnative_node.input(), nullptr); + ASSERT_EQ(depthwiseConv2dnative_node.filter(), nullptr); + ASSERT_EQ(depthwiseConv2dnative_node.padding(), ""); + ASSERT_EQ(depthwiseConv2dnative_node.data_layout(), ""); + ASSERT_EQ(depthwiseConv2dnative_node.strides().size(), 0); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFFakeQuantWithMinMaxVars.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFFakeQuantWithMinMaxVars.test.cpp new file mode 100644 index 000000000..be8fc3a70 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFFakeQuantWithMinMaxVars.test.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 "moco/IR/Nodes/TFFakeQuantWithMinMaxVars.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFFakeQuantWithMinMaxVarsTest, constructor) +{ + moco::TFFakeQuantWithMinMaxVars fakequant_node; + + ASSERT_EQ(fakequant_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(fakequant_node.opcode(), moco::TFOpcode::FakeQuantWithMinMaxVars); + + ASSERT_EQ(fakequant_node.inputs(), nullptr); + ASSERT_EQ(fakequant_node.min(), nullptr); + ASSERT_EQ(fakequant_node.max(), nullptr); + ASSERT_EQ(fakequant_node.num_bits(), 8); + ASSERT_EQ(fakequant_node.narrow_range(), false); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFFusedBatchNorm.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFFusedBatchNorm.test.cpp new file mode 100644 index 000000000..265f8f9a4 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFFusedBatchNorm.test.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 "moco/IR/Nodes/TFFusedBatchNorm.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFFusedBatchNormTest, constructor) +{ + moco::TFFusedBatchNorm fbn_node; + + ASSERT_EQ(fbn_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(fbn_node.opcode(), moco::TFOpcode::FusedBatchNorm); + + ASSERT_EQ(fbn_node.x(), nullptr); + ASSERT_EQ(fbn_node.scale(), nullptr); + ASSERT_EQ(fbn_node.offset(), nullptr); + ASSERT_EQ(fbn_node.mean(), nullptr); + ASSERT_EQ(fbn_node.variance(), nullptr); + ASSERT_NE(fbn_node.epsilon(), 0.0f); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFIdentity.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFIdentity.test.cpp new file mode 100644 index 000000000..deb17d502 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFIdentity.test.cpp @@ -0,0 +1,30 @@ +/* + * 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/IR/Nodes/TFIdentity.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFIdentituTest, constructor) +{ + moco::TFIdentity identity_node; + + ASSERT_EQ(identity_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(identity_node.opcode(), moco::TFOpcode::Identity); + + ASSERT_EQ(identity_node.input(), nullptr); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFMaxPool.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFMaxPool.test.cpp new file mode 100644 index 000000000..482ad889d --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFMaxPool.test.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 "moco/IR/Nodes/TFMaxPool.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFMaxPoolTest, constructor) +{ + moco::TFMaxPool maxpool; + + ASSERT_EQ(maxpool.dialect(), moco::TFDialect::get()); + ASSERT_EQ(maxpool.opcode(), moco::TFOpcode::MaxPool); + + ASSERT_EQ(maxpool.input(), nullptr); + ASSERT_EQ(maxpool.data_layout(), ""); + ASSERT_EQ(maxpool.padding(), ""); + ASSERT_EQ(maxpool.ksize(), std::vector<int64_t>({})); + ASSERT_EQ(maxpool.strides(), std::vector<int64_t>({})); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFMaximum.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFMaximum.test.cpp new file mode 100644 index 000000000..568bd7038 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFMaximum.test.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "moco/IR/Nodes/TFMaximum.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFMaximumTest, constructor) +{ + moco::TFMaximum max_node; + + ASSERT_EQ(max_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(max_node.opcode(), moco::TFOpcode::Maximum); + + ASSERT_EQ(max_node.x(), nullptr); + ASSERT_EQ(max_node.y(), nullptr); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFMean.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFMean.test.cpp new file mode 100644 index 000000000..126b31783 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFMean.test.cpp @@ -0,0 +1,32 @@ +/* + * 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/IR/Nodes/TFMean.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFMeanTest, constructor) +{ + moco::TFMean mean_node; + + ASSERT_EQ(mean_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(mean_node.opcode(), moco::TFOpcode::Mean); + + ASSERT_EQ(mean_node.input(), nullptr); + ASSERT_EQ(mean_node.reduction_indices(), nullptr); + ASSERT_EQ(mean_node.keep_dims(), false); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFMul.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFMul.test.cpp new file mode 100644 index 000000000..a4a1ecfd7 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFMul.test.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 "moco/IR/Nodes/TFMul.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFMulTest, constructor) +{ + moco::TFMul mul_node; + + ASSERT_EQ(mul_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(mul_node.opcode(), moco::TFOpcode::Mul); + + ASSERT_EQ(mul_node.x(), nullptr); + ASSERT_EQ(mul_node.y(), nullptr); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFPack.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFPack.test.cpp new file mode 100644 index 000000000..a62b39f3d --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFPack.test.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 "moco/IR/Nodes/TFPack.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFPackTest, constructor) +{ + moco::TFPack pack_node(3); // num of values + + ASSERT_EQ(pack_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(pack_node.opcode(), moco::TFOpcode::Pack); + + ASSERT_EQ(pack_node.N(), 3); + ASSERT_EQ(pack_node.values(0), nullptr); + ASSERT_EQ(pack_node.values(1), nullptr); + ASSERT_EQ(pack_node.values(2), nullptr); + ASSERT_EQ(pack_node.axis(), 0); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFPad.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFPad.test.cpp new file mode 100644 index 000000000..f3f3dcc8c --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFPad.test.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 "moco/IR/Nodes/TFPad.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFPadTest, constructor) +{ + moco::TFPad pad; + + ASSERT_EQ(pad.dialect(), moco::TFDialect::get()); + ASSERT_EQ(pad.opcode(), moco::TFOpcode::Pad); + + ASSERT_EQ(pad.input(), nullptr); + ASSERT_EQ(pad.paddings(), nullptr); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFPlaceholder.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFPlaceholder.test.cpp new file mode 100644 index 000000000..e082f0c3e --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFPlaceholder.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 "moco/IR/Nodes/TFPlaceholder.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFPlaceholderTest, constructor) +{ + moco::TFPlaceholder placeholder; + + ASSERT_EQ(placeholder.dialect(), moco::TFDialect::get()); + ASSERT_EQ(placeholder.opcode(), moco::TFOpcode::Placeholder); + + ASSERT_EQ(placeholder.dtype(), loco::DataType::Unknown); + ASSERT_EQ(placeholder.rank(), 0); + + placeholder.dtype(loco::DataType::FLOAT32); + ASSERT_EQ(placeholder.dtype(), loco::DataType::FLOAT32); + + placeholder.rank(2); + ASSERT_EQ(placeholder.rank(), 2); + + placeholder.dim(0) = 2; + placeholder.dim(1) = 3; + + ASSERT_TRUE(placeholder.dim(0).known()); + ASSERT_TRUE(placeholder.dim(1).known()); + + ASSERT_EQ(placeholder.dim(0), 2); + ASSERT_EQ(placeholder.dim(1), 3); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFRealDiv.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFRealDiv.test.cpp new file mode 100644 index 000000000..bfb8154a6 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFRealDiv.test.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 "moco/IR/Nodes/TFRealDiv.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFRealDivTest, constructor) +{ + moco::TFRealDiv div_node; + + ASSERT_EQ(div_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(div_node.opcode(), moco::TFOpcode::RealDiv); + + ASSERT_EQ(div_node.x(), nullptr); + ASSERT_EQ(div_node.y(), nullptr); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFRelu.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFRelu.test.cpp new file mode 100644 index 000000000..650e2550d --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFRelu.test.cpp @@ -0,0 +1,30 @@ +/* + * 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/IR/Nodes/TFRelu.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFReluTest, constructor) +{ + moco::TFRelu relu_node; + + ASSERT_EQ(relu_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(relu_node.opcode(), moco::TFOpcode::Relu); + + ASSERT_EQ(relu_node.features(), nullptr); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFRelu6.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFRelu6.test.cpp new file mode 100644 index 000000000..9cce83df3 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFRelu6.test.cpp @@ -0,0 +1,30 @@ +/* + * 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/IR/Nodes/TFRelu6.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFRelu6Test, constructor) +{ + moco::TFRelu6 relu6_node; + + ASSERT_EQ(relu6_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(relu6_node.opcode(), moco::TFOpcode::Relu6); + + ASSERT_EQ(relu6_node.features(), nullptr); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFReshape.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFReshape.test.cpp new file mode 100644 index 000000000..514c691e9 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFReshape.test.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 "moco/IR/Nodes/TFReshape.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFReshapeTest, constructor) +{ + moco::TFReshape reshape_node; + + ASSERT_EQ(reshape_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(reshape_node.opcode(), moco::TFOpcode::Reshape); + + ASSERT_EQ(reshape_node.tensor(), nullptr); + ASSERT_EQ(reshape_node.shape(), nullptr); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFRsqrt.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFRsqrt.test.cpp new file mode 100644 index 000000000..e94336dfe --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFRsqrt.test.cpp @@ -0,0 +1,30 @@ +/* + * 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/IR/Nodes//TFRsqrt.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFRsqrtTest, constructor) +{ + moco::TFRsqrt rsqrt_node; + + ASSERT_EQ(rsqrt_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(rsqrt_node.opcode(), moco::TFOpcode::Rsqrt); + + ASSERT_EQ(rsqrt_node.x(), nullptr); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFShape.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFShape.test.cpp new file mode 100644 index 000000000..28110d790 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFShape.test.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 "moco/IR/Nodes//TFShape.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFShapeTest, constructor) +{ + moco::TFShape shape_node; + + ASSERT_EQ(shape_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(shape_node.opcode(), moco::TFOpcode::Shape); + + ASSERT_EQ(shape_node.input(), nullptr); + ASSERT_EQ(shape_node.dtype(), loco::DataType::Unknown); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFSoftmax.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFSoftmax.test.cpp new file mode 100644 index 000000000..67449feac --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFSoftmax.test.cpp @@ -0,0 +1,30 @@ +/* + * 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/IR/Nodes/TFSoftmax.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFSoftmaxTest, constructor) +{ + moco::TFSoftmax softmax_node; + + ASSERT_EQ(softmax_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(softmax_node.opcode(), moco::TFOpcode::Softmax); + + ASSERT_EQ(softmax_node.logits(), nullptr); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFSqrt.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFSqrt.test.cpp new file mode 100644 index 000000000..942769f6c --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFSqrt.test.cpp @@ -0,0 +1,30 @@ +/* + * 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/IR/Nodes/TFSqrt.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFSqrtTest, constructor) +{ + moco::TFSqrt sqrt_node; + + ASSERT_EQ(sqrt_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(sqrt_node.opcode(), moco::TFOpcode::Sqrt); + + ASSERT_EQ(sqrt_node.x(), nullptr); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFSquaredDifference.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFSquaredDifference.test.cpp new file mode 100644 index 000000000..c3ece9b70 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFSquaredDifference.test.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 "moco/IR/Nodes/TFSquaredDifference.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFSquaredDifferenceTest, constructor) +{ + moco::TFSquaredDifference sd_node; + + ASSERT_EQ(sd_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(sd_node.opcode(), moco::TFOpcode::SquaredDifference); + + ASSERT_EQ(sd_node.x(), nullptr); + ASSERT_EQ(sd_node.y(), nullptr); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFSqueeze.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFSqueeze.test.cpp new file mode 100644 index 000000000..034ca70b2 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFSqueeze.test.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 "moco/IR/Nodes/TFSqueeze.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFSqueezeTest, constructor) +{ + moco::TFSqueeze squeeze_node; + + ASSERT_EQ(squeeze_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(squeeze_node.opcode(), moco::TFOpcode::Squeeze); + + ASSERT_EQ(squeeze_node.input(), nullptr); + ASSERT_EQ(squeeze_node.squeeze_dims().size(), 0); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFStopGradient.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFStopGradient.test.cpp new file mode 100644 index 000000000..054ccda41 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFStopGradient.test.cpp @@ -0,0 +1,30 @@ +/* + * 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/IR/Nodes/TFStopGradient.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFStopGradientTest, constructor) +{ + moco::TFStopGradient node; + + ASSERT_EQ(node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(node.opcode(), moco::TFOpcode::StopGradient); + + ASSERT_EQ(node.input(), nullptr); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFStridedSlice.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFStridedSlice.test.cpp new file mode 100644 index 000000000..9e7e45543 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFStridedSlice.test.cpp @@ -0,0 +1,38 @@ +/* + * 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/IR/Nodes/TFStridedSlice.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFStridedSliceTest, constructor) +{ + moco::TFStridedSlice node; + + ASSERT_EQ(node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(node.opcode(), moco::TFOpcode::StridedSlice); + + ASSERT_EQ(node.input(), nullptr); + ASSERT_EQ(node.begin(), nullptr); + ASSERT_EQ(node.end(), nullptr); + ASSERT_EQ(node.strides(), nullptr); + ASSERT_EQ(node.begin_mask(), 0); + ASSERT_EQ(node.end_mask(), 0); + ASSERT_EQ(node.ellipsis_mask(), 0); + ASSERT_EQ(node.new_axis_mask(), 0); + ASSERT_EQ(node.shrink_axis_mask(), 0); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFSub.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFSub.test.cpp new file mode 100644 index 000000000..4b80713bd --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFSub.test.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 "moco/IR/Nodes/TFSub.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFSubTest, constructor) +{ + moco::TFSub sub_node; + + ASSERT_EQ(sub_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(sub_node.opcode(), moco::TFOpcode::Sub); + + ASSERT_EQ(sub_node.x(), nullptr); + ASSERT_EQ(sub_node.y(), nullptr); +} diff --git a/compiler/moco/lang/src/IR/Nodes/TFTanh.test.cpp b/compiler/moco/lang/src/IR/Nodes/TFTanh.test.cpp new file mode 100644 index 000000000..38458a694 --- /dev/null +++ b/compiler/moco/lang/src/IR/Nodes/TFTanh.test.cpp @@ -0,0 +1,30 @@ +/* + * 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/IR/Nodes/TFTanh.h" +#include "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFTanhTest, constructor) +{ + moco::TFTanh tanh_node; + + ASSERT_EQ(tanh_node.dialect(), moco::TFDialect::get()); + ASSERT_EQ(tanh_node.opcode(), moco::TFOpcode::Tanh); + + ASSERT_EQ(tanh_node.x(), nullptr); +} diff --git a/compiler/moco/lang/src/IR/TFDialect.cpp b/compiler/moco/lang/src/IR/TFDialect.cpp new file mode 100644 index 000000000..35bbcc2c9 --- /dev/null +++ b/compiler/moco/lang/src/IR/TFDialect.cpp @@ -0,0 +1,91 @@ +/* + * 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/IR/TFDialect.h" +#include "moco/IR/TFNode.h" + +#include <loco/IR/Graph.h> +#include <loco/IR/GraphInputIndex.h> +#include <loco/IR/GraphOutputIndex.h> + +#include <stdex/Memory.h> + +#include <cassert> +#include <stdexcept> + +namespace +{ + +struct GiiQueryServiceImpl final : public loco::GraphInputIndexQueryService +{ + bool associated(const loco::Node *node) const final + { + if (auto tfplaceholder = dynamic_cast<const moco::TFPlaceholder *>(node)) + { + return moco::indexed(tfplaceholder); + } + return false; + } + + loco::GraphOutputIndex index(const loco::Node *node) const final + { + assert(associated(node)); + auto tfplaceholder = dynamic_cast<const moco::TFPlaceholder *>(node); + assert(tfplaceholder != nullptr); + return moco::index(tfplaceholder); + } +}; + +struct GoiQueryServiceImpl final : public loco::GraphOutputIndexQueryService +{ + bool associated(const loco::Node *node) const final + { + if (auto tfpush = dynamic_cast<const moco::TFPush *>(node)) + { + return tfpush->indexed(); + } + return false; + } + + loco::GraphOutputIndex index(const loco::Node *node) const final + { + assert(associated(node)); + if (auto tfpush = dynamic_cast<const moco::TFPush *>(node)) + { + return tfpush->index(); + } + throw std::invalid_argument("node"); + } +}; + +} // namespace + +namespace moco +{ + +TFDialect::TFDialect() +{ + service<loco::GraphInputIndexQueryService>(stdex::make_unique<GiiQueryServiceImpl>()); + service<loco::GraphOutputIndexQueryService>(stdex::make_unique<GoiQueryServiceImpl>()); +} + +loco::Dialect *TFDialect::get(void) +{ + static TFDialect d; + return &d; +} + +} // namespace moco diff --git a/compiler/moco/lang/src/IR/TFDialect.test.cpp b/compiler/moco/lang/src/IR/TFDialect.test.cpp new file mode 100644 index 000000000..3c8b1a16b --- /dev/null +++ b/compiler/moco/lang/src/IR/TFDialect.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 "moco/IR/TFDialect.h" + +#include <gtest/gtest.h> + +TEST(TFDialectTest, get) +{ + auto d = moco::TFDialect::get(); + + // get() SHOULD return a valid(non-null) pointer + ASSERT_NE(d, nullptr); + // The return value SHOULD be stable across multiple invocations + ASSERT_EQ(d, moco::TFDialect::get()); +} diff --git a/compiler/moco/lang/src/IR/TFNode.cpp b/compiler/moco/lang/src/IR/TFNode.cpp new file mode 100644 index 000000000..ab9356196 --- /dev/null +++ b/compiler/moco/lang/src/IR/TFNode.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 "moco/IR/TFNode.h" +#include "moco/IR/TFDialect.h" + +#include <cassert> + +namespace moco +{ + +const loco::Dialect *TFNode::dialect(void) const { return TFDialect::get(); } + +} // namespace moco + +// TODO move this to appropriate place +#include <stdex/Memory.h> + +namespace moco +{ + +struct GraphInputIndexAnnotation : public loco::NodeAnnotation +{ +public: + GraphInputIndexAnnotation(const loco::GraphInputIndex &index) : _index{index} + { + // DO NOTHING + } + +public: + const loco::GraphInputIndex &index(void) const { return _index; } + +private: + loco::GraphInputIndex _index; +}; + +bool indexed(const TFPlaceholder *node) +{ + return (node->annot<GraphInputIndexAnnotation>() != nullptr); +} + +loco::GraphInputIndex index(const TFPlaceholder *node) +{ + assert(indexed(node)); + return node->annot<GraphInputIndexAnnotation>()->index(); +} + +void index(TFPlaceholder *node, const loco::GraphInputIndex index) +{ + node->annot(stdex::make_unique<GraphInputIndexAnnotation>(index)); +} + +loco::TensorShape tensor_shape(const TFPlaceholder *node) +{ + assert(node != nullptr); + + loco::TensorShape shape; + + uint32_t rank = node->rank(); + shape.rank(rank); + for (uint32_t index = 0; index < rank; ++index) + { + if (node->dim(index).known()) + shape.dim(index) = node->dim(index).value(); + else + shape.dim(index).unset(); + } + + return shape; +} + +TFPlaceholder *placeholder_node(loco::Graph *g, const loco::GraphInputIndex &idx) +{ + for (uint32_t n = 0; n < g->nodes()->size(); ++n) + { + if (auto tfplaceholder = dynamic_cast<TFPlaceholder *>(g->nodes()->at(n))) + { + if (indexed(tfplaceholder) && index(tfplaceholder) == idx) + { + return tfplaceholder; + } + } + } + return nullptr; +} + +} // namespace moco + +namespace moco +{ + +/** + * TFPush + */ + +void TFPush::index(const loco::GraphOutputIndex &index) +{ + // Push internally stores "GraphOutputIndex" as int64_t + _index = static_cast<int64_t>(index); +} + +loco::GraphOutputIndex TFPush::index(void) const +{ + assert(_index >= std::numeric_limits<loco::GraphOutputIndex>::min()); + assert(_index <= std::numeric_limits<loco::GraphOutputIndex>::max()); + return static_cast<loco::GraphOutputIndex>(_index); +} + +TFPush *push_node(loco::Graph *g, const loco::GraphOutputIndex &index) +{ + for (uint32_t n = 0; n < g->nodes()->size(); ++n) + { + if (auto tfpush = dynamic_cast<TFPush *>(g->nodes()->at(n))) + { + if (tfpush->indexed() && tfpush->index() == index) + { + return tfpush; + } + } + } + return nullptr; +} + +} // namespace moco diff --git a/compiler/moco/lang/src/IR/TFNode.test.cpp b/compiler/moco/lang/src/IR/TFNode.test.cpp new file mode 100644 index 000000000..4df1211db --- /dev/null +++ b/compiler/moco/lang/src/IR/TFNode.test.cpp @@ -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. + */ + +#include "moco/IR/Nodes/TFPlaceholder.h" + +#include <loco.h> + +#include <gtest/gtest.h> + +TEST(TFNodeTest_Placeholder, index) +{ + loco::Graph graph; + + auto test_node = graph.nodes()->create<moco::TFPlaceholder>(); + + loco::GraphInputIndex index_set{100}; + moco::index(test_node, index_set); + + auto index_get = moco::index(test_node); + ASSERT_EQ(index_get, index_set); +} + +TEST(TFNodeTest_Placeholder, name) +{ + loco::Graph graph; + + auto test_node = graph.nodes()->create<moco::TFPlaceholder>(); + + test_node->name("PlaceholderName"); + ASSERT_EQ(test_node->name(), "PlaceholderName"); +} diff --git a/compiler/moco/lang/src/IR/VariadicArityNode.test.cpp b/compiler/moco/lang/src/IR/VariadicArityNode.test.cpp new file mode 100644 index 000000000..57361af98 --- /dev/null +++ b/compiler/moco/lang/src/IR/VariadicArityNode.test.cpp @@ -0,0 +1,55 @@ +/* + * 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/IR/VariadicArityNode.h" + +#include <loco/IR/Nodes.h> + +#include <gtest/gtest.h> + +namespace +{ + +using namespace moco; + +class ArbitraryInputNode : public VariadicArityNode<loco::Node> +{ +public: + ArbitraryInputNode(uint32_t arity) : VariadicArityNode<loco::Node>(arity) {} + + void input(uint32_t idx, loco::Node *node) { at(idx)->node(node); } + loco::Node *input(uint32_t idx) const { return at(idx)->node(); } + + const loco::Dialect *dialect(void) const { return nullptr; } // this won't be called for testing + uint32_t opnum(void) const { return -1; } // this won't be called for testing +}; + +} // namespace + +TEST(CustomOpTest, VariadicArityNode_arity_n) +{ + loco::ConstGen cg0, cg1, cg2; + + ArbitraryInputNode a_node(3); + a_node.input(0, &cg0); + a_node.input(1, &cg1); + a_node.input(2, &cg2); + + ASSERT_EQ(a_node.arity(), 3); + ASSERT_EQ(a_node.input(0), &cg0); + ASSERT_EQ(a_node.input(1), &cg1); + ASSERT_EQ(a_node.input(2), &cg2); +} diff --git a/compiler/moco/pass/CMakeLists.txt b/compiler/moco/pass/CMakeLists.txt new file mode 100644 index 000000000..1eba86283 --- /dev/null +++ b/compiler/moco/pass/CMakeLists.txt @@ -0,0 +1,26 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(moco_pass SHARED ${SOURCES}) +target_include_directories(moco_pass PRIVATE src) +target_include_directories(moco_pass PUBLIC include) +target_link_libraries(moco_pass PUBLIC loco) +target_link_libraries(moco_pass PUBLIC logo_core) +target_link_libraries(moco_pass PUBLIC moco_lang) +target_link_libraries(moco_pass PRIVATE moco_support) +target_link_libraries(moco_pass PRIVATE stdex) +target_link_libraries(moco_pass PRIVATE oops) +install(TARGETS moco_pass DESTINATION lib) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(moco_pass_test ${TESTS}) +target_include_directories(moco_pass_test PRIVATE src) +target_link_libraries(moco_pass_test moco_pass) +target_link_libraries(moco_pass_test moco_support) +target_link_libraries(moco_pass_test stdex) diff --git a/compiler/moco/pass/README.md b/compiler/moco/pass/README.md new file mode 100644 index 000000000..51921b8db --- /dev/null +++ b/compiler/moco/pass/README.md @@ -0,0 +1,3 @@ +# pass + +_pass_ provides _moco_ General Graph Passes for Transformation and Optimization diff --git a/compiler/moco/pass/include/moco/Pass/Passes.h b/compiler/moco/pass/include/moco/Pass/Passes.h new file mode 100644 index 000000000..210f0acfc --- /dev/null +++ b/compiler/moco/pass/include/moco/Pass/Passes.h @@ -0,0 +1,32 @@ +/* + * 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_PASS_PASSES_H__ +#define __MOCO_PASS_PASSES_H__ + +#include "Passes/ConstantFoldAdd.h" +#include "Passes/ConstantFoldMul.h" +#include "Passes/ConstantFoldPack.h" +#include "Passes/ConstantFoldStridedSlice.h" +#include "Passes/FuseBinaryIntoPreceding.h" +#include "Passes/RemoveTFIdentityNode.h" +#include "Passes/ResolveConstantShape.h" +#include "Passes/ResolveFusedBatchNorm.h" +#include "Passes/ResolveReshapeWildcardDim.h" +#include "Passes/ResolveSquaredDifference.h" +#include "Passes/SqueezeReduceNode.h" + +#endif // __MOCO_PASS_PASSES_H__ diff --git a/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldAdd.h b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldAdd.h new file mode 100644 index 000000000..ed58d5ee3 --- /dev/null +++ b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldAdd.h @@ -0,0 +1,41 @@ +/* + * 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_PASS_CONSTANTFOLD_ADD_H__ +#define __MOCO_PASS_CONSTANTFOLD_ADD_H__ + +#include <logo/Pass.h> + +#include <loco.h> + +namespace moco +{ + +/** + * @brief Constant folder for Const + Add -> Const + */ +class ConstantFoldAdd : public logo::Pass +{ +public: + const char *name(void) const final { return "ConstantFoldAdd"; } + +public: + bool run(loco::Graph *graph) override; +}; + +} // namespace moco + +#endif // __MOCO_PASS_CONSTANTFOLD_ADD_H__ diff --git a/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldMul.h b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldMul.h new file mode 100644 index 000000000..5528b8612 --- /dev/null +++ b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldMul.h @@ -0,0 +1,41 @@ +/* + * 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_PASS_CONSTANTFOLD_MUL_H__ +#define __MOCO_PASS_CONSTANTFOLD_MUL_H__ + +#include <logo/Pass.h> + +#include <loco.h> + +namespace moco +{ + +/** + * @brief Constant folder for Const + Mul -> Const +*/ +class ConstantFoldMul : public logo::Pass +{ +public: + const char *name(void) const final { return "ConstantFoldMul"; } + +public: + bool run(loco::Graph *graph) override; +}; + +} // namespace moco + +#endif // __MOCO_PASS_CONSTANTFOLD_MUL_H__ diff --git a/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldPack.h b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldPack.h new file mode 100644 index 000000000..fc6bc0ace --- /dev/null +++ b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldPack.h @@ -0,0 +1,43 @@ +/* + * 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_PASS_CONSTANTFOLD_PACK_H__ +#define __MOCO_PASS_CONSTANTFOLD_PACK_H__ + +#include <logo/Pass.h> + +#include <loco.h> + +namespace moco +{ + +// TODO Provide like ConstantFoldPass<TFPack> for ConstantFold extension + +/** + * @brief Constant folder for Const + Pack -> Const +*/ +class ConstantFoldPack : public logo::Pass +{ +public: + const char *name(void) const final { return "ConstantFoldPack"; } + +public: + bool run(loco::Graph *graph) override; +}; + +} // namespace moco + +#endif // __MOCO_PASS_CONSTANTFOLD_PACK_H__ diff --git a/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldStridedSlice.h b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldStridedSlice.h new file mode 100644 index 000000000..1e3492c2c --- /dev/null +++ b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldStridedSlice.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_PASS_CONSTANTFOLD_STRIDEDSLICE_H__ +#define __MOCO_PASS_CONSTANTFOLD_STRIDEDSLICE_H__ + +#include <logo/Pass.h> + +#include <loco.h> + +namespace moco +{ + +/** + * @brief Constant folder for Const + StridedSlice -> Const +*/ +class ConstantFoldStridedSlice : public logo::Pass +{ +public: + const char *name(void) const final { return "ConstantFoldStridedSlice"; } + +public: + bool run(loco::Graph *graph) override; +}; + +} // namespace moco + +#endif // __MOCO_PASS_CONSTANTFOLD_STRIDEDSLICE_H__ diff --git a/compiler/moco/pass/include/moco/Pass/Passes/FuseBinaryIntoPreceding.h b/compiler/moco/pass/include/moco/Pass/Passes/FuseBinaryIntoPreceding.h new file mode 100644 index 000000000..24e3567c0 --- /dev/null +++ b/compiler/moco/pass/include/moco/Pass/Passes/FuseBinaryIntoPreceding.h @@ -0,0 +1,41 @@ +/* + * 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_PASS_FUSE_BINARY_INTO_PRECEDING_H__ +#define __MOCO_PASS_FUSE_BINARY_INTO_PRECEDING_H__ + +#include <logo/Pass.h> + +#include <loco.h> + +namespace moco +{ + +/** + * @brief Fuse TFAdd, TFMul to preceding TFConv2D or TFDepthWiseConv2D +*/ +class FuseBinaryIntoPreceding : public logo::Pass +{ +public: + const char *name(void) const final { return "FuseBinaryIntoPreceding"; } + +public: + bool run(loco::Graph *graph) override; +}; + +} // namespace moco + +#endif // __MOCO_PASS_FUSE_BINARY_INTO_PRECEDING_H__ diff --git a/compiler/moco/pass/include/moco/Pass/Passes/RemoveTFIdentityNode.h b/compiler/moco/pass/include/moco/Pass/Passes/RemoveTFIdentityNode.h new file mode 100644 index 000000000..388249b63 --- /dev/null +++ b/compiler/moco/pass/include/moco/Pass/Passes/RemoveTFIdentityNode.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_PASS_REMOVE_TFIDENTITY_NODE_H__ +#define __MOCO_PASS_REMOVE_TFIDENTITY_NODE_H__ + +#include <logo/Pass.h> + +#include <loco.h> + +namespace moco +{ + +/** + * @brief Use the input of "TFIdentity" node instead + * + * BEFORE: + * [X] -> [TFIdentity] -> [Y] + * + * AFTER: + * [X] -> [Y] + * [TFIdentity] + * + * NOTE This transform does not remove "TFIdentity" node + * This transform is identical to RemoveForwardNode + */ +struct RemoveTFIdentityNode final : public logo::Pass +{ + const char *name(void) const final { return "RemoveTFIdentityNode"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace moco + +#endif // __MOCO_PASS_REMOVE_TFIDENTITY_NODE_H__ diff --git a/compiler/moco/pass/include/moco/Pass/Passes/ResolveConstantShape.h b/compiler/moco/pass/include/moco/Pass/Passes/ResolveConstantShape.h new file mode 100644 index 000000000..16046a052 --- /dev/null +++ b/compiler/moco/pass/include/moco/Pass/Passes/ResolveConstantShape.h @@ -0,0 +1,41 @@ +/* + * 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_PASS_RESOLVE_CONSTANT_SHAPE_H__ +#define __MOCO_PASS_RESOLVE_CONSTANT_SHAPE_H__ + +#include <logo/Pass.h> + +#include <loco.h> + +namespace moco +{ + +/** + * @brief Replace fully determined TFShape node into TFConst + */ +class ResolveConstantShape : public logo::Pass +{ +public: + const char *name(void) const final { return "ResolveConstantShape"; } + +public: + bool run(loco::Graph *graph) override; +}; + +} // namespace moco + +#endif // __MOCO_PASS_RESOLVE_CONSTANT_SHAPE_H__ diff --git a/compiler/moco/pass/include/moco/Pass/Passes/ResolveFusedBatchNorm.h b/compiler/moco/pass/include/moco/Pass/Passes/ResolveFusedBatchNorm.h new file mode 100644 index 000000000..ce5ea0bb0 --- /dev/null +++ b/compiler/moco/pass/include/moco/Pass/Passes/ResolveFusedBatchNorm.h @@ -0,0 +1,41 @@ +/* + * 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_PASS_RESOLVE_FUSEDBATCHNORM_H__ +#define __MOCO_PASS_RESOLVE_FUSEDBATCHNORM_H__ + +#include <logo/Pass.h> + +#include <loco.h> + +namespace moco +{ + +/** + * @brief Trasform TFFusedBatchNorm into TFAdd + TFRsqrt + TFMul + TFBatchNorm +*/ +class ResolveFusedBatchNorm : public logo::Pass +{ +public: + const char *name(void) const final { return "ResolveFusedBatchNorm"; } + +public: + bool run(loco::Graph *graph) override; +}; + +} // namespace moco + +#endif // __MOCO_PASS_RESOLVE_FUSEDBATCHNORM_H__ diff --git a/compiler/moco/pass/include/moco/Pass/Passes/ResolveReshapeWildcardDim.h b/compiler/moco/pass/include/moco/Pass/Passes/ResolveReshapeWildcardDim.h new file mode 100644 index 000000000..137c97379 --- /dev/null +++ b/compiler/moco/pass/include/moco/Pass/Passes/ResolveReshapeWildcardDim.h @@ -0,0 +1,42 @@ +/* + * 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_PASS_RESOLVE_RESHAPE_WILDCARD_DIM_H__ +#define __MOCO_PASS_RESOLVE_RESHAPE_WILDCARD_DIM_H__ + +#include <logo/Pass.h> + +#include <loco.h> + +namespace moco +{ + +/** + * @brief Determine wildcard dimension (denoted as -1) of Reshape's shape input + * if possible + */ +class ResolveReshapeWildcardDim : public logo::Pass +{ +public: + const char *name(void) const final { return "ResolveReshapeWildcardDim"; } + +public: + bool run(loco::Graph *graph) override; +}; + +} // namespace moco + +#endif // __MOCO_PASS_RESOLVE_RESHAPE_WILDCARD_DIM_H__ diff --git a/compiler/moco/pass/include/moco/Pass/Passes/ResolveSquaredDifference.h b/compiler/moco/pass/include/moco/Pass/Passes/ResolveSquaredDifference.h new file mode 100644 index 000000000..1aa78655e --- /dev/null +++ b/compiler/moco/pass/include/moco/Pass/Passes/ResolveSquaredDifference.h @@ -0,0 +1,41 @@ +/* + * 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_PASS_RESOLVE_SQUARED_DIFFERENCE_H__ +#define __MOCO_PASS_RESOLVE_SQUARED_DIFFERENCE_H__ + +#include <logo/Pass.h> + +#include <loco.h> + +namespace moco +{ + +/** + * @brief Decompose TFSquaredDifference to TFSub, TFMul + */ +class ResolveSquaredDifference : public logo::Pass +{ +public: + const char *name(void) const final { return "ResolveSquaredDifference"; } + +public: + bool run(loco::Graph *graph) override; +}; + +} // namespace moco + +#endif // __MOCO_PASS_RESOLVE_SQUARED_DIFFERENCE_H__ diff --git a/compiler/moco/pass/include/moco/Pass/Passes/SqueezeReduceNode.h b/compiler/moco/pass/include/moco/Pass/Passes/SqueezeReduceNode.h new file mode 100644 index 000000000..d4a3e65c6 --- /dev/null +++ b/compiler/moco/pass/include/moco/Pass/Passes/SqueezeReduceNode.h @@ -0,0 +1,42 @@ +/* + * 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_PASS_SQUEEZE_REDUCE_NODE_H__ +#define __MOCO_PASS_SQUEEZE_REDUCE_NODE_H__ + +#include <logo/Pass.h> + +#include <loco.h> + +namespace moco +{ + +/** + * @brief If ReduceTypeOP don't keep dimensions, replace the ReduceTypeOp + * as new one to keep dimensions and insert TFSqueeze + */ +class SqueezeReduceNode : public logo::Pass +{ +public: + const char *name(void) const final { return "SqueezeReduceNode"; } + +public: + bool run(loco::Graph *graph) override; +}; + +} // namespace moco + +#endif // __MOCO_PASS_SQUEEZE_REDUCE_NODE_H__ diff --git a/compiler/moco/pass/src/ConstantFoldAdd.test.cpp b/compiler/moco/pass/src/ConstantFoldAdd.test.cpp new file mode 100644 index 000000000..bc9489fbd --- /dev/null +++ b/compiler/moco/pass/src/ConstantFoldAdd.test.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 "moco/Pass/Passes/ConstantFoldAdd.h" +#include "TestHelper.h" + +#include <moco/IR/TFNodes.h> +#include <loco.h> +#include <stdex/Memory.h> + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ + +moco::TFConst *const_vector_init(loco::Graph *graph, std::vector<int32_t> values) +{ + auto const_node = graph->nodes()->create<moco::TFConst>(); + auto dim = values.size(); + + const_node->dtype(loco::DataType::S32); + const_node->rank(1); + const_node->dim(0).set(dim); + + const_node->size<loco::DataType::S32>(dim); + for (int32_t i = 0; i < dim; ++i) + const_node->at<loco::DataType::S32>(i) = values[i]; + + return const_node; +} + +} // namespace + +TEST(ConstantFoldAdd, basic_vector) +{ + loco::Graph graph; + + auto add_node = graph.nodes()->create<moco::TFAdd>(); + { + auto const_from_ss = const_vector_init(&graph, {1, 3, 5}); + add_node->x(const_from_ss); + + auto const_y = const_vector_init(&graph, {2}); + add_node->y(const_y); + } + setup_output_node(&graph, add_node); + + auto pass = stdex::make_unique<moco::ConstantFoldAdd>(); + bool cont = true; + while (cont) + { + cont = pass->run(&graph); + } + + auto ssnode = find_first_node_bytype<moco::TFAdd>(&graph); + ASSERT_EQ(ssnode, nullptr); + + auto ssconst = find_first_node_bytype<moco::TFConst>(&graph); + ASSERT_NE(ssconst, nullptr); + ASSERT_EQ(ssconst->size<loco::DataType::S32>(), 3); + ASSERT_EQ(ssconst->at<loco::DataType::S32>(0), 3); + ASSERT_EQ(ssconst->at<loco::DataType::S32>(1), 5); + ASSERT_EQ(ssconst->at<loco::DataType::S32>(2), 7); +} + +TEST(ConstantFoldAdd, basic_refinedet_1) +{ + loco::Graph graph; + + auto add_node = graph.nodes()->create<moco::TFAdd>(); + { + auto const_from_ss = const_vector_init(&graph, {10}); + add_node->x(const_from_ss); + + auto const_y = const_vector_init(&graph, {0}); + add_node->y(const_y); + } + setup_output_node(&graph, add_node); + + auto pass = stdex::make_unique<moco::ConstantFoldAdd>(); + bool cont = true; + while (cont) + { + cont = pass->run(&graph); + } + + auto ssnode = find_first_node_bytype<moco::TFAdd>(&graph); + ASSERT_EQ(ssnode, nullptr); + + auto ssconst = find_first_node_bytype<moco::TFConst>(&graph); + ASSERT_NE(ssconst, nullptr); + ASSERT_EQ(ssconst->size<loco::DataType::S32>(), 1); + ASSERT_EQ(ssconst->at<loco::DataType::S32>(0), 10); +} diff --git a/compiler/moco/pass/src/ConstantFoldHelper.cpp b/compiler/moco/pass/src/ConstantFoldHelper.cpp new file mode 100644 index 000000000..79b04863c --- /dev/null +++ b/compiler/moco/pass/src/ConstantFoldHelper.cpp @@ -0,0 +1,238 @@ +/* + * 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 "ConstantFoldHelper.h" + +#include <cassert> +#include <sstream> +#include <string> + +namespace +{ + +// TODO this may need to be moved to loco +bool same_shape(const loco::TensorShape *lhs, const loco::TensorShape *rhs) +{ + if (lhs->rank() != rhs->rank()) + return false; + + for (uint32_t r = 0; r < lhs->rank(); r++) + { + if (lhs->dim(r).value() != rhs->dim(r).value()) + return false; + } + return true; +} + +} // namespace + +namespace moco +{ + +TFConst *new_const(loco::Graph *graph, loco::TensorShape &tensor_shape, const loco::DataType &dtype) +{ + assert(dtype == loco::DataType::S32 || dtype == loco::DataType::FLOAT32); + + auto const_node = graph->nodes()->create<TFConst>(); + const_node->dtype(dtype); + const_node->rank(tensor_shape.rank()); + + // Calc number of elements for target node and set shape + uint32_t num_elements = 1; + for (uint32_t r = 0; r < tensor_shape.rank(); r++) + { + const_node->dim(r) = tensor_shape.dim(r); + assert(const_node->dim(r).known()); + num_elements = num_elements * const_node->dim(r).value(); + } + if (dtype == loco::DataType::S32) + const_node->size<loco::DataType::S32>(num_elements); + else if (dtype == loco::DataType::FLOAT32) + const_node->size<loco::DataType::FLOAT32>(num_elements); + + // give name for this node from address to be unique + std::ostringstream oss; + oss << "Const_" << (void *)const_node; + const_node->name(oss.str()); + + return const_node; +} + +} // namespace moco + +namespace moco +{ + +template <> int32_t scalar_from_const<int32_t>(const TFConst *tfconst) +{ + assert(tfconst->rank() == 0 || tfconst->rank() == 1); + assert(tfconst->dtype() == loco::DataType::S32); + return tfconst->at<loco::DataType::S32>(0); +} + +template <> float scalar_from_const<float>(const TFConst *tfconst) +{ + assert(tfconst->rank() == 0 || tfconst->rank() == 1); + assert(tfconst->dtype() == loco::DataType::FLOAT32); + return tfconst->at<loco::DataType::FLOAT32>(0); +} + +bool valid_shape_for_constfold_binary_op(const loco::TensorShape &lhs, const loco::TensorShape &rhs) +{ + // scalar + if (lhs.rank() == 0 || rhs.rank() == 0) + return true; + + // same as scalar + if (lhs.rank() == 1 && lhs.dim(0).value() == 1) + return true; + if (rhs.rank() == 1 && rhs.dim(0).value() == 1) + return true; + + // for elementwise binary operation + return ::same_shape(&lhs, &rhs); +} + +} // namespace moco + +namespace moco +{ + +float BinaryFunc::apply(float, float) const +{ + throw std::runtime_error{"F32 is not supported yet"}; +} + +int32_t BinaryFunc::apply(int32_t, int32_t) const +{ + throw std::runtime_error{"S32 is not supported yet"}; +} + +} // namespace moco + +namespace +{ + +void apply_binary_s32(const moco::TFConst *lhs, int32_t rhs, moco::TFConst *output, + const moco::BinaryFunc &f) +{ + assert(lhs->dtype() == loco::DataType::S32); + assert(same_shape(lhs, output)); + + uint32_t nume = num_elements(lhs); + for (uint32_t e = 0; e < nume; e++) + { + output->at<loco::DataType::S32>(e) = f.apply(lhs->at<loco::DataType::S32>(e), rhs); + } +} + +void apply_binary_f32(const moco::TFConst *lhs, float rhs, moco::TFConst *output, + const moco::BinaryFunc &f) +{ + assert(lhs->dtype() == loco::DataType::FLOAT32); + assert(same_shape(lhs, output)); + + uint32_t nume = num_elements(lhs); + for (uint32_t e = 0; e < nume; e++) + { + output->at<loco::DataType::FLOAT32>(e) = f.apply(lhs->at<loco::DataType::FLOAT32>(e), rhs); + } +} + +void apply_binary_s32(const moco::TFConst *lhs, const moco::TFConst *rhs, moco::TFConst *output, + const moco::BinaryFunc &f) +{ + assert(same_shape(output, lhs)); + assert(same_shape(output, rhs)); + assert(output->dtype() == lhs->dtype()); + assert(output->dtype() == rhs->dtype()); + + uint32_t nume = num_elements(lhs); + for (uint32_t e = 0; e < nume; e++) + { + output->at<loco::DataType::S32>(e) = + f.apply(lhs->at<loco::DataType::S32>(e), rhs->at<loco::DataType::S32>(e)); + } +} + +void apply_binary_f32(const moco::TFConst *lhs, const moco::TFConst *rhs, moco::TFConst *output, + const moco::BinaryFunc &f) +{ + assert(same_shape(output, lhs)); + assert(same_shape(output, rhs)); + assert(output->dtype() == lhs->dtype()); + assert(output->dtype() == rhs->dtype()); + + uint32_t nume = num_elements(lhs); + for (uint32_t e = 0; e < nume; e++) + { + output->at<loco::DataType::FLOAT32>(e) = + f.apply(lhs->at<loco::DataType::FLOAT32>(e), rhs->at<loco::DataType::FLOAT32>(e)); + } +} + +} // namespace + +namespace moco +{ + +template <> +void apply_binary<int32_t>(const moco::TFConst *x_const, const moco::TFConst *y_const, + moco::TFConst *output_const, const moco::BinaryFunc &f) +{ + auto x_shape = moco::tensor_shape(x_const); + auto y_shape = moco::tensor_shape(y_const); + + if (y_shape.rank() == 0 || y_shape.rank() == 1) + { + auto rhs = scalar_from_const<int32_t>(y_const); + apply_binary_s32(x_const, rhs, output_const, f); + } + else if (x_shape.rank() == 0 || x_shape.rank() == 1) + { + auto rhs = scalar_from_const<int32_t>(x_const); + apply_binary_s32(y_const, rhs, output_const, f); + } + else + { + apply_binary_f32(x_const, y_const, output_const, f); + } +} + +template <> +void apply_binary<float>(const moco::TFConst *x_const, const moco::TFConst *y_const, + moco::TFConst *output_const, const moco::BinaryFunc &f) +{ + auto x_shape = moco::tensor_shape(x_const); + auto y_shape = moco::tensor_shape(y_const); + + if (y_shape.rank() == 0 || y_shape.rank() == 1) + { + auto rhs = scalar_from_const<float>(y_const); + apply_binary_f32(x_const, rhs, output_const, f); + } + else if (x_shape.rank() == 0 || x_shape.rank() == 1) + { + auto rhs = scalar_from_const<float>(x_const); + apply_binary_f32(y_const, rhs, output_const, f); + } + else + { + apply_binary_f32(x_const, y_const, output_const, f); + } +} + +} // namespace moco diff --git a/compiler/moco/pass/src/ConstantFoldHelper.h b/compiler/moco/pass/src/ConstantFoldHelper.h new file mode 100644 index 000000000..393b083f2 --- /dev/null +++ b/compiler/moco/pass/src/ConstantFoldHelper.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_CONSTANT_FOLD_HELPER_H__ +#define __MOCO_CONSTANT_FOLD_HELPER_H__ + +#include <moco/IR/Nodes/TFConst.h> + +#include <loco.h> +#include <loco/IR/TensorShape.h> + +namespace moco +{ + +TFConst *new_const(loco::Graph *graph, loco::TensorShape &tensor_shape, + const loco::DataType &dtype); + +template <typename T> T scalar_from_const(const TFConst *tfconst); +template <> int32_t scalar_from_const<int32_t>(const TFConst *tfconst); +template <> float scalar_from_const<float>(const TFConst *tfconst); + +/** + * @note Check if it is valid to run Constant folding for binary operations + * as-of current implementation. That is currently we support for + * element-wise or one of the input is scalar. + * TODO Support other shapes of binary operation + */ +bool valid_shape_for_constfold_binary_op(const loco::TensorShape &lhs, + const loco::TensorShape &rhs); + +struct BinaryFunc +{ + virtual ~BinaryFunc() = default; + + virtual float apply(float, float) const; + virtual int32_t apply(int32_t, int32_t) const; +}; + +template <typename T> +void apply_binary(const moco::TFConst *x_const, const moco::TFConst *y_const, + moco::TFConst *output_const, const moco::BinaryFunc &f); +template <> +void apply_binary<int32_t>(const moco::TFConst *x_const, const moco::TFConst *y_const, + moco::TFConst *output_const, const moco::BinaryFunc &f); +template <> +void apply_binary<float>(const moco::TFConst *x_const, const moco::TFConst *y_const, + moco::TFConst *output_const, const moco::BinaryFunc &f); + +} // namespace moco + +#endif // __MOCO_CONSTANT_FOLD_HELPER_H__ diff --git a/compiler/moco/pass/src/ConstantFoldMul.test.cpp b/compiler/moco/pass/src/ConstantFoldMul.test.cpp new file mode 100644 index 000000000..4e9b78fd4 --- /dev/null +++ b/compiler/moco/pass/src/ConstantFoldMul.test.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 "moco/Pass/Passes/ConstantFoldMul.h" +#include "TestHelper.h" + +#include <moco/IR/TFNodes.h> +#include <loco.h> +#include <stdex/Memory.h> + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ + +moco::TFConst *const_vector_init(loco::Graph *graph, std::vector<int32_t> values) +{ + auto const_node = graph->nodes()->create<moco::TFConst>(); + auto dim = values.size(); + + const_node->dtype(loco::DataType::S32); + const_node->rank(1); + const_node->dim(0).set(dim); + + const_node->size<loco::DataType::S32>(dim); + for (int32_t i = 0; i < dim; ++i) + const_node->at<loco::DataType::S32>(i) = values[i]; + + return const_node; +} + +} // namespace + +TEST(ConstantFoldMul, basic_vector) +{ + loco::Graph graph; + + auto mul_node = graph.nodes()->create<moco::TFMul>(); + { + auto const_from_ss = const_vector_init(&graph, {1, 3, 5}); + mul_node->x(const_from_ss); + + auto const_y = const_vector_init(&graph, {2}); + mul_node->y(const_y); + } + setup_output_node(&graph, mul_node); + + auto pass = stdex::make_unique<moco::ConstantFoldMul>(); + bool cont = true; + while (cont) + { + cont = pass->run(&graph); + } + + auto ssnode = find_first_node_bytype<moco::TFMul>(&graph); + ASSERT_EQ(ssnode, nullptr); + + auto ssconst = find_first_node_bytype<moco::TFConst>(&graph); + ASSERT_NE(ssconst, nullptr); + ASSERT_EQ(ssconst->size<loco::DataType::S32>(), 3); + ASSERT_EQ(ssconst->at<loco::DataType::S32>(0), 2); + ASSERT_EQ(ssconst->at<loco::DataType::S32>(1), 6); + ASSERT_EQ(ssconst->at<loco::DataType::S32>(2), 10); +} + +TEST(ConstantFoldMul, basic_refinedet_1) +{ + loco::Graph graph; + + auto mul_node = graph.nodes()->create<moco::TFMul>(); + { + auto const_from_ss = const_vector_init(&graph, {5}); + mul_node->x(const_from_ss); + + auto const_y = const_vector_init(&graph, {2}); + mul_node->y(const_y); + } + setup_output_node(&graph, mul_node); + + auto pass = stdex::make_unique<moco::ConstantFoldMul>(); + bool cont = true; + while (cont) + { + cont = pass->run(&graph); + } + + auto ssnode = find_first_node_bytype<moco::TFMul>(&graph); + ASSERT_EQ(ssnode, nullptr); + + auto ssconst = find_first_node_bytype<moco::TFConst>(&graph); + ASSERT_NE(ssconst, nullptr); + ASSERT_EQ(ssconst->size<loco::DataType::S32>(), 1); + ASSERT_EQ(ssconst->at<loco::DataType::S32>(0), 10); +} diff --git a/compiler/moco/pass/src/ConstantFoldPack.test.cpp b/compiler/moco/pass/src/ConstantFoldPack.test.cpp new file mode 100644 index 000000000..cb6eff0c8 --- /dev/null +++ b/compiler/moco/pass/src/ConstantFoldPack.test.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 "moco/Pass/Passes/ConstantFoldPack.h" +#include "TestHelper.h" + +#include <moco/IR/TFNodes.h> +#include <loco.h> +#include <stdex/Memory.h> + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ + +moco::TFConst *const_vector_init(loco::Graph *graph, std::vector<int32_t> values) +{ + auto const_node = graph->nodes()->create<moco::TFConst>(); + auto dim = values.size(); + + const_node->dtype(loco::DataType::S32); + const_node->rank(1); + const_node->dim(0).set(dim); + + const_node->size<loco::DataType::S32>(dim); + for (int32_t i = 0; i < dim; ++i) + const_node->at<loco::DataType::S32>(i) = values[i]; + + return const_node; +} + +} // namespace + +TEST(ConstantFoldPack, basic_scalar4_vector) +{ + loco::Graph graph; + + auto pack_node = graph.nodes()->create<moco::TFPack>(4); + { + auto input_0 = const_vector_init(&graph, {1}); + pack_node->values(0, input_0); + + auto input_1 = const_vector_init(&graph, {10}); + pack_node->values(1, input_1); + + auto input_2 = const_vector_init(&graph, {10}); + pack_node->values(2, input_2); + + auto input_3 = const_vector_init(&graph, {64}); + pack_node->values(3, input_3); + } + // add Identity node as the output Pack will be replaced + auto identity = graph.nodes()->create<moco::TFIdentity>(); + identity->input(pack_node); + setup_output_node(&graph, identity); + + auto pass = stdex::make_unique<moco::ConstantFoldPack>(); + bool cont = true; + while (cont) + { + cont = pass->run(&graph); + } + + auto pnode = find_first_node_bytype<moco::TFPack>(&graph); + ASSERT_EQ(pnode, nullptr); + + auto pconst = find_first_node_bytype<moco::TFConst>(&graph); + ASSERT_NE(pconst, nullptr); + ASSERT_EQ(pconst->rank(), 2); + ASSERT_EQ(pconst->size<loco::DataType::S32>(), 4); + ASSERT_EQ(pconst->at<loco::DataType::S32>(0), 1); + ASSERT_EQ(pconst->at<loco::DataType::S32>(1), 10); + ASSERT_EQ(pconst->at<loco::DataType::S32>(2), 10); + ASSERT_EQ(pconst->at<loco::DataType::S32>(3), 64); +} diff --git a/compiler/moco/pass/src/ConstantFoldStridedSlice.test.cpp b/compiler/moco/pass/src/ConstantFoldStridedSlice.test.cpp new file mode 100644 index 000000000..b5bada221 --- /dev/null +++ b/compiler/moco/pass/src/ConstantFoldStridedSlice.test.cpp @@ -0,0 +1,268 @@ +/* + * 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 "moco/Pass/Passes/ConstantFoldStridedSlice.h" +#include "TestHelper.h" + +#include <moco/IR/TFNodes.h> +#include <loco.h> +#include <stdex/Memory.h> + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ + +moco::TFConst *const_vector_init(loco::Graph *graph, std::vector<int32_t> values) +{ + auto const_node = graph->nodes()->create<moco::TFConst>(); + auto dim = values.size(); + + const_node->dtype(loco::DataType::S32); + const_node->rank(1); + const_node->dim(0).set(dim); + + const_node->size<loco::DataType::S32>(dim); + for (int32_t i = 0; i < dim; ++i) + const_node->at<loco::DataType::S32>(i) = values[i]; + + return const_node; +} + +moco::TFConst *const_matrix(loco::Graph *graph, int32_t dimh, int32_t dimw) +{ + auto const_node = graph->nodes()->create<moco::TFConst>(); + + const_node->dtype(loco::DataType::S32); + const_node->rank(2); + const_node->dim(0).set(dimh); + const_node->dim(1).set(dimw); + + auto elements = dimh * dimw; + const_node->size<loco::DataType::S32>(elements); + for (int32_t i = 0; i < elements; ++i) + const_node->at<loco::DataType::S32>(i) = i; + + return const_node; +} + +} // namespace + +TEST(ConstantFoldStridedSlice, basic_matrix55_11) +{ + loco::Graph graph; + + auto sslice_node = graph.nodes()->create<moco::TFStridedSlice>(); + { + auto const_input = const_matrix(&graph, 5, 5); + sslice_node->input(const_input); + + auto const_begin = const_vector_init(&graph, {1, 1}); + sslice_node->begin(const_begin); + auto const_end = const_vector_init(&graph, {2, 4}); + sslice_node->end(const_end); + auto const_strides = const_vector_init(&graph, {1, 1}); + sslice_node->strides(const_strides); + + sslice_node->shrink_axis_mask(1); + } + setup_output_node(&graph, sslice_node); + + auto pass = stdex::make_unique<moco::ConstantFoldStridedSlice>(); + bool cont = true; + while (cont) + { + cont = pass->run(&graph); + } + + auto ssnode = find_first_node_bytype<moco::TFStridedSlice>(&graph); + ASSERT_EQ(ssnode, nullptr); + + auto ssconst = find_first_node_bytype<moco::TFConst>(&graph); + ASSERT_NE(ssconst, nullptr); + ASSERT_EQ(ssconst->size<loco::DataType::S32>(), 3); + ASSERT_EQ(ssconst->at<loco::DataType::S32>(0), 6); + ASSERT_EQ(ssconst->at<loco::DataType::S32>(1), 7); + ASSERT_EQ(ssconst->at<loco::DataType::S32>(2), 8); +} + +TEST(ConstantFoldStridedSlice, basic_vector4_0) +{ + loco::Graph graph; + + auto sslice_node = graph.nodes()->create<moco::TFStridedSlice>(); + { + auto const_input = const_vector_init(&graph, {1, 5, 5, 64}); + sslice_node->input(const_input); + + auto const_begin = const_vector_init(&graph, {0}); + sslice_node->begin(const_begin); + auto const_end = const_vector_init(&graph, {1}); + sslice_node->end(const_end); + auto const_strides = const_vector_init(&graph, {1}); + sslice_node->strides(const_strides); + + sslice_node->shrink_axis_mask(1); + } + setup_output_node(&graph, sslice_node); + + auto pass = stdex::make_unique<moco::ConstantFoldStridedSlice>(); + bool cont = true; + while (cont) + { + cont = pass->run(&graph); + } + + auto ssnode = find_first_node_bytype<moco::TFStridedSlice>(&graph); + ASSERT_EQ(ssnode, nullptr); + + auto ssconst = find_first_node_bytype<moco::TFConst>(&graph); + ASSERT_NE(ssconst, nullptr); + ASSERT_EQ(ssconst->size<loco::DataType::S32>(), 1); + ASSERT_EQ(ssconst->at<loco::DataType::S32>(0), 1); +} + +TEST(ConstantFoldStridedSlice, basic_vector4_1) +{ + loco::Graph graph; + + auto sslice_node = graph.nodes()->create<moco::TFStridedSlice>(); + { + auto const_input = const_vector_init(&graph, {1, 5, 5, 64}); + sslice_node->input(const_input); + + auto const_begin = const_vector_init(&graph, {1}); + sslice_node->begin(const_begin); + auto const_end = const_vector_init(&graph, {2}); + sslice_node->end(const_end); + auto const_strides = const_vector_init(&graph, {1}); + sslice_node->strides(const_strides); + + sslice_node->shrink_axis_mask(1); + } + setup_output_node(&graph, sslice_node); + + auto pass = stdex::make_unique<moco::ConstantFoldStridedSlice>(); + bool cont = true; + while (cont) + { + cont = pass->run(&graph); + } + + auto ssnode = find_first_node_bytype<moco::TFStridedSlice>(&graph); + ASSERT_EQ(ssnode, nullptr); + + auto ssconst = find_first_node_bytype<moco::TFConst>(&graph); + ASSERT_NE(ssconst, nullptr); + ASSERT_EQ(ssconst->size<loco::DataType::S32>(), 1); + ASSERT_EQ(ssconst->at<loco::DataType::S32>(0), 5); +} + +TEST(ConstantFoldStridedSlice, basic_vector4_2) +{ + loco::Graph graph; + + auto sslice_node = graph.nodes()->create<moco::TFStridedSlice>(); + { + auto const_input = const_vector_init(&graph, {1, 5, 5, 64}); + sslice_node->input(const_input); + + auto const_begin = const_vector_init(&graph, {2}); + sslice_node->begin(const_begin); + auto const_end = const_vector_init(&graph, {3}); + sslice_node->end(const_end); + auto const_strides = const_vector_init(&graph, {1}); + sslice_node->strides(const_strides); + + sslice_node->shrink_axis_mask(1); + } + setup_output_node(&graph, sslice_node); + + auto pass = stdex::make_unique<moco::ConstantFoldStridedSlice>(); + bool cont = true; + while (cont) + { + cont = pass->run(&graph); + } + + auto ssnode = find_first_node_bytype<moco::TFStridedSlice>(&graph); + ASSERT_EQ(ssnode, nullptr); + + auto ssconst = find_first_node_bytype<moco::TFConst>(&graph); + ASSERT_NE(ssconst, nullptr); + ASSERT_EQ(ssconst->size<loco::DataType::S32>(), 1); + ASSERT_EQ(ssconst->at<loco::DataType::S32>(0), 5); +} + +namespace +{ + +/** + * @note tfconst_at() implementation should be same as that of inside + * ConstantFoldStridedSlice.cpp for valid testing + */ +int32_t tfconst_at(const moco::TFConst *tfconst, const std::vector<uint32_t> &pos) +{ + uint32_t rank = tfconst->rank(); + assert(rank == pos.size()); + + uint32_t element = 0; + for (uint32_t r = 0; r < rank; ++r) + { + uint32_t dim = tfconst->dim(r).value(); + element = element * dim + pos.at(r); + } + return tfconst->at<loco::DataType::S32>(element); +} + +} // namespace + +TEST(ConstantFoldStridedSlice, tfconst_at) +{ + loco::Graph graph; + + auto const_node = graph.nodes()->create<moco::TFConst>(); + + const_node->dtype(loco::DataType::S32); + const_node->rank(3); + const_node->dim(0).set(2); + const_node->dim(1).set(3); + const_node->dim(2).set(4); + + auto elements = 2 * 3 * 4; + const_node->size<loco::DataType::S32>(elements); + for (int32_t i = 0; i < elements; ++i) + const_node->at<loco::DataType::S32>(i) = i; + /* + [ + [ 0, 1, 2, 3] <- [0,0,0] + [ 4, 5, 6, 7] <- [0,1,0] [0,1,1] [0,1,2] + [ 8, 9,10,11] + ] + [ + [12,13,14,15] + [16,17,18,19] <- [1,1,0] [1,1,1] + [20,21,22,23] <- [1,2,0] [1,2,1] [1,2,2] [1,2,3] + ] + */ + + ASSERT_EQ(tfconst_at(const_node, {0, 0, 0}), 0); + ASSERT_EQ(tfconst_at(const_node, {1, 1, 1}), 17); + ASSERT_EQ(tfconst_at(const_node, {0, 1, 2}), 6); + ASSERT_EQ(tfconst_at(const_node, {1, 2, 3}), 23); +} diff --git a/compiler/moco/pass/src/Passes/ConstantFoldAdd.cpp b/compiler/moco/pass/src/Passes/ConstantFoldAdd.cpp new file mode 100644 index 000000000..018749b78 --- /dev/null +++ b/compiler/moco/pass/src/Passes/ConstantFoldAdd.cpp @@ -0,0 +1,116 @@ +/* + * 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/Pass/Passes/ConstantFoldAdd.h" + +#include "ConstantFoldHelper.h" + +#include <moco/IR/Nodes/TFAdd.h> +#include <moco/IR/Nodes/TFConst.h> + +#include <moco/Support/NodeAs.h> + +namespace +{ + +struct Func final : public moco::BinaryFunc +{ + float apply(float lhs, float rhs) const { return lhs + rhs; } + int32_t apply(int32_t lhs, int32_t rhs) const { return lhs + rhs; } +}; + +bool constantfold_add(moco::TFAdd *node) +{ + auto x_const = moco::as<moco::TFConst>(node->x()); + auto y_const = moco::as<moco::TFConst>(node->y()); + if (x_const == nullptr || y_const == nullptr) + return false; + + if (x_const->dtype() != y_const->dtype()) + return false; + // TODO support other types + if (x_const->dtype() != loco::DataType::S32 && x_const->dtype() != loco::DataType::FLOAT32) + return false; + + // NOTE we support limited shape of elementwise add or add with a scalar. + // valid_shape_for_constfold_binary_op() explains limited shape. + auto x_shape = moco::tensor_shape(x_const); + auto y_shape = moco::tensor_shape(y_const); + if (!moco::valid_shape_for_constfold_binary_op(x_shape, y_shape)) + return false; + + loco::TensorShape output_shape; + if (y_shape.rank() == 0 || y_shape.rank() == 1) + output_shape = x_shape; + else + output_shape = y_shape; + + auto graph = node->graph(); + auto output_const = moco::new_const(graph, output_shape, x_const->dtype()); + Func f; + + if (x_const->dtype() == loco::DataType::S32) + { + moco::apply_binary<int32_t>(x_const, y_const, output_const, f); + } + else if (x_const->dtype() == loco::DataType::FLOAT32) + { + moco::apply_binary<float>(x_const, y_const, output_const, f); + } + + // replace + loco::replace(node).with(output_const); + + return true; +} + +} // namespace + +namespace moco +{ + +/** + * @note This will Replace TFAdd with TFConst when inputs are TFConst + * + * Before + * A --- TFAdd --- C + * B --/ + * After + * A --- TFAdd + * B --/ + * TFConst ---------- C + * Where + * A,B : inputs of TFAdd + * C : a node that uses TFAdd as an input + * TFAdd is disconnected from C + * Nodes are drawn multiple times to simplify the diagram + */ +bool ConstantFoldAdd::run(loco::Graph *graph) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(graph))) + { + if (auto add_node = as<moco::TFAdd>(node)) + { + if (constantfold_add(add_node)) + changed = true; + } + } + + return changed; +} + +} // namespace moco diff --git a/compiler/moco/pass/src/Passes/ConstantFoldMul.cpp b/compiler/moco/pass/src/Passes/ConstantFoldMul.cpp new file mode 100644 index 000000000..c1870ffee --- /dev/null +++ b/compiler/moco/pass/src/Passes/ConstantFoldMul.cpp @@ -0,0 +1,116 @@ +/* + * 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/Pass/Passes/ConstantFoldMul.h" + +#include "ConstantFoldHelper.h" + +#include <moco/IR/Nodes/TFMul.h> +#include <moco/IR/Nodes/TFConst.h> + +#include <moco/Support/NodeAs.h> + +namespace +{ + +struct Func final : public moco::BinaryFunc +{ + float apply(float lhs, float rhs) const { return lhs * rhs; } + int32_t apply(int32_t lhs, int32_t rhs) const { return lhs * rhs; } +}; + +bool constantfold_mul(moco::TFMul *node) +{ + auto x_const = moco::as<moco::TFConst>(node->x()); + auto y_const = moco::as<moco::TFConst>(node->y()); + if (x_const == nullptr || y_const == nullptr) + return false; + + if (x_const->dtype() != y_const->dtype()) + return false; + // TODO support other types + if (x_const->dtype() != loco::DataType::S32 && x_const->dtype() != loco::DataType::FLOAT32) + return false; + + // NOTE we support limited shape of elementwise mul or multiply with a scalar. + // valid_shape_for_constfold_binary_op() explains limited shape. + auto x_shape = moco::tensor_shape(x_const); + auto y_shape = moco::tensor_shape(y_const); + if (!moco::valid_shape_for_constfold_binary_op(x_shape, y_shape)) + return false; + + loco::TensorShape output_shape; + if (y_shape.rank() == 0 || y_shape.rank() == 1) + output_shape = x_shape; + else + output_shape = y_shape; + + auto graph = node->graph(); + auto output_const = moco::new_const(graph, output_shape, x_const->dtype()); + Func f; + + if (x_const->dtype() == loco::DataType::S32) + { + moco::apply_binary<int32_t>(x_const, y_const, output_const, f); + } + else if (x_const->dtype() == loco::DataType::FLOAT32) + { + moco::apply_binary<float>(x_const, y_const, output_const, f); + } + + // replace + loco::replace(node).with(output_const); + + return true; +} + +} // namespace + +namespace moco +{ + +/** + * @note This will Replace TFMul with TFConst when input are TFConst + * + * Before + * A --- TFMul --- C + * B --/ + * After + * A --- TFMul + * B --/ + * TFConst ---------- C + * Where + * A,B : inputs of TFMul + * C : a node that uses TFMul as an input + * TFMul is disconnected from C + * Nodes are drawn multiple times to simplify the diagram + */ +bool ConstantFoldMul::run(loco::Graph *graph) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(graph))) + { + if (auto mul_node = as<moco::TFMul>(node)) + { + if (constantfold_mul(mul_node)) + changed = true; + } + } + + return changed; +} + +} // namespace moco diff --git a/compiler/moco/pass/src/Passes/ConstantFoldPack.cpp b/compiler/moco/pass/src/Passes/ConstantFoldPack.cpp new file mode 100644 index 000000000..cc8a23d18 --- /dev/null +++ b/compiler/moco/pass/src/Passes/ConstantFoldPack.cpp @@ -0,0 +1,191 @@ +/* + * 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/Pass/Passes/ConstantFoldPack.h" + +#include "ConstantFoldHelper.h" +#include "TensorPackEnumerator.h" + +#include <moco/IR/Nodes/TFPack.h> +#include <moco/IR/Nodes/TFConst.h> + +#include <moco/Support/NodeAs.h> + +#include <oops/UserExn.h> + +#include <cassert> +#include <vector> + +namespace +{ + +// TODO move to loco +bool operator==(const loco::TensorShape &lhs, const loco::TensorShape &rhs) +{ + if (lhs.rank() != rhs.rank()) + return false; + for (uint32_t axis = 0; axis < lhs.rank(); ++axis) + { + if (!(lhs.dim(axis) == rhs.dim(axis))) + return false; + } + return true; +} + +bool valid_axis_range(int32_t output_rank, int32_t pack_axis) +{ + // check axis range in [-r-1, r+1) + assert(output_rank > 0); + return (-output_rank <= pack_axis) && (pack_axis < output_rank); +} + +bool constantfold_pack(moco::TFPack *node) +{ + // check if all the inputs are Const + std::vector<moco::TFConst *> input_nodes; + uint32_t num = node->N(); + + for (uint32_t index = 0; index < num; ++index) + { + auto in = dynamic_cast<moco::TFConst *>(node->values(index)); + if (in == nullptr) + return false; + + input_nodes.push_back(in); + } + assert(input_nodes.size() == num); + + // check if all inputs have same shape and dtype + auto input_0 = input_nodes.at(0); + auto shape_0 = moco::tensor_shape(input_0); + auto dtype_0 = input_0->dtype(); + if (dtype_0 != loco::DataType::S32 && dtype_0 != loco::DataType::FLOAT32) + { + // TODO support other types + assert(false); + return false; + } + for (uint32_t index = 1; index < num; ++index) + { + auto input_i = input_nodes.at(index); + auto shape_i = moco::tensor_shape(input_i); + auto dtype_i = input_i->dtype(); + if (!(shape_0 == shape_i)) + return false; + if (dtype_0 != dtype_i) + return false; + } + + int32_t output_rank = static_cast<int32_t>(shape_0.rank() + 1); + int32_t pack_axis = node->axis(); + if (!valid_axis_range(output_rank, pack_axis)) + { + throw oops::UserExn("axis is out of range: ", node->name()); + } + + if (pack_axis < 0) + { + pack_axis = output_rank + pack_axis; + } + + // define output shape + loco::TensorShape output_shape; + output_shape.rank(output_rank); + + for (int32_t r = 0, s = 0; r < output_rank; ++r) + { + if (r == pack_axis) + { + output_shape.dim(r).set(num); + } + else + { + output_shape.dim(r).set(shape_0.dim(s++).value()); + } + } + + auto graph = node->graph(); + + // create new constant + auto output_const = moco::new_const(graph, output_shape, input_0->dtype()); + + moco::TensorPackEnumerator etor; + + etor.shape(shape_0, output_shape); + etor.axis(pack_axis); + for (etor.start(); etor.valid(); etor.advance()) + { + uint32_t inp_num = etor.inp_num(); + uint32_t inp_element = etor.inp_element(); + uint32_t out_element = etor.out_element(); + + auto inp_const = input_nodes[inp_num]; + + if (input_0->dtype() == loco::DataType::S32) + { + int32_t val = inp_const->at<loco::DataType::S32>(inp_element); + output_const->at<loco::DataType::S32>(out_element) = val; + } + else if (input_0->dtype() == loco::DataType::FLOAT32) + { + float val = inp_const->at<loco::DataType::FLOAT32>(inp_element); + output_const->at<loco::DataType::FLOAT32>(out_element) = val; + } + } + + // replace + loco::replace(node).with(output_const); + + return true; +} + +} // namespace + +namespace moco +{ + +/** + * @note This will Replace TFPack with TFConst when inputs are TFConst + * + * Before + * A --- TFPack --- C + * B --/ + * After + * A --- TFPack + * B --/ + * TFConst ---------- C + * Where + * A, B : inputs of TFPack + * C : a node that uses TFPack as an input + * TFPack is disconnected from C + * Nodes are drawn multiple times to simplify the diagram + */ +bool ConstantFoldPack::run(loco::Graph *graph) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(graph))) + { + if (auto pack_node = as<moco::TFPack>(node)) + { + if (constantfold_pack(pack_node)) + changed = true; + } + } + + return changed; +} + +} // namespace moco diff --git a/compiler/moco/pass/src/Passes/ConstantFoldStridedSlice.cpp b/compiler/moco/pass/src/Passes/ConstantFoldStridedSlice.cpp new file mode 100644 index 000000000..8be47648d --- /dev/null +++ b/compiler/moco/pass/src/Passes/ConstantFoldStridedSlice.cpp @@ -0,0 +1,292 @@ +/* + * 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 "moco/Pass/Passes/ConstantFoldStridedSlice.h" + +#include "ConstantFoldHelper.h" +#include "TensorSliceEnumerator.h" + +#include <moco/IR/Nodes/TFStridedSlice.h> +#include <moco/IR/Nodes/TFConst.h> + +#include <moco/Support/NodeAs.h> +#include <moco/Support/TFShapeInferenceHelper.h> + +#include <oops/UserExn.h> + +#include <cassert> +#include <vector> + +namespace +{ + +loco::TensorShape calc_output_shape(moco::TFStridedSlice *node) +{ + auto const_input = dynamic_cast<moco::TFConst *>(node->input()); + auto const_begin = dynamic_cast<moco::TFConst *>(node->begin()); + auto const_end = dynamic_cast<moco::TFConst *>(node->end()); + auto const_strides = dynamic_cast<moco::TFConst *>(node->strides()); + auto input_rank = const_input->rank(); + auto output_rank = input_rank; + loco::TensorShape output_shape_range; + + output_shape_range.rank(input_rank); + for (uint32_t r = 0; r < input_rank; ++r) + { + // TODO apply begin/end mask + // TODO apply ellipsis mask + // TODO apply strides + auto end = const_end->at<loco::DataType::S32>(r); + auto begin = const_begin->at<loco::DataType::S32>(r); + auto size = end - begin; + output_shape_range.dim(r).set(size); + } + + loco::TensorShape output_tensor_shape; + if (node->shrink_axis_mask() != 0) + { + for (uint32_t rs = 0; rs < input_rank; ++rs) + { + int32_t bit = 1 << rs; + int32_t mask = node->shrink_axis_mask(); + if (bit & mask) + { + // shrink one dimension + assert(output_rank > 0); + output_rank = output_rank - 1; + } + } + output_tensor_shape.rank(output_rank); + for (uint32_t rs = 0, rd = 0; rs < input_rank; ++rs) + { + int32_t bit = 1 << rs; + int32_t mask = node->shrink_axis_mask(); + if ((bit & mask) == 0) + { + // use this dimension + output_tensor_shape.dim(rd).set(output_shape_range.dim(rs).value()); + rd++; + } + // else this dimension is shrink-ed + } + } + else + { + output_tensor_shape = output_shape_range; + } + + return output_tensor_shape; +} + +moco::u32v_t vector_from_const(moco::TFConst *tfconst) +{ + moco::u32v_t result; + + auto rank = tfconst->rank(); + assert(rank == 1); + auto dim = tfconst->dim(0).value(); + + result.resize(dim); + for (uint32_t r = 0; r < dim; ++r) + { + auto val = tfconst->at<loco::DataType::S32>(r); + result.at(r) = val; + } + + return result; +} + +moco::u32v_t operator-(const moco::u32v_t &lhs, const moco::u32v_t &rhs) +{ + assert(lhs.size() == rhs.size()); + + moco::u32v_t res; + res.resize(lhs.size()); + for (uint32_t r = 0; r < lhs.size(); r++) + { + res.at(r) = lhs.at(r) - rhs.at(r); + } + return res; +} + +template <typename T> T tfconst_at(const moco::TFConst *tfconst, const moco::u32v_t &pos); + +template <> int32_t tfconst_at<int32_t>(const moco::TFConst *tfconst, const moco::u32v_t &pos) +{ + uint32_t rank = tfconst->rank(); + assert(rank == pos.size()); + uint32_t element = 0; + for (uint32_t r = 0; r < rank; ++r) + { + uint32_t dim = tfconst->dim(r).value(); + element = element * dim + pos.at(r); + } + return tfconst->at<loco::DataType::S32>(element); +} + +template <> float tfconst_at<float>(const moco::TFConst *tfconst, const moco::u32v_t &pos) +{ + uint32_t rank = tfconst->rank(); + assert(rank == pos.size()); + uint32_t element = 0; + for (uint32_t r = 0; r < rank; ++r) + { + uint32_t dim = tfconst->dim(r).value(); + element = element * dim + pos.at(r); + } + return tfconst->at<loco::DataType::FLOAT32>(element); +} + +void tfconst_at(moco::TFConst *tfconst, const moco::u32v_t &pos, int32_t value) +{ + // tfconst->rank() can be smaller than pos.size() + // i.e., tfconst: shape[3] and pos[0,1] + // where shape[3] is output result shape + // [0,1] is position of input const + uint32_t rank = pos.size(); + uint32_t element = 0; + for (uint32_t r = 0; r < rank; ++r) + { + // this is like expand the shape from [3] to [1,3] to use same formula as in reading + uint32_t dim = tfconst->rank() < r ? tfconst->dim(r).value() : 1; + element = element * dim + pos.at(r); + } + + tfconst->at<loco::DataType::S32>(element) = value; +} + +void tfconst_at(moco::TFConst *tfconst, const moco::u32v_t &pos, float value) +{ + uint32_t rank = pos.size(); + uint32_t element = 0; + for (uint32_t r = 0; r < rank; ++r) + { + uint32_t dim = tfconst->rank() < r ? tfconst->dim(r).value() : 1; + element = element * dim + pos.at(r); + } + + tfconst->at<loco::DataType::FLOAT32>(element) = value; +} + +bool constantfold_stridedslice(moco::TFStridedSlice *node) +{ + auto const_input = dynamic_cast<moco::TFConst *>(node->input()); + if (const_input == nullptr) + { + // input is not TFConst, there's nothing to do + return false; + } + + // TODO support full mask features: see import codes also + assert(node->begin_mask() == 0); + assert(node->end_mask() == 0); + assert(node->ellipsis_mask() == 0); + assert(node->shrink_axis_mask() == 1); + + // TODO support other dtypes + assert(const_input->dtype() == loco::DataType::S32 || + const_input->dtype() == loco::DataType::FLOAT32); + + auto const_begin = dynamic_cast<moco::TFConst *>(node->begin()); + auto const_end = dynamic_cast<moco::TFConst *>(node->end()); + auto const_strides = dynamic_cast<moco::TFConst *>(node->strides()); + if (const_begin == nullptr || const_end == nullptr || const_strides == nullptr) + { + return false; + } + + // NOTE need shape but cannot depend on shape inference service module + auto tensor_shape = calc_output_shape(node); + auto input_shape = moco::tensor_shape(const_input); + + auto graph = node->graph(); + + // Create our target TFConst node with shape from begin~end/strides + auto const_sliced = moco::new_const(graph, tensor_shape, const_input->dtype()); + + // Copy sliced elements using TensorSliceEnumerator + moco::TensorSliceEnumerator etor; + auto v_begin = vector_from_const(const_begin); + auto v_end = vector_from_const(const_end); + moco::u32v_t v_cursor; + moco::u32v_t v_offset; + + etor.shape(input_shape); + etor.begin(v_begin); + etor.end(v_end); + + for (etor.start(); etor.valid(); etor.advance()) + { + v_cursor = etor.cursor(); + v_offset = v_cursor - v_begin; + + if (const_input->dtype() == loco::DataType::S32) + { + int32_t value = tfconst_at<int32_t>(const_input, v_cursor); + tfconst_at(const_sliced, v_offset, value); + } + else if (const_input->dtype() == loco::DataType::FLOAT32) + { + float value = tfconst_at<float>(const_input, v_cursor); + tfconst_at(const_sliced, v_offset, value); + } + } + + // replace + loco::replace(node).with(const_sliced); + + return true; +} + +} // namespace + +namespace moco +{ + +/** + * @note This will Replace TFStridedSlice with TFConst when 'input' is TFConst + * + * Before + * A --- TFStridedSlice --- C + * B --/ + * After + * A --- TFStridedSlice + * B --/ + * TFConst ---------- C + * Where + * A,B : inputs of TFStridedSlice + * C : a node that uses TFStridedSlice as an input + * TFStridedSlice is disconnected from C + * Nodes are drawn multiple times to simplify the diagram + * Limits + * Only limit set of inputs are supported for now + */ +bool ConstantFoldStridedSlice::run(loco::Graph *graph) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(graph))) + { + if (auto sslice_node = as<moco::TFStridedSlice>(node)) + { + if (constantfold_stridedslice(sslice_node)) + changed = true; + } + } + + return changed; +} + +} // namespace moco diff --git a/compiler/moco/pass/src/Passes/FuseBinaryIntoPreceding.cpp b/compiler/moco/pass/src/Passes/FuseBinaryIntoPreceding.cpp new file mode 100644 index 000000000..4a9631ea9 --- /dev/null +++ b/compiler/moco/pass/src/Passes/FuseBinaryIntoPreceding.cpp @@ -0,0 +1,539 @@ +/* + * 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/Pass/Passes/FuseBinaryIntoPreceding.h" + +#include <moco/Support/TFShapeInferenceHelper.h> + +#include <moco/IR/TFDialect.h> +#include <moco/IR/Nodes/TFAdd.h> +#include <moco/IR/Nodes/TFBiasAdd.h> +#include <moco/IR/Nodes/TFConst.h> +#include <moco/IR/Nodes/TFConv2D.h> +#include <moco/IR/Nodes/TFDepthwiseConv2dNative.h> +#include <moco/IR/Nodes/TFMul.h> + +#include <cassert> +#include <memory> + +namespace +{ + +/** + * @brief Fusable operation type + */ +enum class FuseType +{ + Conv2D, + DepthwiseConv2D, + // TODO Support FullyConnected +}; + +// TODO rename this method when there is a better name +bool is_only_one_valid(moco::TFConst *xc, moco::TFConst *yc) +{ + if (xc == nullptr && yc == nullptr) + return false; + if (xc != nullptr && yc != nullptr) + return false; + + return true; +} + +// TODO Put this in some common place +void copy_shape(const moco::TFConst *src, moco::TFConst *dst) +{ + assert(src != nullptr); + assert(dst != nullptr); + + uint32_t rank = src->rank(); + dst->rank(rank); + for (uint32_t index = 0; index < rank; ++index) + { + if (src->dim(index).known()) + dst->dim(index) = src->dim(index); + else + dst->dim(index).unset(); + } +} + +/** + * @brief return true if shape is identical + */ +bool shape_match(const moco::TFConst *c1, const moco::TFConst *c2) +{ + assert(c1 != nullptr); + assert(c2 != nullptr); + + uint32_t rank = c1->rank(); + if (rank != c2->rank()) + return false; + + for (uint32_t index = 0; index < rank; ++index) + { + if (!c1->dim(index).known() || !c2->dim(index).known()) + return false; + + if (c1->dim(index).value() != c2->dim(index).value()) + return false; + } + return true; +} + +template <FuseType FT> +moco::TFConst *create_kernel_from_fuse_mulparam(loco::Graph *graph, moco::TFConst *ker, + moco::TFConst *mulparam); + +template <> +moco::TFConst *create_kernel_from_fuse_mulparam<FuseType::Conv2D>(loco::Graph *graph, + moco::TFConst *ker, + moco::TFConst *mulparam) +{ + auto ker_shape_inf = moco::node_shape(ker); + assert(ker_shape_inf.domain() != loco::Domain::Unknown); + auto ker_shape = ker_shape_inf.as<loco::TensorShape>(); + + auto mulparam_shape_inf = moco::node_shape(mulparam); + assert(mulparam_shape_inf.domain() != loco::Domain::Unknown); + auto mulparam_shape = mulparam_shape_inf.as<loco::TensorShape>(); + + // create new ker_fused with same size of ker + auto ker_fused = graph->nodes()->create<moco::TFConst>(); + + assert(ker_shape.rank() == 4); + assert(mulparam_shape.rank() == 1); + assert(ker_shape.dim(3).value() == mulparam_shape.dim(0).value()); + + ker_fused->dtype(loco::DataType::FLOAT32); + copy_shape(ker, ker_fused); + auto ker_num_elements = ker->size<loco::DataType::FLOAT32>(); + ker_fused->size<loco::DataType::FLOAT32>(ker_num_elements); + + // TensorFlow Conv2D Kernel has HWIO format + // Broadcast Mul vector to Kernel tensor by the Output + const uint32_t ker_height = ker_shape.dim(0).value(); + const uint32_t ker_width = ker_shape.dim(1).value(); + const uint32_t ker_input = ker_shape.dim(2).value(); + const uint32_t ker_output = ker_shape.dim(3).value(); + + for (uint32_t ker_y = 0; ker_y < ker_height; ++ker_y) + { + for (uint32_t ker_x = 0; ker_x < ker_width; ++ker_x) + { + for (uint32_t in_ch = 0; in_ch < ker_input; ++in_ch) + { + uint32_t num_items = ((ker_y * ker_width + ker_x) * ker_input + in_ch) * ker_output; + for (uint32_t out_ch = 0; out_ch < ker_output; ++out_ch) + { + auto mulparam_v = mulparam->at<loco::DataType::FLOAT32>(out_ch); + auto ker_v = ker->at<loco::DataType::FLOAT32>(num_items + out_ch); + ker_fused->at<loco::DataType::FLOAT32>(num_items + out_ch) = ker_v * mulparam_v; + } + } + } + } + + return ker_fused; +} + +/** + * @brief Create a kernel from fuse mulparam<FuseType::DepthwiseConv2D> object + * @return Kernel of fused mulparam + */ +template <> +moco::TFConst *create_kernel_from_fuse_mulparam<FuseType::DepthwiseConv2D>(loco::Graph *graph, + moco::TFConst *ker, + moco::TFConst *mulparam) +{ + auto ker_shape_inf = moco::node_shape(ker); + assert(ker_shape_inf.domain() != loco::Domain::Unknown); + auto ker_shape = ker_shape_inf.as<loco::TensorShape>(); + + auto mulparam_shape_inf = moco::node_shape(mulparam); + assert(mulparam_shape_inf.domain() != loco::Domain::Unknown); + auto mulparam_shape = mulparam_shape_inf.as<loco::TensorShape>(); + + // create new ker_fused with same size of ker + auto ker_fused = graph->nodes()->create<moco::TFConst>(); + + assert(ker_shape.rank() == 4); + assert(mulparam_shape.rank() == 1); + assert(ker_shape.dim(2).value() * ker_shape.dim(3).value() == mulparam_shape.dim(0).value()); + + ker_fused->dtype(loco::DataType::FLOAT32); + copy_shape(ker, ker_fused); + auto ker_num_elements = ker->size<loco::DataType::FLOAT32>(); + ker_fused->size<loco::DataType::FLOAT32>(ker_num_elements); + + // TensorFlow DepthwiseConv2DNative Kernel has HWIM format + // Broadcast Mul vector to Kernel tensor by the Output + const uint32_t ker_height = ker_shape.dim(0).value(); + const uint32_t ker_width = ker_shape.dim(1).value(); + const uint32_t ker_input = ker_shape.dim(2).value(); + const uint32_t ker_multiplier = ker_shape.dim(3).value(); + + for (uint32_t ker_y = 0; ker_y < ker_height; ++ker_y) + { + for (uint32_t ker_x = 0; ker_x < ker_width; ++ker_x) + { + for (uint32_t in_ch = 0; in_ch < ker_input; ++in_ch) + { + uint32_t num_items = ((ker_y * ker_width + ker_x) * ker_input + in_ch) * ker_multiplier; + for (uint32_t ker_ch = 0; ker_ch < ker_multiplier; ++ker_ch) + { + auto mulparam_v = mulparam->at<loco::DataType::FLOAT32>(in_ch + ker_ch * ker_input); + auto ker_v = ker->at<loco::DataType::FLOAT32>(num_items + ker_ch); + ker_fused->at<loco::DataType::FLOAT32>(num_items + ker_ch) = ker_v * mulparam_v; + } + } + } + } + + return ker_fused; +} + +/** + * @brief Create a fused convolution opertion from kernel of fused mulparam + * @return Fused convolution operation + */ +template <FuseType FT, class T> +T *fused_conv_node(loco::Graph *graph, moco::TFConst *mulparam, T *conv_node) +{ + // LOGGER(l); + + // ker should be constant + auto ker = dynamic_cast<moco::TFConst *>(conv_node->filter()); + if (ker == nullptr) + { + // Wait until ker is becomes TFConst: there are cases when it's Identity. + // INFO(l) << "Mul fuse_to_preceding: precedingOp ker is not TFConst"; + return nullptr; + } + auto ifm = conv_node->input(); + assert(ifm != nullptr); + + // we need shape information, if not wait till it's ready + auto ker_shape_inf = moco::node_shape(ker); + if (ker_shape_inf.domain() == loco::Domain::Unknown) + { + // INFO(l) << "Mul fuse_to_preceding: precedingOp ker has no shape"; + return nullptr; + } + auto mulparam_shape_inf = moco::node_shape(mulparam); + if (mulparam_shape_inf.domain() == loco::Domain::Unknown) + { + // INFO(l) << "Mul fuse_to_preceding: precedingOp mulparam has no shape"; + return nullptr; + } + // if MulParam rank is not 1 we cannot fuse, just skip + auto mulparam_shape = mulparam_shape_inf.as<loco::TensorShape>(); + if (mulparam_shape.rank() != 1) + { + // INFO(l) << "Mul fuse_to_preceding: Mul rank is not 1"; + return nullptr; + } + + auto ker_fused = create_kernel_from_fuse_mulparam<FT>(graph, ker, mulparam); + auto conv_fused = graph->nodes()->create<T>(); + + conv_fused->input(ifm); + conv_fused->filter(ker_fused); + conv_fused->padding(conv_node->padding()); + conv_fused->data_layout(conv_node->data_layout()); + conv_fused->strides(conv_node->strides()); + + return conv_fused; +} + +/** + * @note This creates fused ker:2 from ker:1, 'mulparam' and + * new precedingOp:2 that uses ker:2 as the kernel. + * Then make C to use precedingOp:2 as new input. + * + * <Before> + * mulparam-\ + * ker:1 --\ \ + * ifm ----- precedingOp:1 ----------- Mul --- C + * + * + * <After> + * mulparam-\ + * ker:1 --\ \ + * - precedingOp:1 ----------- Mul --- + * / + * ifm ----- precedingOp:2 ------------------- C + * ker:2 ---/ + * + * + * [Where] + * - precedingOp:1 can be one of TFConv2D, TFDepthwiseConv2dNative, FullyConnected + * - 'mulparam' and Mul will be disconnected from the Output. + * - ker:2 is added with fused values of ker:1 and mulparam + * - precedingOp:2 is added using ifm and ker:2 and other parameters + * same as precedingOp:1. + * - ker:1, precedingOp:1, 'mulparam' and Mul should be removed in + * RemoveDeadNodeTransform if not used. + */ +bool fuse_to_preceding(loco::Graph *graph, moco::TFMul *node) +{ + auto xc = dynamic_cast<moco::TFConst *>(node->x()); + auto yc = dynamic_cast<moco::TFConst *>(node->y()); + + // Note: if both are constants, it should be done by constant-folding + if (!(is_only_one_valid(xc, yc))) + return false; + + moco::TFConst *mulparam = nullptr; + moco::TFNode *precedingOp = nullptr; + + if (xc != nullptr) + { + mulparam = xc; + precedingOp = dynamic_cast<moco::TFNode *>(node->y()); + } + else // yc != nullptr + { + mulparam = yc; + precedingOp = dynamic_cast<moco::TFNode *>(node->x()); + } + + assert(mulparam->dtype() == loco::DataType::FLOAT32); + + // TODO support FullyConnected + moco::TFNode *fused_node = nullptr; + if (auto conv2d = dynamic_cast<moco::TFConv2D *>(precedingOp)) + fused_node = fused_conv_node<FuseType::Conv2D, moco::TFConv2D>(graph, mulparam, conv2d); + else if (auto dw_conv2d = dynamic_cast<moco::TFDepthwiseConv2dNative *>(precedingOp)) + fused_node = fused_conv_node<FuseType::DepthwiseConv2D, moco::TFDepthwiseConv2dNative>( + graph, mulparam, dw_conv2d); + + // Not ready yet + if (fused_node == nullptr) + return false; + + // Replace TFMul node with new precedingOp with fused kernel + // This will leave existing precedingOp as-is but can be removed if not used + // from other transformations + replace(node).with(fused_node); + // TODO check if need to disconnect + // node->x(nullptr); + // node->y(nullptr); + // fused_node->ifm(nullptr); + // fused_node->ker(nullptr); + + return true; +} + +/** + * @brief Create zero-filled BiasAdd opertion and insert after precedingOp + * The plan is to fuse 'addparam' to TFBiasAdd bias + * @return Zero-filled BiasAdd operation + */ +template <class T> +moco::TFBiasAdd *create_biasadd_node(loco::Graph *graph, moco::TFConst *addparam, T *precedingOp) +{ + auto dtype = addparam->dtype(); + assert(dtype == loco::DataType::FLOAT32); + + // Create TFConst(bias of TFBiasAdd) with same shape and dtype of 'addparam' but + // with values 0.0 + auto biasadd_param = graph->nodes()->create<moco::TFConst>(); + biasadd_param->dtype(dtype); + copy_shape(addparam, biasadd_param); + auto biasadd_num_elements = addparam->size<loco::DataType::FLOAT32>(); + biasadd_param->size<loco::DataType::FLOAT32>(biasadd_num_elements); + for (int32_t i = 0; i < biasadd_num_elements; i++) + { + biasadd_param->at<loco::DataType::FLOAT32>(i) = 0.0f; + } + + // Create TFBiasAdd with same shape as TFAdd + auto data_layout = precedingOp->data_layout(); + auto tf_biasadd = graph->nodes()->create<moco::TFBiasAdd>(); + tf_biasadd->data_layout(data_layout); + + loco::replace(precedingOp).with(tf_biasadd); + tf_biasadd->value(precedingOp); + tf_biasadd->bias(biasadd_param); + + return tf_biasadd; +} + +/** + * @note TFAdd will be fused to TFBiasAdd + * + * <Before> + * If precedingOp is not TFBiasAdd, then insert TFConst:1 + TFBiasAdd that + * TFConst:1 has zero values. + * + * addparam --\ + * \ + * precedingOp ---------------------------- TFAdd ----- C + * + * + * <Intermediate> + * If it's TFBiasAdd and one of the input is TFConst type, + * then we can fuse 'addparam' to the input TFConst:2 value of TFBiasAdd, where + * TFConst:2 has added values from 'addparam' + * + * addparam --\ + * TFConst:1 --------\ \ + * precedingOp ------- TFBiasAdd ---------- TFAdd ----- C + * + * + * <After> + * addparam --\ + * TFConst:2 --------\ \ + * precedingOp ------- TFBiasAdd ---------- TFAdd ----- + * \--------------------- C + * + * + * [Where] + * - precedingOp can be TFConv2D, TFDepthwiseConv2dNative, FullyConnected, + * TFBiasAdd. + * - Intermediate is to insert TFBiasAdd + TFConst:1 + * - After is to fuse 'addparam' of TFAdd into TFConst:1 + TFBiasAdd + * that becomes TFConst:2 + TFBiasAdd + */ +bool fuse_to_preceding(loco::Graph *graph, moco::TFAdd *node) +{ + // LOGGER(l); + + auto xc = dynamic_cast<moco::TFConst *>(node->x()); + auto yc = dynamic_cast<moco::TFConst *>(node->y()); + + // Note: if both are constants, it should be done by constant-folding + if (!(is_only_one_valid(xc, yc))) + return false; + + moco::TFConst *addparam = nullptr; + moco::TFNode *precedingOp = nullptr; + + if (xc != nullptr) + { + addparam = xc; + precedingOp = dynamic_cast<moco::TFNode *>(node->y()); + } + else // yc != nullptr + { + addparam = yc; + precedingOp = dynamic_cast<moco::TFNode *>(node->x()); + } + + auto addparam_shape_inf = moco::node_shape(addparam); + if (addparam_shape_inf.domain() == loco::Domain::Unknown) + { + // INFO(l) << "Add fuse_to_preceding: addparam has no shape"; + return false; + } + // if AddParam rank is not 0 or 1 we cannot fuse, just skip + auto addparam_shape = addparam_shape_inf.as<loco::TensorShape>(); + if (addparam_shape.rank() > 1) + { + // INFO(l) << "Add fuse_to_preceding: Add rank is not 0 or 1"; + return false; + } + + // TODO do something when rank() is 0 + if (addparam_shape.rank() == 0) + { + // Not supported yet + return false; + } + assert(addparam_shape.rank() != 0); + + // TODO support FullyConnected + moco::TFBiasAdd *biasadd = nullptr; + if (auto conv2d = dynamic_cast<moco::TFConv2D *>(precedingOp)) + biasadd = create_biasadd_node<moco::TFConv2D>(graph, addparam, conv2d); + else if (auto dw_conv2d = dynamic_cast<moco::TFDepthwiseConv2dNative *>(precedingOp)) + biasadd = create_biasadd_node<moco::TFDepthwiseConv2dNative>(graph, addparam, dw_conv2d); + else if (auto old_bias_add = dynamic_cast<moco::TFBiasAdd *>(precedingOp)) + biasadd = old_bias_add; + + if (biasadd == nullptr) + { + // try next turn + return false; + } + + // Let's fuse addparam into biasadd bias + auto biasadd_bias = dynamic_cast<moco::TFConst *>(biasadd->bias()); + assert(biasadd_bias != nullptr); + if (!shape_match(biasadd_bias, addparam)) + { + // INFO(l) << "TFBiasAdd bias and TFAdd input shape mismatch"; + return false; + } + auto add_num_elements = addparam->size<loco::DataType::FLOAT32>(); + assert(add_num_elements == biasadd_bias->size<loco::DataType::FLOAT32>()); + for (int32_t i = 0; i < add_num_elements; i++) + { + biasadd_bias->at<loco::DataType::FLOAT32>(i) += addparam->at<loco::DataType::FLOAT32>(i); + } + + replace(node).with(biasadd); + // TODO check if need to disconnect + // node->x(nullptr); + // node->y(nullptr); + + return true; +} + +} // namespace + +namespace moco +{ + +bool FuseBinaryIntoPreceding::run(loco::Graph *graph) +{ + bool changed = false; + auto active_nodes = loco::active_nodes(loco::output_nodes(graph)); + + for (auto node : active_nodes) + { + if (node->dialect() == moco::TFDialect::get()) + { + { + auto tf_node = dynamic_cast<moco::TFMul *>(node); + if (tf_node != nullptr) + { + if (fuse_to_preceding(graph, tf_node)) + changed = true; + } + } + { + // TODO support Div + } + + { + auto tf_node = dynamic_cast<moco::TFAdd *>(node); + if (tf_node != nullptr) + { + if (fuse_to_preceding(graph, tf_node)) + changed = true; + } + } + { + // TODO support Sub + } + } + } + + return changed; +} + +} // namespace moco diff --git a/compiler/moco/pass/src/Passes/RemoveTFIdentityNode.cpp b/compiler/moco/pass/src/Passes/RemoveTFIdentityNode.cpp new file mode 100644 index 000000000..d3d22c90e --- /dev/null +++ b/compiler/moco/pass/src/Passes/RemoveTFIdentityNode.cpp @@ -0,0 +1,66 @@ +/* + * 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/Pass/Passes/RemoveTFIdentityNode.h" + +#include <moco/IR/TFDialect.h> +#include <moco/IR/TFNode.h> + +#include <set> + +namespace moco +{ + +bool RemoveTFIdentityNode::run(loco::Graph *g) +{ + struct Collector final : public moco::TFNodeMutableVisitor<void> + { + void visit(moco::TFIdentity *node) final + { + if (node->input() != nullptr) + { + candidates.insert(node); + } + } + + void visit(moco::TFNode *) final { return; } + + std::set<moco::TFIdentity *> candidates; + }; + + Collector collector; + + for (auto node : loco::all_nodes(g)) + { + if (node->dialect() == moco::TFDialect::get()) + { + auto tf_node = dynamic_cast<moco::TFNode *>(node); + // NOTE our analysis tool reports an error for tf_node may be nullptr + if (tf_node != nullptr) + tf_node->accept(&collector); + } + } + + for (auto node : collector.candidates) + { + replace(node).with(node->input()); + node->input(nullptr); + } + + return collector.candidates.size() > 0; +} + +} // namespace moco diff --git a/compiler/moco/pass/src/Passes/ResolveConstantShape.cpp b/compiler/moco/pass/src/Passes/ResolveConstantShape.cpp new file mode 100644 index 000000000..2a1323fbc --- /dev/null +++ b/compiler/moco/pass/src/Passes/ResolveConstantShape.cpp @@ -0,0 +1,129 @@ +/* + * 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/Pass/Passes/ResolveConstantShape.h" + +#include <moco/Support/TFShapeInferenceHelper.h> +#include <moco/Support/NodeAs.h> + +#include <moco/IR/Nodes/TFShape.h> +#include <moco/IR/Nodes/TFConst.h> + +#include <loco.h> + +#include <oops/UserExn.h> + +#include <cassert> + +namespace +{ + +/** + * WHEN: + * - TFShape's input shape is determined + * DO: + * - Replace TFShape into TFConst + * + * + * <Before> + * in ---- TFShape ---- out(s) + * + * <After> + * in ---- TFShape + * + * TFConst ---- out(s) + */ +bool resolve_constant_shape(loco::Graph *graph, moco::TFShape *shape_node) +{ + auto input_shape = moco::node_shape(shape_node->input()); + + // Check condition + if (input_shape.domain() == loco::Domain::Unknown) + { + // Cannot resolve without known input_shape + return false; + } + + auto input_tensor_shape = input_shape.as<loco::TensorShape>(); + + auto shape_rank = input_tensor_shape.rank(); + for (uint32_t axis = 0; axis < shape_rank; ++axis) + { + if (!input_tensor_shape.dim(axis).known()) + { + // Cannot resolve with unknown dimension + return false; + } + } + + // Make TFConst to replace TFShape + auto const_node = graph->nodes()->create<moco::TFConst>(); + + // set dtype + auto dtype = shape_node->dtype(); + const_node->dtype(dtype); + + // set shape + const_node->rank(1); + const_node->dim(0) = shape_rank; + + // set data + if (dtype == loco::DataType::S32) + { + // TODO Better to make template for this when support new dtype + const_node->size<loco::DataType::S32>(shape_rank); + for (uint32_t axis = 0; axis < shape_rank; ++axis) + { + int32_t dim = (int32_t)input_tensor_shape.dim(axis).value(); + if (!(dim > 0)) + { + throw oops::UserExn("Invalid input shape", shape_node->name()); + } + const_node->at<loco::DataType::S32>(axis) = dim; + } + } + else + { + throw oops::UserExn("Unsupported data type", shape_node->name()); + } + + // replace + loco::replace(shape_node).with(const_node); + + return true; +} + +} // namespace + +namespace moco +{ + +bool ResolveConstantShape::run(loco::Graph *graph) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(graph))) + { + if (auto shape_node = as<moco::TFShape>(node)) + { + if (resolve_constant_shape(graph, shape_node)) + changed = true; + } + } + + return changed; +} + +} // namespace moco diff --git a/compiler/moco/pass/src/Passes/ResolveFusedBatchNorm.cpp b/compiler/moco/pass/src/Passes/ResolveFusedBatchNorm.cpp new file mode 100644 index 000000000..6fd1474af --- /dev/null +++ b/compiler/moco/pass/src/Passes/ResolveFusedBatchNorm.cpp @@ -0,0 +1,254 @@ +/* + * 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/Pass/Passes/ResolveFusedBatchNorm.h" + +#include <moco/Support/NodeAs.h> + +#include <moco/IR/Nodes/TFAdd.h> +#include <moco/IR/Nodes/TFConst.h> +#include <moco/IR/Nodes/TFMul.h> +#include <moco/IR/Nodes/TFFusedBatchNorm.h> + +#include <cassert> +#include <cmath> +#include <memory> + +namespace +{ + +bool is_same_shape(moco::TFConst *lc, moco::TFConst *rc) +{ + if (lc->rank() != rc->rank()) + return false; + + for (auto r = 0; r < lc->rank(); ++r) + { + if (lc->dim(r).value() != rc->dim(r).value()) + return false; + } + return true; +} + +void copy_shape(const moco::TFConst *src, moco::TFConst *dst) +{ + assert(src != nullptr); + assert(dst != nullptr); + + uint32_t rank = src->rank(); + dst->rank(rank); + for (uint32_t index = 0; index < rank; ++index) + { + if (src->dim(index).known()) + dst->dim(index) = src->dim(index).value(); + else + dst->dim(index).unset(); + } +} + +/** + * @note resolve_to_muladd() will transform TFFusedBatchNorm to TFMul, TFAdd and two ConstGen + * + * <arguments> + * %0:input + * %1:gamma : const + * %2:beta : const + * %3:mean : const + * %4:variance : const + * %5:epsilon : const + * + * <constant operations> + * fbn_epsilon_array = make_array(%5:epsilon) + * fbn_epsilon = %4:variance + fbn_epsilon_array + * fbn_rsqrt = 1.0 / math::sqrt(fbn_epsilon) + * + * fbn_mean = %3:mean + * fbn_mul = fbn_rsqrt * %1:gamma + * fbn_offset = %2:beta + * + * fbn_mul_0_param = fbn_mul + * fbn_add_param = fbn_offset - fbn_mean * fbn_mul + * + * <new replace nodes> + * %11:fbn_mul_0_param = ConstGen(fbn_mul_0_param) + * %12:fbn_mul_0 = TFMul(%0:input, %11:fbn_mul_0_param) + * %21:fbn_add_param = ConstGen(fbn_add_param) + * %22:fbn = TFAdd(%12:fbn_mul_0,%21:fbn_add_param) + */ +bool resolve_to_muladd(loco::Graph *graph, moco::TFFusedBatchNorm *node) +{ + // LOGGER(lfbn); + + auto tffbn_x = node->x(); + if (tffbn_x == nullptr) + { + // This node is already converted + return false; + } + + auto tffbn_scale = dynamic_cast<moco::TFConst *>(node->scale()); + auto tffbn_offset = dynamic_cast<moco::TFConst *>(node->offset()); + auto tffbn_mean = dynamic_cast<moco::TFConst *>(node->mean()); + auto tffbn_variance = dynamic_cast<moco::TFConst *>(node->variance()); + + // all should be const + if (tffbn_scale == nullptr || tffbn_offset == nullptr || tffbn_mean == nullptr || + tffbn_variance == nullptr) + { + // INFO(lfbn) << "TFFBN resolve_to_muladd: One of constant input node is not a constant" + // << std::endl; + return false; + } + assert(tffbn_scale->dtype() == loco::DataType::FLOAT32); + assert(tffbn_offset->dtype() == loco::DataType::FLOAT32); + assert(tffbn_mean->dtype() == loco::DataType::FLOAT32); + assert(tffbn_variance->dtype() == loco::DataType::FLOAT32); + + // check all const shape are the same + if (!is_same_shape(tffbn_scale, tffbn_offset) || !is_same_shape(tffbn_scale, tffbn_mean) || + !is_same_shape(tffbn_scale, tffbn_variance)) + { + // INFO(lfbn) << "TFFBN resolve_to_muladd: Shape of constant are not same" << std::endl; + return false; + } + + auto tffbn_epsilon = node->epsilon(); + // INFO(lfbn) << "TFFBN tffbn_epsilon = " << tffbn_epsilon << std::endl; + auto const_num_elements = tffbn_scale->size<loco::DataType::FLOAT32>(); + // INFO(lfbn) << "TFFBN const_num_elements = " << const_num_elements << std::endl; + + // fbn_epsilon = %4:variance + fbn_epsilon_array + std::unique_ptr<float[]> fbn_epsilon{new float[const_num_elements]}; + for (int32_t i = 0; i < const_num_elements; i++) + { + auto variance = tffbn_variance->at<loco::DataType::FLOAT32>(i); + fbn_epsilon.get()[i] = variance + tffbn_epsilon; + } + + // fbn_rsqrt = 1.0 / math::sqrt(fbn_epsilon) + std::unique_ptr<float[]> fbn_rsqrt{new float[const_num_elements]}; + for (int32_t i = 0; i < const_num_elements; i++) + { + fbn_rsqrt.get()[i] = 1.0 / sqrt(fbn_epsilon.get()[i]); + } + + // fbn_mean = %3:mean : TODO remove this block and use %3:mean + std::unique_ptr<float[]> fbn_mean{new float[const_num_elements]}; + for (int32_t i = 0; i < const_num_elements; i++) + { + fbn_mean.get()[i] = tffbn_mean->at<loco::DataType::FLOAT32>(i); + } + + // fbn_mul = fbn_rsqrt * %1:gamma + std::unique_ptr<float[]> fbn_mul{new float[const_num_elements]}; + for (int32_t i = 0; i < const_num_elements; i++) + { + fbn_mul.get()[i] = fbn_rsqrt.get()[i] * tffbn_scale->at<loco::DataType::FLOAT32>(i); + } + + // fbn_offset = %2:beta : TODO remove this block and use %2:beta + std::unique_ptr<float[]> fbn_offset{new float[const_num_elements]}; + for (int32_t i = 0; i < const_num_elements; i++) + { + fbn_offset.get()[i] = tffbn_offset->at<loco::DataType::FLOAT32>(i); + } + + // fbn_mul_0_param = fbn_mul : remove this and use fbn_mul + std::unique_ptr<float[]> fbn_mul_0_param{new float[const_num_elements]}; + for (int32_t i = 0; i < const_num_elements; i++) + { + fbn_mul_0_param.get()[i] = fbn_mul.get()[i]; + } + + // fbn_add_param = fbn_offset - fbn_mean * fbn_mul + std::unique_ptr<float[]> fbn_add_param{new float[const_num_elements]}; + for (int32_t i = 0; i < const_num_elements; i++) + { + fbn_add_param.get()[i] = fbn_offset.get()[i] - fbn_mean.get()[i] * fbn_mul.get()[i]; + } + + // INFO(lfbn) << "TFFBN create ConstGen" << std::endl; + + /* + * %11:fbn_mul_0_param = ConstGen(fbn_mul_0_param) + * %21:fbn_add_param = ConstGen(fbn_add_param) + */ + auto const_fbn_mul_0_param = graph->nodes()->create<moco::TFConst>(); + const_fbn_mul_0_param->dtype(loco::DataType::FLOAT32); + copy_shape(tffbn_scale, const_fbn_mul_0_param); + const_fbn_mul_0_param->size<loco::DataType::FLOAT32>(const_num_elements); + for (int32_t i = 0; i < const_num_elements; i++) + { + const_fbn_mul_0_param->at<loco::DataType::FLOAT32>(i) = fbn_mul_0_param.get()[i]; + } + auto const_fbn_add_param = graph->nodes()->create<moco::TFConst>(); + const_fbn_add_param->dtype(loco::DataType::FLOAT32); + copy_shape(tffbn_scale, const_fbn_add_param); + const_fbn_add_param->size<loco::DataType::FLOAT32>(const_num_elements); + for (int32_t i = 0; i < const_num_elements; i++) + { + const_fbn_add_param->at<loco::DataType::FLOAT32>(i) = fbn_add_param.get()[i]; + } + + // INFO(lfbn) << "TFFBN create TFMul, TFAdd" << std::endl; + /* + * %12:fbn_mul_0 = TFMul(%0:input, %11:fbn_mul_0_param) + * %22:fbn = TFAdd(%12:fbn_mul_0,%21:fbn_add_param) + */ + auto fbn_mul_0 = graph->nodes()->create<moco::TFMul>(); + fbn_mul_0->x(tffbn_x); + fbn_mul_0->y(const_fbn_mul_0_param); + + auto fbn = graph->nodes()->create<moco::TFAdd>(); + fbn->x(fbn_mul_0); + fbn->y(const_fbn_add_param); + + // replace old node with new fbn + replace(node).with(fbn); + // unlink from graph + node->x(nullptr); + node->scale(nullptr); + node->offset(nullptr); + node->mean(nullptr); + node->variance(nullptr); + + return true; +} + +} // namespace + +namespace moco +{ + +bool ResolveFusedBatchNorm::run(loco::Graph *graph) +{ + for (auto node : loco::active_nodes(loco::output_nodes(graph))) + { + if (as<moco::TFFusedBatchNorm>(node)) + { + if (resolve_to_muladd(graph, as<moco::TFFusedBatchNorm>(node))) + { + // tree has been changed. let's return so that we don't need to + // considier about following node is correct or not. + return true; + } + } + } + + return false; +} + +} // namespace moco diff --git a/compiler/moco/pass/src/Passes/ResolveReshapeWildcardDim.cpp b/compiler/moco/pass/src/Passes/ResolveReshapeWildcardDim.cpp new file mode 100644 index 000000000..3446716cb --- /dev/null +++ b/compiler/moco/pass/src/Passes/ResolveReshapeWildcardDim.cpp @@ -0,0 +1,153 @@ +/* + * 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/Pass/Passes/ResolveReshapeWildcardDim.h" + +#include <moco/Support/TFShapeInferenceHelper.h> +#include <moco/Support/NodeAs.h> + +#include <moco/IR/Nodes/TFReshape.h> +#include <moco/IR/Nodes/TFConst.h> + +#include <cassert> +#include <limits> + +namespace +{ + +/** + * @return true when 'node' has one and only one wildcard dimension + * @return false when 'node' has no wildcard dimension, i.e. fixed reshape case + * + * @note Assertions in this function are sanity check for 'node', Reshape's + * Const shape input + */ +bool has_one_wildcard_dim(const moco::TFConst *node) +{ + assert(node->dtype() == loco::DataType::S32); + assert(node->rank() == 1); + + auto len = node->dim(0).value(); + assert(len > 0); + + // Must have one and only wildcard dimension(-1) + uint32_t count_wildcard_dim = 0; + for (uint32_t i = 0; i < len; ++i) + { + auto dim = node->at<loco::DataType::S32>(i); + if (dim == -1) + count_wildcard_dim++; + else + assert(dim >= 1); + } + + assert(count_wildcard_dim <= 1 && + "Invalid Reshape: there should be none or only one wildcard dimension"); + return count_wildcard_dim; +} + +uint32_t volume(const loco::TensorShape &shape) +{ + uint32_t ret = 1; + auto rank = shape.rank(); + for (uint32_t axis = 0; axis < rank; ++axis) + { + ret *= shape.dim(axis).value(); + } + return ret; +} + +void deduce_and_fix_wildcard_dim(moco::TFConst *node, const loco::NodeShape &tensor_input_shape) +{ + assert(has_one_wildcard_dim(node)); + + assert(tensor_input_shape.domain() == loco::Domain::Tensor); + auto shape = tensor_input_shape.as<loco::TensorShape>(); + + auto len = node->dim(0).value(); + uint32_t wildcard_index = std::numeric_limits<uint32_t>::max(); + uint32_t product_of_non_wildcard_dims = 1; + + // Deduce + for (uint32_t i = 0; i < len; ++i) + { + auto dim = node->at<loco::DataType::S32>(i); + if (dim == -1) + { + wildcard_index = i; + } + else + { + product_of_non_wildcard_dims *= dim; + } + } + assert(wildcard_index != std::numeric_limits<uint32_t>::max()); + + // Fix + assert(volume(shape) % product_of_non_wildcard_dims == 0); + node->at<loco::DataType::S32>(wildcard_index) = volume(shape) / product_of_non_wildcard_dims; +} + +/** + * WHEN: + * - TFReshape's shape input is TFConst + * - The TFConst is valid shape input for dynamic reshape, i.e. it has one and + * only wildcard dimension(-1) + * - TFReshape's tensor input has complete shape inference data + * DO: + * - Deduce what the wildcard dimension is and fix it + */ +bool resolve_wildcard_dim(moco::TFReshape *reshape) +{ + // Check conditions (WHEN) + auto const_shape_input = dynamic_cast<moco::TFConst *>(reshape->shape()); + if (!const_shape_input) + return false; + + if (!has_one_wildcard_dim(const_shape_input)) + return false; + + auto tensor_input_shape = moco::node_shape(reshape->tensor()); + if (tensor_input_shape.domain() == loco::Domain::Unknown) + return false; + + // Deduce (DO) + deduce_and_fix_wildcard_dim(const_shape_input, tensor_input_shape); + + return true; +} + +} // namespace + +namespace moco +{ + +bool ResolveReshapeWildcardDim::run(loco::Graph *graph) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(graph))) + { + if (auto reshape = as<moco::TFReshape>(node)) + { + if (resolve_wildcard_dim(reshape)) + changed = true; + } + } + + return changed; +} + +} // namespace moco diff --git a/compiler/moco/pass/src/Passes/ResolveSquaredDifference.cpp b/compiler/moco/pass/src/Passes/ResolveSquaredDifference.cpp new file mode 100644 index 000000000..b66add1ae --- /dev/null +++ b/compiler/moco/pass/src/Passes/ResolveSquaredDifference.cpp @@ -0,0 +1,97 @@ +/* + * 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/Pass/Passes/ResolveSquaredDifference.h" + +#include <moco/IR/TFDialect.h> +#include <moco/IR/TFNodes.h> +#include <moco/IR/TFNodeVisitor.h> +#include <moco/IR/TFNodeImpl.h> + +#include <loco/IR/NodeShape.h> +#include <loco/Service/ShapeInference.h> + +#include <stdex/Memory.h> + +namespace +{ + +bool decompose_sqdiff(moco::TFSquaredDifference *node) +{ + /** + * @note This will decompose TFSquaredDifference node into TFSub and TFMul + * + * Before + * A --- TFSquaredDifference -- C + * B --/ + * After + * A --- TFSquaredDifference -- + * B --/ + * A --- TFSub == TFMul -- C + * B --/ + * Where + * A : x of TFSquaredDifference + * B : y of TFSquaredDifference + * C : a node that uses TFSquaredDifference as an input + * TFSquaredDifference is disconnected from C + * A and B are drawn multiple times to simplify the diagram + */ + + auto node_A = node->x(); + auto node_B = node->y(); + + auto sub_node = node->graph()->nodes()->create<moco::TFSub>(); + auto mul_node = node->graph()->nodes()->create<moco::TFMul>(); + + // update connections + sub_node->x(node_A); + sub_node->y(node_B); + mul_node->x(sub_node); + mul_node->y(sub_node); + + // replace node + replace(node).with(mul_node); + + return true; +} + +} // namespace + +namespace moco +{ + +bool ResolveSquaredDifference::run(loco::Graph *graph) +{ + auto active_nodes = loco::active_nodes(loco::output_nodes(graph)); + bool changed = false; + + for (auto node : active_nodes) + { + if (node->dialect() == TFDialect::get()) + { + auto tf_node = dynamic_cast<moco::TFSquaredDifference *>(node); + if (tf_node != nullptr) + { + if (decompose_sqdiff(tf_node)) + changed = true; + } + } + } + + return changed; +} + +} // namespace moco diff --git a/compiler/moco/pass/src/Passes/SqueezeReduceNode.cpp b/compiler/moco/pass/src/Passes/SqueezeReduceNode.cpp new file mode 100644 index 000000000..0d9686328 --- /dev/null +++ b/compiler/moco/pass/src/Passes/SqueezeReduceNode.cpp @@ -0,0 +1,105 @@ +/* + * 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/Pass/Passes/SqueezeReduceNode.h" + +#include <moco/Support/NodeAs.h> + +#include <moco/IR/Nodes/TFConst.h> +#include <moco/IR/Nodes/TFSqueeze.h> +#include <moco/IR/Nodes/TFMean.h> + +#include <cassert> + +namespace +{ + +/** + * WHEN: + * - Reduce operations do not keep dimensions + * DO: + * - Replace original ReduceTypeOp to new ReduceTypeOp, which 'keep_dims' attribute is true + * - Insert TFSqueeze after new ReduceTypeOp + * + * + * <Before> + * in ---- ReduceTypeOp:0 (keep_dims = false) --- out(s) + * + * <After> + * --- ReduceTypeOp:0 (keep_dims = false) + * / + * in ---- ReduceTypeOp:1 (keep_dims = true) ---- TFSqueeze --- out(s) + * + * <Where> + * - 'keep_dims' attribute of ReduceTypeOp:0 is false + * + */ +template <class TFNode> bool squeeze_reduce_node(loco::Graph *graph, TFNode *reduce_node) +{ + // Don't need to squeeze reduce node + if (reduce_node->keep_dims()) + return false; + + // Reduction indices are not yet constant + auto const_reduction_indices = dynamic_cast<moco::TFConst *>(reduce_node->reduction_indices()); + if (const_reduction_indices == nullptr) + return false; + + auto squeeze_node = graph->nodes()->create<moco::TFSqueeze>(); + auto new_reduce_node = graph->nodes()->create<TFNode>(); + + new_reduce_node->input(reduce_node->input()); + new_reduce_node->reduction_indices(reduce_node->reduction_indices()); + new_reduce_node->keep_dims(true); + + // Insert squeeze dims + // TODO Support S64 type + assert(const_reduction_indices->dtype() == loco::DataType::S32); + + std::vector<int64_t> reduction_values; + for (uint32_t i = 0; i < const_reduction_indices->size<loco::DataType::S32>(); ++i) + reduction_values.push_back(const_reduction_indices->at<loco::DataType::S32>(i)); + squeeze_node->squeeze_dims(reduction_values); + + // replace + loco::replace(reduce_node).with(squeeze_node); + squeeze_node->input(new_reduce_node); + + return true; +} + +} // namespace + +namespace moco +{ + +bool SqueezeReduceNode::run(loco::Graph *graph) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(graph))) + { + if (auto shape_node = as<moco::TFMean>(node)) + { + if (squeeze_reduce_node(graph, shape_node)) + changed = true; + } + // TODO Add more reduce type operations + } + + return changed; +} + +} // namespace moco diff --git a/compiler/moco/pass/src/TensorPackEnumerator.cpp b/compiler/moco/pass/src/TensorPackEnumerator.cpp new file mode 100644 index 000000000..61a160cfb --- /dev/null +++ b/compiler/moco/pass/src/TensorPackEnumerator.cpp @@ -0,0 +1,134 @@ +/* + * 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 "TensorPackEnumerator.h" + +#include <cassert> + +namespace moco +{ + +void TensorPackEnumerator::shape(const loco::TensorShape &si, const loco::TensorShape &so) +{ + _shape_inp = si; + _shape_out = so; + + assert(_shape_inp.rank() + 1 == _shape_out.rank()); + + _rank_out = _shape_out.rank(); +} + +void TensorPackEnumerator::increment(uint32_t r) +{ + _cursor_out.at(r) = _cursor_out.at(r) + 1; + + if (_cursor_out.at(r) >= _boundary_out.at(r)) + { + if (r > 0) + { + _cursor_out.at(r) = 0; + increment(r - 1); + } + else + { + // reached to the end + } + } +} + +void TensorPackEnumerator::start(void) +{ + uint32_t rank = _rank_out; + + _cursor_out.resize(rank); + _boundary_out.resize(rank); + for (uint32_t r = 0; r < rank; ++r) + { + _cursor_out.at(r) = 0; + _boundary_out.at(r) = _shape_out.dim(r).value(); + } + + rank = _rank_out - 1; + _cursor_inp.resize(rank); + _boundary_inp.resize(rank); + for (uint32_t r = 0; r < rank; ++r) + { + _cursor_inp.at(r) = 0; + _boundary_inp.at(r) = _shape_inp.dim(r).value(); + } + _num_inp = 0; +} + +bool TensorPackEnumerator::valid(void) +{ + uint32_t rank = _rank_out; + for (uint32_t r = 0; r < rank; ++r) + { + if (_cursor_out.at(r) >= _boundary_out.at(r)) + { + return false; + } + } + return true; +} + +void TensorPackEnumerator::advance(void) +{ + uint32_t r = _rank_out - 1; + increment(r); + + // from _cursor_out, set _cursor_inp and _num + for (int32_t r = 0, s = 0; r < _rank_out; ++r) + { + if (r == _axis) + { + _num_inp = _cursor_out.at(r); + } + else + { + _cursor_inp.at(s) = _cursor_out.at(r); + s++; + } + } +} + +uint32_t TensorPackEnumerator::inp_num(void) const { return _num_inp; } + +uint32_t TensorPackEnumerator::inp_element(void) const +{ + uint32_t rank = _rank_out - 1; + uint32_t element = 0; + for (uint32_t r = 0; r < rank; ++r) + { + uint32_t dim = _boundary_inp.at(r); + element = element * dim + _cursor_inp.at(r); + } + return element; +} + +uint32_t TensorPackEnumerator::out_element(void) const +{ + uint32_t rank = _rank_out; + uint32_t element = 0; + for (uint32_t r = 0; r < rank; ++r) + { + uint32_t dim = _boundary_out.at(r); + element = element * dim + _cursor_out.at(r); + } + return element; +} + +} // namespace moco diff --git a/compiler/moco/pass/src/TensorPackEnumerator.h b/compiler/moco/pass/src/TensorPackEnumerator.h new file mode 100644 index 000000000..efdec3eb6 --- /dev/null +++ b/compiler/moco/pass/src/TensorPackEnumerator.h @@ -0,0 +1,66 @@ +/* + * 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_TENSOR_PACK_ENUMERATOR_H__ +#define __MOCO_TENSOR_PACK_ENUMERATOR_H__ + +#include <loco/IR/TensorShape.h> + +#include <vector> + +namespace moco +{ + +using u32v_t = std::vector<uint32_t>; + +class TensorPackEnumerator +{ +public: + TensorPackEnumerator() = default; + +public: + void shape(const loco::TensorShape &si, const loco::TensorShape &so); + void axis(uint32_t axis) { _axis = axis; } + +public: + void start(void); + bool valid(void); + void advance(void); + +public: + uint32_t inp_num(void) const; + uint32_t inp_element(void) const; + uint32_t out_element(void) const; + +private: + void increment(uint32_t); + +private: + loco::TensorShape _shape_inp; + loco::TensorShape _shape_out; + + uint32_t _axis = 0; + uint32_t _rank_out = 0; + uint32_t _num_inp = 0; + u32v_t _cursor_inp; + u32v_t _cursor_out; + u32v_t _boundary_inp; + u32v_t _boundary_out; +}; + +} // namespace moco + +#endif // __MOCO_TENSOR_PACK_ENUMERATOR_H__ diff --git a/compiler/moco/pass/src/TensorSliceEnumerator.cpp b/compiler/moco/pass/src/TensorSliceEnumerator.cpp new file mode 100644 index 000000000..58bd0554c --- /dev/null +++ b/compiler/moco/pass/src/TensorSliceEnumerator.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TensorSliceEnumerator.h" + +#include <cassert> + +namespace moco +{ + +void TensorSliceEnumerator::shape(loco::TensorShape &s) +{ + _shape_in = s; + _rank_in = _shape_in.rank(); +} + +void TensorSliceEnumerator::increment(uint32_t r) +{ + if (_cursor.at(r) < _boundary.at(r)) + _cursor.at(r) = _cursor.at(r) + 1; + else + { + if (r > 0) + { + _cursor.at(r) = _begin[r]; + increment(r - 1); + } + else + { + // reached to the end + } + } +} + +void TensorSliceEnumerator::start(void) +{ + auto rank = _rank_in; + + _cursor.resize(rank); + _boundary.resize(rank); + for (uint32_t r = 0; r < rank; ++r) + { + _cursor.at(r) = _begin[r]; + _boundary.at(r) = _end[r]; + } +} + +bool TensorSliceEnumerator::valid(void) +{ + auto rank = _rank_in; + for (uint32_t r = 0; r < rank; ++r) + { + if (_cursor.at(r) >= _boundary.at(r)) + return false; + } + return true; +} + +void TensorSliceEnumerator::advance(void) +{ + uint32_t r = _rank_in - 1; + increment(r); +} + +uint32_t TensorSliceEnumerator::cursor(uint32_t rank) const +{ + assert(rank < _rank_in); + return _cursor.at(rank); +} + +} // namespace moco diff --git a/compiler/moco/pass/src/TensorSliceEnumerator.h b/compiler/moco/pass/src/TensorSliceEnumerator.h new file mode 100644 index 000000000..c8206fe9d --- /dev/null +++ b/compiler/moco/pass/src/TensorSliceEnumerator.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MOCO_TENSOR_SLICE_ENUMERATOR_H__ +#define __MOCO_TENSOR_SLICE_ENUMERATOR_H__ + +#include <loco/IR/TensorShape.h> + +#include <vector> + +namespace moco +{ + +using u32v_t = std::vector<uint32_t>; + +class TensorSliceEnumerator +{ +public: + TensorSliceEnumerator() = default; + +public: + void shape(loco::TensorShape &s); + void begin(u32v_t &b) { _begin = b; } + void end(u32v_t &e) { _end = e; } + +public: + void start(void); + bool valid(void); + void advance(void); + + uint32_t cursor(uint32_t rank) const; + const u32v_t cursor(void) const { return _cursor; } + +private: + void increment(uint32_t); + +private: + loco::TensorShape _shape_in; + + uint32_t _rank_in = 0; + u32v_t _cursor; + u32v_t _boundary; + u32v_t _begin; + u32v_t _end; +}; + +} // namespace moco + +#endif // __MOCO_TENSOR_SLICE_ENUMERATOR_H__ diff --git a/compiler/moco/pass/src/TensorSliceEnumerator.test.cpp b/compiler/moco/pass/src/TensorSliceEnumerator.test.cpp new file mode 100644 index 000000000..078fe423f --- /dev/null +++ b/compiler/moco/pass/src/TensorSliceEnumerator.test.cpp @@ -0,0 +1,83 @@ +/* + * 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 "TensorSliceEnumerator.h" + +#include <gtest/gtest.h> + +TEST(TensorSliceEnumeratorTest, basic_vector) +{ + moco::TensorSliceEnumerator iter; + loco::TensorShape shape; + uint32_t rank = 1; + + shape.rank(rank); + shape.dim(0) = loco::Dimension(4); + + std::vector<uint32_t> begin = {1}; + std::vector<uint32_t> end = {3}; + + iter.shape(shape); + iter.begin(begin); + iter.end(end); + + for (iter.start(); iter.valid(); iter.advance()) + { + for (uint32_t r = 0; r < rank; ++r) + { + printf("%d ", iter.cursor(r)); + } + printf("\n"); + } + + GTEST_SUCCEED(); +} + +TEST(TensorSliceEnumeratorTest, basic_matrix) +{ + moco::TensorSliceEnumerator etor; + loco::TensorShape shape; + uint32_t rank = 2; + + shape.rank(rank); + shape.dim(0) = loco::Dimension(5); + shape.dim(1) = loco::Dimension(5); + + std::vector<uint32_t> begin = {1, 1}; + std::vector<uint32_t> end = {2, 4}; + std::vector<uint32_t> offset; + std::vector<uint32_t> cursor; + + etor.shape(shape); + etor.begin(begin); + etor.end(end); + + for (etor.start(); etor.valid(); etor.advance()) + { + cursor = etor.cursor(); + assert(cursor.size() == begin.size()); + + offset.resize(cursor.size()); + for (uint32_t r = 0; r < cursor.size(); r++) + { + offset.at(r) = cursor.at(r) - begin.at(r); + std::cout << offset.at(r) << " "; + } + std::cout << std::endl; + } + + GTEST_SUCCEED(); +} diff --git a/compiler/moco/pass/src/TestHelper.h b/compiler/moco/pass/src/TestHelper.h new file mode 100644 index 000000000..b97491dba --- /dev/null +++ b/compiler/moco/pass/src/TestHelper.h @@ -0,0 +1,67 @@ +/* + * 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 <moco/Support/NodeAs.h> + +namespace moco +{ +namespace test +{ + +template <typename T> T *find_first_node_bytype(loco::Graph *g) +{ + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (auto T_node = as<T>(node)) + { + return T_node; + } + } + + return nullptr; +} + +template <typename T> std::vector<T *> find_nodes_bytype(loco::Graph *g) +{ + std::vector<T *> find_nodes; + + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (auto T_node = as<T>(node)) + { + find_nodes.push_back(T_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 moco + +#endif // __TEST_HELPER_H__ diff --git a/compiler/moco/pass/src/TestHelper.test.cpp b/compiler/moco/pass/src/TestHelper.test.cpp new file mode 100644 index 000000000..59915d60f --- /dev/null +++ b/compiler/moco/pass/src/TestHelper.test.cpp @@ -0,0 +1,38 @@ +/* + * 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" + +namespace moco +{ +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 moco diff --git a/compiler/moco/requires.cmake b/compiler/moco/requires.cmake new file mode 100644 index 000000000..1a7d36454 --- /dev/null +++ b/compiler/moco/requires.cmake @@ -0,0 +1,8 @@ +require("loco") +require("locop") +require("stdex") +require("moco-log") +require("plier-tf") +require("mio-tf") +require("logo") +require("oops") diff --git a/compiler/moco/service/CMakeLists.txt b/compiler/moco/service/CMakeLists.txt new file mode 100644 index 000000000..dff0233b1 --- /dev/null +++ b/compiler/moco/service/CMakeLists.txt @@ -0,0 +1,24 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(moco_service SHARED ${SOURCES}) +target_include_directories(moco_service PRIVATE src) +target_include_directories(moco_service PUBLIC include) +target_link_libraries(moco_service PUBLIC loco) +target_link_libraries(moco_service PUBLIC moco_lang) +target_link_libraries(moco_service PRIVATE moco_support) +target_link_libraries(moco_service PRIVATE nncc_common) +target_link_libraries(moco_service PRIVATE stdex) +target_link_libraries(moco_service PRIVATE oops) +install(TARGETS moco_service DESTINATION lib) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(moco_service_test ${TESTS}) +target_include_directories(moco_service_test PRIVATE src) +target_link_libraries(moco_service_test moco_service) diff --git a/compiler/moco/service/README.md b/compiler/moco/service/README.md new file mode 100644 index 000000000..78906dbfe --- /dev/null +++ b/compiler/moco/service/README.md @@ -0,0 +1,3 @@ +# service + +`service` provides TensorFlow Dialect Services diff --git a/compiler/moco/service/include/moco/Service/TFShapeInferenceRule.h b/compiler/moco/service/include/moco/Service/TFShapeInferenceRule.h new file mode 100644 index 000000000..98d716c2a --- /dev/null +++ b/compiler/moco/service/include/moco/Service/TFShapeInferenceRule.h @@ -0,0 +1,38 @@ +/* + * 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_SERVICE_SHAPE_INFERENCE_RULE_H__ +#define __MOCO_SERVICE_SHAPE_INFERENCE_RULE_H__ + +#include <loco/Service/ShapeInferenceRule.h> + +namespace moco +{ + +/** + * @brief Shape inference rule for TensorFlow dialect + */ +struct TFShapeInferenceRule final : public loco::ShapeInferenceRule +{ + bool support(const API &ver) const final; + bool recognize(const loco::Dialect *) const final; + bool infer(const loco::Node *, loco::NodeShape &) const final; + void infer(const Context *, const loco::Node *, Sink *) const final; +}; + +} // namespace moco + +#endif // __MOCO_SERVICE_SHAPE_INFERENCE_RULE_H__ diff --git a/compiler/moco/service/include/moco/Service/TFTypeInferenceRule.h b/compiler/moco/service/include/moco/Service/TFTypeInferenceRule.h new file mode 100644 index 000000000..f712fdb01 --- /dev/null +++ b/compiler/moco/service/include/moco/Service/TFTypeInferenceRule.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 __MOCO_SERVICE_TYPE_INFERENCE_RULE_H__ +#define __MOCO_SERVICE_TYPE_INFERENCE_RULE_H__ + +#include <loco/Service/TypeInference.h> + +namespace moco +{ + +/** + * @brief Type Inference Rule for TFDialect + */ +struct TFTypeInferenceRule final : public loco::TypeInferenceRule +{ + bool recognize(const loco::Dialect *) const final; + bool infer(const loco::Node *, loco::DataType &) const final; +}; + +} // namespace moco + +#endif // __MOCO_SERVICE_TYPE_INFERENCE_RULE_H__ diff --git a/compiler/moco/service/src/Service/TFShapeInferenceRule.cpp b/compiler/moco/service/src/Service/TFShapeInferenceRule.cpp new file mode 100644 index 000000000..6d122c863 --- /dev/null +++ b/compiler/moco/service/src/Service/TFShapeInferenceRule.cpp @@ -0,0 +1,891 @@ +/* + * 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/Service/TFShapeInferenceRule.h" + +#include <moco/Support/TFShapeInferenceHelper.h> + +#include "moco/IR/TFDialect.h" +#include "moco/IR/TFNode.h" + +#include <loco/IR/NodeShape.h> +#include <loco/Service/ShapeInference.h> + +#include <oops/UserExn.h> + +#include <cassert> +#include <cmath> + +namespace +{ + +class ShapeInferenceAlgorithm final : public moco::TFNodeVisitor<loco::NodeShape> +{ +public: + ShapeInferenceAlgorithm(const loco::ShapeInferenceRule::Context *ctx) : _ctx{ctx} + { + // DO NOTHING + } + +private: + const loco::ShapeInferenceRule::Context *_ctx; + +private: + bool shape_known(const loco::Node *node) const { return _ctx->known(node); } + loco::NodeShape node_shape(const loco::Node *node) const { return _ctx->get(node); } + +private: + loco::NodeShape binary_node_shape(const moco::TFNode::Node *node) + { + // This helper works only for binary node. + assert(node->arity() == 2); + + auto lhs_shape = node_shape(node->arg(0)); + auto rhs_shape = node_shape(node->arg(1)); + + loco::TensorShape lhs_tensorshape = lhs_shape.as<loco::TensorShape>(); + loco::TensorShape rhs_tensorshape = rhs_shape.as<loco::TensorShape>(); + loco::TensorShape sum_tensorshape = moco::broadcast_shape(lhs_tensorshape, rhs_tensorshape); + + loco::NodeShape sum_shape({sum_tensorshape}); + + return sum_shape; + } + + loco::NodeShape node_shape_with_check(const moco::TFNode::Node *node) + { + auto nodeshape = node_shape(node); + assert(nodeshape.domain() == loco::Domain::Tensor); + + return nodeshape; + } + + bool valid_scalar_value(moco::TFConst *node) + { + auto nodeshape = node_shape(node); + if (nodeshape.domain() != loco::Domain::Tensor) + { + return false; + } + 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; + } + + return true; + } + + int32_t scalar_value(moco::TFConst *node) + { + auto nodeshape = node_shape(node); + assert(node->dtype() == loco::DataType::S32); + + auto tensor_shape = nodeshape.as<loco::TensorShape>(); + assert(tensor_shape.rank() == 0 || tensor_shape.rank() == 1); + + return node->at<loco::DataType::S32>(0); + } + +public: + loco::NodeShape visit(const moco::TFAdd *node) final { return binary_node_shape(node); } + + loco::NodeShape visit(const moco::TFAvgPool *node) final + { + auto value_shape = node_shape(node->value()); + assert(value_shape.domain() != loco::Domain::Unknown); + + moco::PlaneInference infer_plane_shape; + + infer_plane_shape.padding(node->padding()); + infer_plane_shape.stride(moco::stride_of(node->strides(), node->data_layout())); + infer_plane_shape.window(moco::window_of(node->ksize(), node->data_layout())); + + auto input_feature_shape = moco::as_feature_shape(value_shape, node->data_layout()); + auto input_plane_shape = moco::make_plane_shape(input_feature_shape); + auto output_feature_shape = input_feature_shape; + auto output_plane_shape = infer_plane_shape(input_plane_shape); + + moco::update(output_feature_shape).with(output_plane_shape); + + return moco::as_tensor_shape(output_feature_shape, node->data_layout()); + } + + loco::NodeShape visit(const moco::TFBiasAdd *node) final + { + return node_shape_with_check(node->value()); + } + + loco::NodeShape visit(const moco::TFConcatV2 *node) final + { + // axis shape should be available + auto axis_node = node->axis(); + auto axis_shape = node_shape(axis_node); + assert(axis_shape.domain() != loco::Domain::Unknown); + + // check all input shapes and all ranks should be same + auto value_a = node->values(0); + auto value_a_shape = node_shape(value_a); + assert(value_a_shape.domain() == loco::Domain::Tensor); + auto value_a_tensor_shape = value_a_shape.as<loco::TensorShape>(); + uint32_t a_rank = value_a_tensor_shape.rank(); + + uint32_t num_values = node->num_values(); + for (uint32_t ni = 1; ni < num_values; ++ni) + { + auto value_b = node->values(ni); + auto value_b_shape = node_shape(value_b); + assert(value_b_shape.domain() == loco::Domain::Tensor); + auto value_b_tensor_shape = value_b_shape.as<loco::TensorShape>(); + assert(a_rank == value_b_tensor_shape.rank()); + } + + int32_t axis_value = 0; + bool axis_available = false; + { + // check for axis is TFConst + auto tfconst = dynamic_cast<moco::TFConst *>(axis_node); + if (tfconst != nullptr) + { + if (valid_scalar_value(tfconst)) + { + axis_value = scalar_value(tfconst); + axis_available = true; + } + } + } + if (!axis_available) + { + // TODO may need to refine error message + throw oops::UserExn("ConcatV2 node does not have axis input", node->name()); + } + + uint32_t axis_absolute = (axis_value >= 0) ? axis_value : (int32_t)a_rank + axis_value; + loco::TensorShape output_tensor_shape = value_a_tensor_shape; + + for (uint32_t index = 0; index < a_rank; ++index) + { + if (value_a_tensor_shape.dim(index).known()) + { + uint32_t dim = value_a_tensor_shape.dim(index).value(); + uint32_t dim_acc = dim; + + for (uint32_t ni = 1; ni < num_values; ++ni) + { + auto value_b = node->values(ni); + auto value_b_shape = node_shape(value_b); + assert(value_b_shape.domain() == loco::Domain::Tensor); + auto value_b_tensor_shape = value_b_shape.as<loco::TensorShape>(); + assert(value_b_tensor_shape.dim(index).known()); + if (index == axis_absolute) + dim_acc += value_b_tensor_shape.dim(index).value(); + else + assert(dim == value_b_tensor_shape.dim(index).value()); + } + output_tensor_shape.dim(index) = dim_acc; + } + else + output_tensor_shape.dim(index).unset(); + } + return loco::NodeShape(output_tensor_shape); + } + + loco::NodeShape visit(const moco::TFConst *node) final + { + loco::TensorShape output_tensor_shape; + + uint32_t rank = node->rank(); + output_tensor_shape.rank(rank); + for (uint32_t index = 0; index < rank; ++index) + { + if (node->dim(index).known()) + output_tensor_shape.dim(index) = node->dim(index).value(); + else + output_tensor_shape.dim(index).unset(); + } + + return loco::NodeShape(output_tensor_shape); + } + + loco::NodeShape visit(const moco::TFConv2D *node) final + { + 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>(); // in HWIO + auto node_stride = moco::stride_of(node->strides(), node->data_layout()); + auto node_window = moco::window_of(ker_tensor_shape, "HWIO"); + + moco::PlaneInference infer_plane_shape; + + infer_plane_shape.padding(node->padding()); + infer_plane_shape.stride(node_stride); + infer_plane_shape.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); + // output count is from input count, depth is from kernel 'O' which is dim(3) + auto output_feature_shape = input_feature_shape; + output_feature_shape.depth() = ker_tensor_shape.dim(3).value(); + + auto output_plane_shape = infer_plane_shape(input_plane_shape); + + moco::update(output_feature_shape).with(output_plane_shape); + + return moco::as_tensor_shape(output_feature_shape, node->data_layout()); + } + + loco::NodeShape visit(const moco::TFConv2DBackpropInput *node) final + { + // TFConv2DBackpropInput's first input, named 'input_sizes', actually contains shape of node + // output's feature map. We can get shape of TFConv2DBackpropInput by just copying this. + // TODO Support when 'input_sizes' is not TFConst, or support constant folding + auto input_sizes_node = dynamic_cast<moco::TFConst *>(node->input_sizes()); + if (input_sizes_node == nullptr) + { + // we are now supporting somekind of constant folding for this node, wait till it is finished + loco::NodeShape unknown; + return unknown; + } + + // Let's support S32 for time being + // TODO Support other integer types + assert(input_sizes_node->dtype() == loco::DataType::S32); + assert(input_sizes_node->size<loco::DataType::S32>() == 4); + + // copy! + loco::TensorShape ofm_tensor_shape; + ofm_tensor_shape.rank(4); + for (uint32_t i = 0; i < 4; ++i) + { + int32_t dim = input_sizes_node->at<loco::DataType::S32>(i); + assert(dim > 0); + ofm_tensor_shape.dim(i) = (uint32_t)dim; + } + + return loco::NodeShape(ofm_tensor_shape); + } + + loco::NodeShape visit(const moco::TFDepthwiseConv2dNative *node) final + { + auto input_shape = moco::node_shape(node->input()); // NHWC + auto ker_shape = moco::node_shape(node->filter()); + auto ker_tensor_shape = ker_shape.as<loco::TensorShape>(); // in HWCM + auto node_stride = moco::stride_of(node->strides(), node->data_layout()); + auto node_window = moco::window_of(ker_tensor_shape, "HWCM"); + + moco::PlaneInference infer_plane_shape; + + infer_plane_shape.padding(node->padding()); + infer_plane_shape.stride(node_stride); + infer_plane_shape.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); + // output count is from input count, depth is from kernel 'CM' which is dim(2) * dim(3) + auto output_feature_shape = input_feature_shape; + output_feature_shape.depth() = + loco::Dimension(ker_tensor_shape.dim(2).value() * ker_tensor_shape.dim(3).value()); + + auto output_plane_shape = infer_plane_shape(input_plane_shape); + + moco::update(output_feature_shape).with(output_plane_shape); + + return moco::as_tensor_shape(output_feature_shape, node->data_layout()); + } + + loco::NodeShape visit(const moco::TFFakeQuantWithMinMaxVars *node) final + { + return node_shape_with_check(node->inputs()); + } + + loco::NodeShape visit(const moco::TFFusedBatchNorm *node) final + { + return node_shape_with_check(node->x()); + } + + loco::NodeShape visit(const moco::TFIdentity *node) final + { + return node_shape_with_check(node->input()); + } + + loco::NodeShape visit(const moco::TFMaximum *node) final { return binary_node_shape(node); } + + loco::NodeShape visit(const moco::TFMaxPool *node) final + { + auto input_shape = node_shape(node->input()); + assert(input_shape.domain() != loco::Domain::Unknown); + + moco::PlaneInference infer_plane_shape; + + infer_plane_shape.padding(node->padding()); + infer_plane_shape.stride(moco::stride_of(node->strides(), node->data_layout())); + infer_plane_shape.window(moco::window_of(node->ksize(), node->data_layout())); + + auto input_feature_shape = moco::as_feature_shape(input_shape, node->data_layout()); + auto input_plane_shape = moco::make_plane_shape(input_feature_shape); + auto output_feature_shape = input_feature_shape; + auto output_plane_shape = infer_plane_shape(input_plane_shape); + + moco::update(output_feature_shape).with(output_plane_shape); + + return moco::as_tensor_shape(output_feature_shape, node->data_layout()); + } + + loco::NodeShape visit(const moco::TFMean *node) final + { + auto input_shape = node_shape(node->input()); + auto reduction_indices = node->reduction_indices(); + + // Get constant values if reduction_indices is const + std::vector<int32_t> reduction_values; + if (auto tfconst = dynamic_cast<moco::TFConst *>(reduction_indices)) + { + assert(tfconst->dtype() == loco::DataType::S32); + auto const_size = tfconst->size<loco::DataType::S32>(); + for (uint32_t i = 0; i < const_size; ++i) + { + int32_t axis = tfconst->at<loco::DataType::S32>(i); + if (axis < 0) + axis += input_shape.as<loco::TensorShape>().rank(); + reduction_values.push_back(axis); + } + } + else + { + // we cannot find a valid reduction indices value + loco::NodeShape unknown; + return unknown; + } + + loco::TensorShape output_shape; + auto input_tensor_shape = input_shape.as<loco::TensorShape>(); + + if (node->keep_dims()) + { + output_shape.rank(input_tensor_shape.rank()); + for (uint32_t i = 0; i < input_tensor_shape.rank(); ++i) + output_shape.dim(i) = input_tensor_shape.dim(i); + for (uint32_t i = 0; i < reduction_values.size(); ++i) + output_shape.dim(reduction_values.at(i)) = 1; + } + else + { + std::vector<bool> check_reduce(input_tensor_shape.rank(), false); + for (uint32_t i = 0; i < reduction_values.size(); ++i) + check_reduce.at(reduction_values.at(i)) = true; + + uint32_t reduce_cnt = 0; + for (uint32_t i = 0; i < check_reduce.size(); ++i) + if (check_reduce.at(i)) + ++reduce_cnt; + + output_shape.rank(input_tensor_shape.rank() - reduce_cnt); + for (uint32_t i = 0, j = 0; i < check_reduce.size(); ++i) + if (check_reduce.at(i) == false) + output_shape.dim(j++) = i; + } + + return loco::NodeShape(output_shape); + } + + loco::NodeShape visit(const moco::TFMul *node) final { return binary_node_shape(node); } + + loco::NodeShape visit(const moco::TFPack *node) final + { + loco::NodeShape unknown; + + auto input_shape_0 = node_shape(node->values(0)); + if (input_shape_0.domain() != loco::Domain::Tensor) + { + // TODO fix this for other cases + // We support only valid tensor shape for now + return unknown; + } + loco::TensorShape tensor_shape_0 = input_shape_0.as<loco::TensorShape>(); + + // all input shapes should be same + auto num_values = node->N(); + for (uint32_t i = 1; i < num_values; ++i) + { + auto input_shape = node_shape(node->values(i)); + if (input_shape.domain() != loco::Domain::Tensor) + { + // TODO ditto + return unknown; + } + + loco::TensorShape tensor_shape = input_shape.as<loco::TensorShape>(); + if (!(input_shape_0 == input_shape)) + { + throw oops::UserExn("All input values shape should be same", node->name()); + } + } + + // output rank will be +1 of rank of the input + // axis should be in range of [-r, r), where r is rank of the output + auto axis = node->axis(); + int32_t rank = static_cast<int32_t>(tensor_shape_0.rank()); + assert(rank >= 0); + int32_t rank_output = rank + 1; + if (axis < -rank_output || rank_output <= axis) + { + throw oops::UserExn("axis is out of range", node->name()); + } + + auto axis_stack = (axis >= 0) ? axis : rank_output + axis; + + loco::TensorShape output_tensor_shape; + + output_tensor_shape.rank(rank_output); + for (int32_t r = 0; r < axis_stack; ++r) + { + output_tensor_shape.dim(r).set(tensor_shape_0.dim(r).value()); + } + output_tensor_shape.dim(axis_stack).set(num_values); + for (int32_t r = axis_stack; r < rank; ++r) + { + output_tensor_shape.dim(r + 1).set(tensor_shape_0.dim(r).value()); + } + + return loco::NodeShape(output_tensor_shape); + } + + loco::NodeShape visit(const moco::TFPad *node) final + { + auto input_shape = node_shape(node->input()); + assert(input_shape.domain() == loco::Domain::Tensor); + + auto const_paddings = dynamic_cast<moco::TFConst *>(node->paddings()); + assert(const_paddings); + assert(const_paddings->dtype() == loco::DataType::S32); + assert(const_paddings->rank() == 2); + + loco::TensorShape input_tensor_shape = input_shape.as<loco::TensorShape>(); + loco::TensorShape output_tensor_shape; + + output_tensor_shape.rank(input_tensor_shape.rank()); + for (uint32_t axis = 0; axis < input_tensor_shape.rank(); ++axis) + { + output_tensor_shape.dim(axis) = input_tensor_shape.dim(axis).value() + + const_paddings->at<loco::DataType::S32>(axis * 2) + + const_paddings->at<loco::DataType::S32>(axis * 2 + 1); + } + + return loco::NodeShape{output_tensor_shape}; + } + + loco::NodeShape visit(const moco::TFPlaceholder *node) final + { + loco::TensorShape output_tensor_shape; + + uint32_t rank = node->rank(); + output_tensor_shape.rank(rank); + for (uint32_t index = 0; index < rank; ++index) + { + if (node->dim(index).known()) + output_tensor_shape.dim(index) = node->dim(index).value(); + else + output_tensor_shape.dim(index).unset(); + } + + return loco::NodeShape(output_tensor_shape); + } + + loco::NodeShape visit(const moco::TFRealDiv *node) final { return binary_node_shape(node); } + + loco::NodeShape visit(const moco::TFRelu *node) final + { + return node_shape_with_check(node->features()); + } + + loco::NodeShape visit(const moco::TFRelu6 *node) final + { + return node_shape_with_check(node->features()); + } + + loco::NodeShape visit(const moco::TFReshape *node) final + { + loco::NodeShape unknown; + + // For now, we only consider Fixed Reshape, i.e. Reshape with determined + // 'shape' input. So here we only support case when 'shape' input of + // TFReshape is TFConst. If 'shape' input is not TFConst, another + // transform (e.g. constant folding) should be done beforehand to make + // it TFConst. + // TODO Support dynamic Reshape + // Note that 'shape()' here is 'shape' input, not node's shape information + auto const_shape_input = dynamic_cast<moco::TFConst *>(node->shape()); + if (!const_shape_input) + { + // 'shape' input of TFReshape is not TFConst, we can not do shape inference + return unknown; + } + + // 'Shape' input should be integer tensor of rank 1, e.g. [2, 3, 4] or [3, -1] + 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); + + loco::TensorShape output_shape; + output_shape.rank(shape_rank); + for (uint32_t axis = 0; axis < shape_rank; ++axis) + { + auto shape_dim = const_shape_input->at<loco::DataType::S32>(axis); + if (shape_dim == -1) + { + // Reshape's new shape has wildcard dimension, i.e. dynamic reshape + return unknown; + } + assert(shape_dim >= 1); + output_shape.dim(axis) = shape_dim; + } + + // TODO Compare 'tensor' input and validate coherency? + // Not sure this is appropriate stage for this task. + + return loco::NodeShape(output_shape); + } + + loco::NodeShape visit(const moco::TFRsqrt *node) final + { + return node_shape_with_check(node->x()); + } + + loco::NodeShape visit(const moco::TFShape *node) final + { + auto input_shape = node_shape(node->input()); + auto input_tensor_shape = input_shape.as<loco::TensorShape>(); + + loco::TensorShape output_shape; + + // Note that input shape becomes node(TFShape)'s value + output_shape.rank(1); + output_shape.dim(0) = input_tensor_shape.rank(); + + return loco::NodeShape(output_shape); + } + + loco::NodeShape visit(const moco::TFSoftmax *node) final + { + return node_shape_with_check(node->logits()); + } + + loco::NodeShape visit(const moco::TFSqrt *node) final { return node_shape_with_check(node->x()); } + + loco::NodeShape visit(const moco::TFSquaredDifference *node) final + { + return binary_node_shape(node); + } + + loco::NodeShape visit(const moco::TFSqueeze *node) final + { + auto input_shape = node_shape(node->input()); + + // TODO Not sure Squeeze only get input as Tensor + // Note that tensor_shape() has assertion in it + auto input_tensor_shape = input_shape.as<loco::TensorShape>(); + + auto squeeze_dims_vec = node->squeeze_dims(); + std::set<int64_t> squeeze_dims(squeeze_dims_vec.cbegin(), squeeze_dims_vec.cend()); + + loco::TensorShape output_shape; + uint32_t output_rank = 0; + + if (squeeze_dims.empty()) + { + // Remove all dimensions whose value is 1 + for (uint32_t axis = 0; axis < input_tensor_shape.rank(); ++axis) + { + assert(input_tensor_shape.dim(axis).known()); + auto dim = input_tensor_shape.dim(axis).value(); + if (dim != 1) + { + assert(dim > 1); + output_shape.rank(++output_rank); + output_shape.dim(output_rank - 1) = dim; + } + } + } + else + { + uint32_t input_rank = input_tensor_shape.rank(); + + // Sanity check for 'squeeze_dims' + auto is_valid_squeeze_dims = [&squeeze_dims, &input_rank]() { + if (!(squeeze_dims.size() < input_rank)) + return false; + for (auto squeeze_dim : squeeze_dims) + { + if (!(squeeze_dim >= -(int64_t)input_rank)) + return false; + if (!(squeeze_dim < (int64_t)input_rank)) + return false; + } + return true; + }; + + if (!is_valid_squeeze_dims()) + { + throw oops::UserExn("Invalid squeeze dimension", node->name()); + } + + // Resolve negative squeeze dimension + std::set<int64_t> resolved_squeeze_dims; + for (auto squeeze_dim : squeeze_dims) + { + if (squeeze_dim < 0) + resolved_squeeze_dims.insert(squeeze_dim + (int64_t)input_rank); + else + resolved_squeeze_dims.insert(squeeze_dim); + } + + // Remove squeeze dimensions only + for (uint32_t axis = 0; axis < input_rank; ++axis) + { + assert(input_tensor_shape.dim(axis).known()); + auto dim = input_tensor_shape.dim(axis).value(); + if (resolved_squeeze_dims.find((int64_t)axis) == resolved_squeeze_dims.cend()) + { + // Not squeeze dim + output_shape.rank(++output_rank); + output_shape.dim(output_rank - 1) = dim; + } + else + { + // Is squeeze dim + assert(dim == 1); + // DO NOTHING + } + } + } + + assert(output_shape.rank() > 0); + + return loco::NodeShape(output_shape); + } + + loco::NodeShape visit(const moco::TFStopGradient *node) final + { + return node_shape_with_check(node->input()); + } + + loco::NodeShape visit(const moco::TFStridedSlice *node) final + { + loco::NodeShape unknown; + auto input_shape = node_shape(node->input()); + if (input_shape.domain() != loco::Domain::Tensor) + { + // TODO fix this for other cases + // We support only tensor shape for now + return unknown; + } + + // TODO support full mask features: see import codes also + // Limited attributes for now + assert(node->begin_mask() == 0); + assert(node->end_mask() == 0); + assert(node->ellipsis_mask() == 0); + assert(node->shrink_axis_mask() == 1); + + auto const_begin = dynamic_cast<moco::TFConst *>(node->begin()); + auto const_end = dynamic_cast<moco::TFConst *>(node->end()); + auto const_strides = dynamic_cast<moco::TFConst *>(node->strides()); + + assert(dynamic_cast<moco::TFConst *>(node->input()) != nullptr); + assert(const_begin != nullptr); + assert(const_end != nullptr); + assert(const_strides != nullptr); + + auto input_tensor_shape = input_shape.as<loco::TensorShape>(); + auto input_rank = input_tensor_shape.rank(); + auto output_rank = input_rank; + + // TODO support strides with > 1 + uint32_t elements = const_strides->size<loco::DataType::S32>(); + for (uint32_t e = 0; e < elements; ++e) + assert(const_strides->at<loco::DataType::S32>(e) == 1); + + // lets apply begin ~ end range from input shape + loco::TensorShape output_shape_range; + + output_shape_range.rank(input_rank); + for (uint32_t r = 0; r < input_rank; ++r) + { + // TODO apply begin/end mask + // TODO apply ellipsis mask + // TODO apply strides + auto end = const_end->at<loco::DataType::S32>(r); + auto begin = const_begin->at<loco::DataType::S32>(r); + auto size = end - begin; + output_shape_range.dim(r).set(size); + } + + // get final tensor shape from applying shrink mask to output_shape_range + loco::TensorShape output_tensor_shape; + + if (node->shrink_axis_mask() != 0) + { + for (uint32_t rs = 0; rs < input_rank; ++rs) + { + int32_t bit = 1 << rs; + int32_t mask = node->shrink_axis_mask(); + if (bit & mask) + { + // shrink one dimension + assert(output_rank > 0); + output_rank = output_rank - 1; + } + } + output_tensor_shape.rank(output_rank); + for (uint32_t rs = 0, rd = 0; rs < input_rank; ++rs) + { + int32_t bit = 1 << rs; + int32_t mask = node->shrink_axis_mask(); + if ((bit & mask) == 0) + { + // use this dimension + output_tensor_shape.dim(rd).set(output_shape_range.dim(rs).value()); + rd++; + } + // else this dimension is shrink-ed + } + } + else + { + output_tensor_shape = output_shape_range; + } + + return loco::NodeShape(output_tensor_shape); + } + + loco::NodeShape visit(const moco::TFSub *node) final { return binary_node_shape(node); } + + loco::NodeShape visit(const moco::TFTanh *node) final { return node_shape_with_check(node->x()); } + + // For virtual nodes + loco::NodeShape visit(const moco::TFPush *node) { return node_shape_with_check(node->from()); } + +public: + loco::NodeShape visit(const moco::TFNode *) final + { + loco::NodeShape unknown; + return unknown; + } +}; + +} // namespace + +namespace +{ +namespace compat +{ + +struct Context final : public loco::ShapeInferenceRule::Context +{ + bool known(const loco::Node *node) const final { return loco::shape_known(node); } + loco::NodeShape get(const loco::Node *node) const final { return loco::shape_get(node); } +}; + +class Sink final : public loco::ShapeInferenceRule::Sink +{ +public: + enum Status + { + Unknown, + Okay, + Fail, + }; + +public: + const Status &status(void) const { return _status; } + const loco::NodeShape &shape(void) const { return _shape; } + +public: + void okay(const loco::NodeShape &shape) final + { + _status = Okay; + _shape = shape; + } + + void fail(void) final + { + // Notify failrue + _status = Fail; + } + +private: + Status _status = Unknown; + loco::NodeShape _shape; +}; + +} // namespace compat +} // namespace + +namespace moco +{ + +bool TFShapeInferenceRule::support(const API &api) const +{ + return api == API::V1 or api == API::V2; +} + +bool TFShapeInferenceRule::recognize(const loco::Dialect *d) const +{ + // handle only TensorFlow dialect + return TFDialect::get() == d; +} + +bool TFShapeInferenceRule::infer(const loco::Node *node, loco::NodeShape &shape) const +{ + ::compat::Context ctx; + ::compat::Sink sink; + + infer(&ctx, node, &sink); + + assert(sink.status() == ::compat::Sink::Okay or sink.status() == ::compat::Sink::Fail); + + if (sink.status() == ::compat::Sink::Fail) + { + return false; + } + + shape = sink.shape(); + + return true; +} + +void TFShapeInferenceRule::infer(const Context *ctx, const loco::Node *node, Sink *sink) const +{ + assert(node->dialect() == TFDialect::get()); + assert(dynamic_cast<const TFNode *>(node) != nullptr); + + ShapeInferenceAlgorithm alg{ctx}; + auto shape = dynamic_cast<const TFNode *>(node)->accept(&alg); + + if (shape.domain() == loco::Domain::Unknown) + sink->fail(); + else + sink->okay(shape); +} + +} // namespace moco diff --git a/compiler/moco/service/src/Service/TFShapeInferenceRule.test.cpp b/compiler/moco/service/src/Service/TFShapeInferenceRule.test.cpp new file mode 100644 index 000000000..1e1b48ca7 --- /dev/null +++ b/compiler/moco/service/src/Service/TFShapeInferenceRule.test.cpp @@ -0,0 +1,500 @@ +/* + * 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/Service/TFShapeInferenceRule.h" + +#include "TestHelper.h" + +#include "moco/IR/TFNodes.h" + +#include <loco.h> +#include <loco/Service/ShapeInference.h> + +#include <gtest/gtest.h> + +using namespace moco::test; + +namespace +{ + +moco::TFAvgPool *avgpool_network_simple1331(loco::Graph *graph) +{ + auto avgpool_node = graph->nodes()->create<moco::TFAvgPool>(); + + avgpool_node->data_layout("NHWC"); + avgpool_node->ksize({1, 3, 3, 1}); + avgpool_node->strides({1, 1, 1, 1}); + + // Dummy const node as ifm, just to fake TFShapeInferenceRule for TFAvgPool. + auto const_node = graph->nodes()->create<moco::TFConst>(); + { + const_node->rank(4); + const_node->dim(0).set(1); + const_node->dim(1).set(3); + const_node->dim(2).set(3); + const_node->dim(3).set(1); + } + avgpool_node->value(const_node); + + setup_output_node(graph, avgpool_node); + + return avgpool_node; +} + +} // namespace + +TEST(TFShapeInferenceRule, avgpool_same) +{ + moco::TFShapeInferenceRule shape_infer; + loco::Graph graph; + + auto avgpool_node = avgpool_network_simple1331(&graph); + avgpool_node->padding("SAME"); + + bool cont = true; + while (cont) + { + cont = loco::apply(&shape_infer).to(&graph); + }; + + auto nodeshape = loco::shape_get(avgpool_node); + auto tshape = nodeshape.as<loco::TensorShape>(); + ASSERT_EQ(tshape.rank(), 4); + ASSERT_EQ(tshape.dim(0).value(), 1); + ASSERT_EQ(tshape.dim(1).value(), 3); + ASSERT_EQ(tshape.dim(2).value(), 3); + ASSERT_EQ(tshape.dim(3).value(), 1); +} + +TEST(TFShapeInferenceRule, avgpool_valid) +{ + moco::TFShapeInferenceRule shape_infer; + loco::Graph graph; + + auto avgpool_node = avgpool_network_simple1331(&graph); + avgpool_node->padding("VALID"); + + bool cont = true; + while (cont) + { + cont = loco::apply(&shape_infer).to(&graph); + }; + + auto nodeshape = loco::shape_get(avgpool_node); + auto tshape = nodeshape.as<loco::TensorShape>(); + ASSERT_EQ(tshape.rank(), 4); + ASSERT_EQ(tshape.dim(0).value(), 1); + ASSERT_EQ(tshape.dim(1).value(), 1); + ASSERT_EQ(tshape.dim(2).value(), 1); + ASSERT_EQ(tshape.dim(3).value(), 1); +} + +namespace +{ + +void conv2d_test(const std::array<uint32_t, 4> ifm_shape, const std::array<uint32_t, 4> ker_shape, + const std::array<uint32_t, 2> stride_h_w, std::string padding, + const std::array<uint32_t, 4> expected_shape) +{ + moco::TFShapeInferenceRule shape_infer; + loco::Graph graph; + + auto conv2d_node = graph.nodes()->create<moco::TFConv2D>(); + conv2d_node->data_layout("NHWC"); + conv2d_node->strides({1, stride_h_w[0], stride_h_w[1], 1}); + conv2d_node->padding(padding); + + auto ifm_node = graph.nodes()->create<moco::TFConst>(); + { + ifm_node->rank(4); + ifm_node->dim(0).set(ifm_shape[0]); + ifm_node->dim(1).set(ifm_shape[1]); + ifm_node->dim(2).set(ifm_shape[2]); + ifm_node->dim(3).set(ifm_shape[3]); + } + + auto ker_node = graph.nodes()->create<moco::TFConst>(); + { + ker_node->rank(4); + ker_node->dim(0).set(ker_shape[0]); + ker_node->dim(1).set(ker_shape[1]); + ker_node->dim(2).set(ker_shape[2]); + ker_node->dim(3).set(ker_shape[3]); + } + + conv2d_node->input(ifm_node); + conv2d_node->filter(ker_node); + + setup_output_node(&graph, conv2d_node); + + bool cont = true; + while (cont) + { + cont = loco::apply(&shape_infer).to(&graph); + }; + + auto nodeshape = loco::shape_get(conv2d_node); + auto tshape = nodeshape.as<loco::TensorShape>(); + ASSERT_EQ(tshape.rank(), 4); + ASSERT_EQ(tshape.dim(0).value(), expected_shape[0]); + ASSERT_EQ(tshape.dim(1).value(), expected_shape[1]); + ASSERT_EQ(tshape.dim(2).value(), expected_shape[2]); + ASSERT_EQ(tshape.dim(3).value(), expected_shape[3]); +} + +} // namespace + +/* + Testing "InceptionV3/InceptionV3/Conv2d_1a_3x3/Conv2D" Conv2D node in Inception_v3: + The result shape of this test is generated with the code below: + + ifm = tf.constant(value=1.1, shape=[1, 299, 299, 3]) + ker = tf.constant(value=1.1, shape=[3, 3, 3, 32]) + + out = tf.nn.conv2d(ifm, ker, strides = [1, 2, 2, 1], padding= 'VALID') + + with tf.Session() as sess: + res = sess.run(out) + print(res.shape) + */ +TEST(TFShapeInferenceRule, conv2d_VALID) +{ + conv2d_test({1, 299, 299, 3}, // ifm + {3, 3, 3, 32}, // ker + {2, 2}, // strides + "VALID", // padding + {1, 149, 149, 32}); // expected shape after FixShape +} + +/* + Testing "InceptionV3/InceptionV3/Conv2d_2b_3x3/Conv2D" Conv2D node in Inception_v3: + The result shape of this test is generated with the code below: + + ifm = tf.constant(value=1.1, shape=[1, 147, 147, 32]) + ker = tf.constant(value=1.1, shape=[3, 3, 32, 64]) + + out = tf.nn.conv2d(ifm, ker, strides = [1, 1, 1, 1], padding= 'SAME') + + with tf.Session() as sess: + res = sess.run(out) + print(res.shape) + */ +TEST(TFShapeInferenceRule, conv2d_SAME) +{ + conv2d_test({1, 147, 147, 32}, // ifm + {3, 3, 32, 64}, // ker + {1, 1}, // strides + "SAME", // padding + {1, 147, 147, 64}); // expected shape after FixShape +} + +/* + Testing Pack +*/ +namespace +{ + +moco::TFConst *const_scalar(loco::Graph *graph, int32_t val) +{ + auto const_node = graph->nodes()->create<moco::TFConst>(); + + const_node->dtype(loco::DataType::S32); + const_node->rank(0); + const_node->size<loco::DataType::S32>(1); + const_node->at<loco::DataType::S32>(0) = val; + + return const_node; +} + +moco::TFConst *const_vector(loco::Graph *graph, int32_t dim) +{ + auto const_node = graph->nodes()->create<moco::TFConst>(); + + const_node->dtype(loco::DataType::S32); + const_node->rank(1); + const_node->dim(0).set(dim); + + const_node->size<loco::DataType::S32>(dim); + for (int32_t i = 0; i < dim; ++i) + const_node->at<loco::DataType::S32>(i) = i; + + return const_node; +} + +moco::TFConst *const_vector_init(loco::Graph *graph, std::vector<int32_t> values) +{ + auto const_node = graph->nodes()->create<moco::TFConst>(); + auto dim = values.size(); + + const_node->dtype(loco::DataType::S32); + const_node->rank(1); + const_node->dim(0).set(dim); + + const_node->size<loco::DataType::S32>(dim); + for (int32_t i = 0; i < dim; ++i) + const_node->at<loco::DataType::S32>(i) = values[i]; + + return const_node; +} + +moco::TFConst *const_matrix(loco::Graph *graph, int32_t dimh, int32_t dimw) +{ + auto const_node = graph->nodes()->create<moco::TFConst>(); + + const_node->dtype(loco::DataType::S32); + const_node->rank(2); + const_node->dim(0).set(dimh); + const_node->dim(1).set(dimw); + + auto elements = dimh * dimw; + const_node->size<loco::DataType::S32>(elements); + for (int32_t i = 0; i < elements; ++i) + const_node->at<loco::DataType::S32>(i) = i; + + return const_node; +} + +} // namespace + +TEST(TFShapeInferenceRule, pack_scalar_2) +{ + moco::TFShapeInferenceRule shape_infer; + loco::Graph graph; + + auto pack_node = graph.nodes()->create<moco::TFPack>(2); + pack_node->axis(0); + { + auto const_node_0 = const_scalar(&graph, 1); + pack_node->values(0, const_node_0); + auto const_node_1 = const_scalar(&graph, 1); + pack_node->values(1, const_node_1); + } + setup_output_node(&graph, pack_node); + + bool cont = true; + while (cont) + { + cont = loco::apply(&shape_infer).to(&graph); + }; + + auto nodeshape = loco::shape_get(pack_node); + auto tshape = nodeshape.as<loco::TensorShape>(); + ASSERT_EQ(tshape.rank(), 1); + ASSERT_EQ(tshape.dim(0).value(), 2); +} + +TEST(TFShapeInferenceRule, pack_vector3_2) +{ + moco::TFShapeInferenceRule shape_infer; + loco::Graph graph; + + auto pack_node = graph.nodes()->create<moco::TFPack>(2); + pack_node->axis(0); + { + auto const_node_0 = const_vector(&graph, 3); + pack_node->values(0, const_node_0); + auto const_node_1 = const_vector(&graph, 3); + pack_node->values(1, const_node_1); + } + setup_output_node(&graph, pack_node); + + bool cont = true; + while (cont) + { + cont = loco::apply(&shape_infer).to(&graph); + }; + + auto nodeshape = loco::shape_get(pack_node); + auto tshape = nodeshape.as<loco::TensorShape>(); + + ASSERT_EQ(tshape.rank(), 2); + ASSERT_EQ(tshape.dim(0).value(), 2); + ASSERT_EQ(tshape.dim(1).value(), 3); +} + +TEST(TFShapeInferenceRule, pack_vector3_2_axis_1) +{ + moco::TFShapeInferenceRule shape_infer; + loco::Graph graph; + + auto pack_node = graph.nodes()->create<moco::TFPack>(2); + pack_node->axis(1); + { + auto const_node_0 = const_vector(&graph, 3); + pack_node->values(0, const_node_0); + auto const_node_1 = const_vector(&graph, 3); + pack_node->values(1, const_node_1); + } + setup_output_node(&graph, pack_node); + + bool cont = true; + while (cont) + { + cont = loco::apply(&shape_infer).to(&graph); + }; + + auto nodeshape = loco::shape_get(pack_node); + auto tshape = nodeshape.as<loco::TensorShape>(); + + ASSERT_EQ(tshape.rank(), 2); + ASSERT_EQ(tshape.dim(0).value(), 3); + ASSERT_EQ(tshape.dim(1).value(), 2); +} + +TEST(TFShapeInferenceRule, pack_vector3_2_axis_m2) +{ + moco::TFShapeInferenceRule shape_infer; + loco::Graph graph; + + auto pack_node = graph.nodes()->create<moco::TFPack>(2); + pack_node->axis(-2); + { + auto const_node_0 = const_vector(&graph, 3); + pack_node->values(0, const_node_0); + auto const_node_1 = const_vector(&graph, 3); + pack_node->values(1, const_node_1); + } + setup_output_node(&graph, pack_node); + + bool cont = true; + while (cont) + { + cont = loco::apply(&shape_infer).to(&graph); + }; + + auto nodeshape = loco::shape_get(pack_node); + auto tshape = nodeshape.as<loco::TensorShape>(); + + ASSERT_EQ(tshape.rank(), 2); + ASSERT_EQ(tshape.dim(0).value(), 2); + ASSERT_EQ(tshape.dim(1).value(), 3); +} + +TEST(TFShapeInferenceRule, pack_vector3_2_axis_m3) +{ + moco::TFShapeInferenceRule shape_infer; + loco::Graph graph; + + auto pack_node = graph.nodes()->create<moco::TFPack>(2); + pack_node->axis(-3); + { + auto const_node_0 = const_vector(&graph, 3); + pack_node->values(0, const_node_0); + auto const_node_1 = const_vector(&graph, 3); + pack_node->values(1, const_node_1); + } + setup_output_node(&graph, pack_node); + + // -3 is out of range and should throw + EXPECT_ANY_THROW(loco::apply(&shape_infer).to(&graph)); +} + +TEST(TFShapeInferenceRule, pack_matrix3x4_2_axis_1) +{ + moco::TFShapeInferenceRule shape_infer; + loco::Graph graph; + + auto pack_node = graph.nodes()->create<moco::TFPack>(2); + pack_node->axis(1); + { + auto const_node_0 = const_matrix(&graph, 3, 4); + pack_node->values(0, const_node_0); + auto const_node_1 = const_matrix(&graph, 3, 4); + pack_node->values(1, const_node_1); + } + setup_output_node(&graph, pack_node); + + bool cont = true; + while (cont) + { + cont = loco::apply(&shape_infer).to(&graph); + }; + + auto nodeshape = loco::shape_get(pack_node); + auto tshape = nodeshape.as<loco::TensorShape>(); + + ASSERT_EQ(tshape.rank(), 3); + ASSERT_EQ(tshape.dim(0).value(), 3); + ASSERT_EQ(tshape.dim(1).value(), 2); + ASSERT_EQ(tshape.dim(2).value(), 4); +} + +TEST(TFShapeInferenceRule, stridedslice_matrix5x5_shrink) +{ + moco::TFShapeInferenceRule shape_infer; + loco::Graph graph; + + auto sslice_node = graph.nodes()->create<moco::TFStridedSlice>(); + { + auto const_input = const_matrix(&graph, 5, 5); + sslice_node->input(const_input); + + auto const_begin = const_vector_init(&graph, {1, 1}); + sslice_node->begin(const_begin); + auto const_end = const_vector_init(&graph, {2, 4}); + sslice_node->end(const_end); + auto const_strides = const_vector_init(&graph, {1, 1}); + sslice_node->strides(const_strides); + + sslice_node->shrink_axis_mask(1); + } + setup_output_node(&graph, sslice_node); + + bool cont = true; + while (cont) + { + cont = loco::apply(&shape_infer).to(&graph); + }; + + auto nodeshape = loco::shape_get(sslice_node); + auto tshape = nodeshape.as<loco::TensorShape>(); + + ASSERT_EQ(tshape.rank(), 1); + ASSERT_EQ(tshape.dim(0).value(), 3); +} + +TEST(TFShapeInferenceRule, stridedslice_4_shrink) +{ + moco::TFShapeInferenceRule shape_infer; + loco::Graph graph; + + auto sslice_node = graph.nodes()->create<moco::TFStridedSlice>(); + { + auto const_input = const_vector(&graph, 4); + sslice_node->input(const_input); + + auto const_begin = const_vector_init(&graph, {0}); + sslice_node->begin(const_begin); + auto const_end = const_vector_init(&graph, {1}); + sslice_node->end(const_end); + auto const_strides = const_vector_init(&graph, {1}); + sslice_node->strides(const_strides); + + sslice_node->shrink_axis_mask(1); + } + setup_output_node(&graph, sslice_node); + + bool cont = true; + while (cont) + { + cont = loco::apply(&shape_infer).to(&graph); + }; + + auto nodeshape = loco::shape_get(sslice_node); + auto tshape = nodeshape.as<loco::TensorShape>(); + + ASSERT_EQ(tshape.rank(), 0); +} diff --git a/compiler/moco/service/src/Service/TFTypeInferenceRule.cpp b/compiler/moco/service/src/Service/TFTypeInferenceRule.cpp new file mode 100644 index 000000000..112ab955d --- /dev/null +++ b/compiler/moco/service/src/Service/TFTypeInferenceRule.cpp @@ -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. + */ + +#include "moco/Service/TFTypeInferenceRule.h" + +#include "moco/IR/TFDialect.h" +#include "moco/IR/TFNodeVisitor.h" +#include "moco/IR/TFNodes.h" + +#include "moco/IR/TFNodeImpl.h" + +#include <cassert> + +namespace +{ + +using namespace moco; + +struct TypeForwardAlgorithm final : public moco::TFNodeVisitor<loco::DataType> +{ + loco::DataType visit(const TFAdd *node) { return dtype_get(node->x()); } + loco::DataType visit(const TFAvgPool *node) { return dtype_get(node->value()); } + loco::DataType visit(const TFBiasAdd *node) { return dtype_get(node->value()); } + loco::DataType visit(const TFConcatV2 *node) { return dtype_get(node->values(0)); } + + loco::DataType visit(const TFConst *node) { return node->dtype(); } + + loco::DataType visit(const TFConv2D *node) { return dtype_get(node->input()); } + loco::DataType visit(const TFConv2DBackpropInput *node) + { + return dtype_get(node->out_backprop()); + } + loco::DataType visit(const TFDepthwiseConv2dNative *node) { return dtype_get(node->input()); } + loco::DataType visit(const TFFakeQuantWithMinMaxVars *node) { return dtype_get(node->inputs()); } + loco::DataType visit(const TFFusedBatchNorm *node) { return dtype_get(node->x()); } + loco::DataType visit(const TFIdentity *node) { return dtype_get(node->input()); } + loco::DataType visit(const TFMaximum *node) { return dtype_get(node->x()); } + loco::DataType visit(const TFMaxPool *node) { return dtype_get(node->input()); } + loco::DataType visit(const TFMean *node) { return dtype_get(node->input()); } + loco::DataType visit(const TFMul *node) { return dtype_get(node->x()); } + loco::DataType visit(const TFPack *node) { return dtype_get(node->values(0)); } + loco::DataType visit(const TFPad *node) { return dtype_get(node->input()); } + + loco::DataType visit(const TFPlaceholder *node) { return node->dtype(); } + + loco::DataType visit(const TFRealDiv *node) { return dtype_get(node->x()); } + loco::DataType visit(const TFRelu *node) { return dtype_get(node->features()); } + loco::DataType visit(const TFRelu6 *node) { return dtype_get(node->features()); } + loco::DataType visit(const TFReshape *node) { return dtype_get(node->tensor()); } + loco::DataType visit(const TFRsqrt *node) { return dtype_get(node->x()); } + + loco::DataType visit(const TFShape *node) { return node->dtype(); } + + loco::DataType visit(const TFSoftmax *node) { return dtype_get(node->logits()); } + loco::DataType visit(const TFSqrt *node) { return dtype_get(node->x()); } + loco::DataType visit(const TFSquaredDifference *node) { return dtype_get(node->x()); } + loco::DataType visit(const TFSqueeze *node) { return dtype_get(node->input()); } + loco::DataType visit(const TFStopGradient *node) { return dtype_get(node->input()); } + loco::DataType visit(const TFStridedSlice *node) { return dtype_get(node->input()); } + loco::DataType visit(const TFSub *node) { return dtype_get(node->x()); } + loco::DataType visit(const TFTanh *node) { return dtype_get(node->x()); } + + // For virtual nodes + loco::DataType visit(const TFPush *node) { return dtype_get(node->from()); } +}; + +} // namespace + +namespace moco +{ + +bool TFTypeInferenceRule::recognize(const loco::Dialect *d) const +{ + // This rule recognizes only "TFDialect" dialect! + return TFDialect::get() == d; +} + +bool TFTypeInferenceRule::infer(const loco::Node *node, loco::DataType &dtype) const +{ + assert(node->dialect() == TFDialect::get()); + + TypeForwardAlgorithm alg; + +// clang-format off +#define TENSORFLOW_NODE(OPCODE,CLASS) \ + if (dynamic_cast<const moco::CLASS *>(node)) \ + { \ + auto tfnode = dynamic_cast<const moco::CLASS *>(node); \ + dtype = tfnode->accept(&alg); \ + assert(dtype != loco::DataType::Unknown); \ + return true; \ + } +#include "moco/IR/TFNodes.lst" +#undef TENSORFLOW_NODE + // clang-format on + + return false; +} + +} // namespace moco diff --git a/compiler/moco/service/src/TestHelper.h b/compiler/moco/service/src/TestHelper.h new file mode 100644 index 000000000..8f3ff764e --- /dev/null +++ b/compiler/moco/service/src/TestHelper.h @@ -0,0 +1,69 @@ +/* + * 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> + +namespace moco +{ +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 moco + +#endif // __TEST_HELPER_H__ diff --git a/compiler/moco/service/src/TestHelper.test.cpp b/compiler/moco/service/src/TestHelper.test.cpp new file mode 100644 index 000000000..59915d60f --- /dev/null +++ b/compiler/moco/service/src/TestHelper.test.cpp @@ -0,0 +1,38 @@ +/* + * 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" + +namespace moco +{ +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 moco diff --git a/compiler/moco/support/CMakeLists.txt b/compiler/moco/support/CMakeLists.txt new file mode 100644 index 000000000..2a896d495 --- /dev/null +++ b/compiler/moco/support/CMakeLists.txt @@ -0,0 +1,9 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") + +add_library(moco_support SHARED ${SOURCES}) +target_include_directories(moco_support PRIVATE src) +target_include_directories(moco_support PUBLIC include) +target_link_libraries(moco_support PUBLIC loco) +target_link_libraries(moco_support PUBLIC moco_lang) +target_link_libraries(moco_support PRIVATE oops) +install(TARGETS moco_support DESTINATION lib) diff --git a/compiler/moco/support/README.md b/compiler/moco/support/README.md new file mode 100644 index 000000000..081f65d39 --- /dev/null +++ b/compiler/moco/support/README.md @@ -0,0 +1,3 @@ +# support + +_support_ privides _moco_ support libraries diff --git a/compiler/moco/support/include/moco/Support/NodeAs.h b/compiler/moco/support/include/moco/Support/NodeAs.h new file mode 100644 index 000000000..dc78ff94a --- /dev/null +++ b/compiler/moco/support/include/moco/Support/NodeAs.h @@ -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. + */ + +#ifndef __MOCO_SUPPORT_NODE_AS_H__ +#define __MOCO_SUPPORT_NODE_AS_H__ + +#include <loco.h> + +namespace moco +{ + +template <typename DERIVED> DERIVED *as(loco::Node *node) { return dynamic_cast<DERIVED *>(node); } + +} // namespace moco + +#endif // __MOCO_SUPPORT_NODE_AS_H__ diff --git a/compiler/moco/support/include/moco/Support/TFShapeInferenceHelper.h b/compiler/moco/support/include/moco/Support/TFShapeInferenceHelper.h new file mode 100644 index 000000000..52324700a --- /dev/null +++ b/compiler/moco/support/include/moco/Support/TFShapeInferenceHelper.h @@ -0,0 +1,221 @@ +/* + * 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_SUPPORT_SHAPE_INFERENCE_HELPER_H__ +#define __MOCO_SUPPORT_SHAPE_INFERENCE_HELPER_H__ + +#include <moco/IR/TFDataLayout.h> +#include <moco/IR/TFPadding.h> + +#include <loco.h> +#include <loco/IR/NodeShape.h> +#include <loco/IR/Padding2D.h> +#include <loco/IR/Stride.h> +#include <loco/IR/Window.h> + +#include <cassert> + +namespace moco +{ + +/** + * @note Helper for return broadcasted shape for binary operators having + * different shape for input x and y + */ +loco::TensorShape broadcast_shape(const loco::TensorShape &x, const loco::TensorShape &y); + +} // namespace moco + +namespace moco +{ + +/** + * @brief Return true if node has shape inference data for checking shape + * inference is done or not + * + * @note Will be deprecated in near future + */ +bool shape_inference_done(const loco::Node *node); + +/** + * @note While in shape inference, Node maybe Canonical, TF dialect or other dialects + * This will provide common loco::NodeShape as shape information + */ +loco::NodeShape node_shape(const loco::Node *node); +bool node_shape(const loco::Node *node, loco::NodeShape &nodeshape); + +loco::TensorShape as_tensor_shape(const loco::FeatureShape &feature_shape, + const TFDataLayout &data_layout); + +loco::FeatureShape as_feature_shape(const loco::NodeShape &nodeshape, + const TFDataLayout &data_layout); + +} // namespace moco + +namespace moco +{ + +struct PlaneShape +{ + loco::Dimension height; + loco::Dimension width; +}; + +class FeatureShapeUpdater final +{ +public: + FeatureShapeUpdater(loco::FeatureShape *ptr) : _feature_shape_ptr{ptr} + { + // DO NOTHING + } + +public: + void with(const PlaneShape &plane_shape) const + { + _feature_shape_ptr->height() = plane_shape.height; + _feature_shape_ptr->width() = plane_shape.width; + } + +private: + loco::FeatureShape *_feature_shape_ptr; +}; + +PlaneShape make_plane_shape(const loco::FeatureShape &feature_shape); + +FeatureShapeUpdater update(loco::FeatureShape &feature_shape); + +class PlaneInference +{ +protected: + struct Parameters + { + PlaneShape input; + PlaneShape stride; + PlaneShape window; + PlaneShape dilation; + PlaneShape effective_window; + PlaneShape output; + }; + + void fill(Parameters &p, const PlaneShape &in) + { + p.input.height = in.height; + p.input.width = in.width; + + p.stride.height = _stride.vertical(); + p.stride.width = _stride.horizontal(); + + p.window.height = _window.vertical(); + p.window.width = _window.horizontal(); + + // TODO support dilation + p.dilation.height = 1; + p.dilation.width = 1; + + p.effective_window.height = p.dilation.height.value() * (p.window.height.value() - 1) + 1; + p.effective_window.width = p.dilation.width.value() * (p.window.width.value() - 1) + 1; + } + + PlaneShape infer(const Parameters &p, const PlaneShape &) + { + PlaneShape res; + + if (_padding == "VALID") + { + res.height = + (p.input.height.value() + p.stride.height.value() - p.effective_window.height.value()) / + p.stride.height.value(); + res.width = + (p.input.width.value() + p.stride.width.value() - p.effective_window.width.value()) / + p.stride.width.value(); + } + else if (_padding == "SAME") + { + res.height = (p.input.height.value() + p.stride.height.value() - 1) / p.stride.height.value(); + res.width = (p.input.width.value() + p.stride.width.value() - 1) / p.stride.width.value(); + } + else + assert(false); + + return res; + } + +public: + PlaneShape operator()(const PlaneShape &in) + { + Parameters p; + + fill(p, in); + + return infer(p, in); + } + +public: + void padding(const TFPadding &value) { _padding = value; } + void window(const loco::Window<2> value) { _window = value; } + void stride(const loco::Stride<2> value) { _stride = value; } + +private: + TFPadding _padding; + loco::Window<2> _window; + loco::Stride<2> _stride; +}; + +class Padding2DInference final : public PlaneInference +{ +public: + loco::Padding2D operator()(const PlaneShape &in) + { + Parameters p; + + fill(p, in); + + auto output = infer(p, in); + + int64_t i_height = (int64_t)(output.height.value() - 1) * (int64_t)p.stride.height.value() + + (int64_t)p.effective_window.height.value() - (int64_t)p.input.height.value(); + int64_t i_width = (int64_t)(output.width.value() - 1) * (int64_t)p.stride.width.value() + + (int64_t)p.effective_window.width.value() - (int64_t)p.input.width.value(); + + uint32_t pad_height = i_height >= 0 ? (uint32_t)i_height : 0U; + uint32_t pad_width = i_width >= 0 ? (uint32_t)i_width : 0U; + + loco::Padding2D padding2d; + + padding2d.top(pad_height / 2); + padding2d.bottom(pad_height - padding2d.top()); + padding2d.left(pad_width / 2); + padding2d.right(pad_width - padding2d.left()); + + return padding2d; + } +}; + +} // namespace moco + +namespace moco +{ + +using TFStrides = std::vector<int64_t>; +using TFKSize = std::vector<int64_t>; + +loco::Stride<2> stride_of(const TFStrides &strides, const TFDataLayout &datalayout); +loco::Window<2> window_of(const TFKSize &ksize, const TFDataLayout &datalayout); +loco::Window<2> window_of(const loco::TensorShape &shape, const TFDataLayout &datalayout); + +} // namespace moco + +#endif // __MOCO_SERVICE_SHAPE_INFERENCE_HELPER_H__ diff --git a/compiler/moco/support/src/TFShapeInferenceHelper.cpp b/compiler/moco/support/src/TFShapeInferenceHelper.cpp new file mode 100644 index 000000000..13e514a78 --- /dev/null +++ b/compiler/moco/support/src/TFShapeInferenceHelper.cpp @@ -0,0 +1,354 @@ +/* + * 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/Support/TFShapeInferenceHelper.h" + +#include <loco/Service/ShapeInference.h> + +#include <oops/InternalExn.h> + +#include <cassert> + +namespace +{ + +// TODO Use codes in loco and remove duplicate broadcast_shape() and related +/** + * @brief Create a higher-rank TensorShape following NumPy broadcasting semantics + * + * HOW TO USE: + * + * auto expanded_tensor_shape = expand(tensor_shape).to(N); + */ +class TensorShapeExpander +{ +public: + TensorShapeExpander(const loco::TensorShape &shape) : _shape{shape} + { + // DO NOTHING + } + +public: + loco::TensorShape to(uint32_t output_rank) + { + auto const &input_shape = _shape; + uint32_t const input_rank = input_shape.rank(); + + assert(input_rank <= output_rank && "Cannot shrink rank"); + uint32_t const axis_shift = output_rank - input_rank; + + loco::TensorShape output_shape; + + output_shape.rank(output_rank); + for (uint32_t axis = 0; axis < output_rank; ++axis) + { + output_shape.dim(axis) = (axis < axis_shift) ? 1 : input_shape.dim(axis - axis_shift); + } + + return output_shape; + } + +private: + const loco::TensorShape _shape; +}; + +/** + * @breif Expand shape x and y to same rank by align right and filling with 1 + */ +void expand_rank(loco::TensorShape &x, loco::TensorShape &y) +{ + auto x_rank = x.rank(); + auto y_rank = y.rank(); + + if (x_rank == y_rank) + return; + + TensorShapeExpander x_exp(x); + TensorShapeExpander y_exp(y); + + auto xy_rank = std::max(x_rank, y_rank); + + x = x_rank > y_rank ? x : x_exp.to(xy_rank); + y = y_rank > x_rank ? y : y_exp.to(xy_rank); +} + +/** + * @breif Returns shape of expanded dimension of input x and y having same rank + */ +loco::TensorShape expand_dimension(const loco::TensorShape &x, const loco::TensorShape &y) +{ + assert(x.rank() == y.rank()); + + auto rank = x.rank(); + + loco::TensorShape output_shape; + + output_shape.rank(rank); + for (uint32_t axis = 0; axis < rank; ++axis) + { + assert(x.dim(axis).known() && y.dim(axis).known()); + + auto x_dim = x.dim(axis).value(); + auto y_dim = y.dim(axis).value(); + + // each dimension of x and y should be same or one must be 1 if different + if (!((x_dim == y_dim) || (x_dim == 1 || y_dim == 1))) + { + // TODO may need to refine message + INTERNAL_EXN("ShapeInference: Input shapes don't match"); + } + + output_shape.dim(axis) = std::max(x_dim, y_dim); + } + + return output_shape; +} + +} // namespace + +namespace moco +{ + +loco::TensorShape broadcast_shape(const loco::TensorShape &x, const loco::TensorShape &y) +{ + auto x_match = x; + auto y_match = y; + + expand_rank(x_match, y_match); + + auto output_shape = expand_dimension(x_match, y_match); + + return output_shape; +} + +} // namespace moco + +namespace moco +{ + +loco::NodeShape node_shape(const loco::Node *node) +{ + loco::NodeShape nodeshape; // default domain is Unknown + + if (loco::shape_known(node)) + { + nodeshape = loco::shape_get(node); + } + + return nodeshape; +} + +bool node_shape(const loco::Node *node, loco::NodeShape &nodeshape) +{ + nodeshape = node_shape(node); + return (nodeshape.domain() != loco::Domain::Unknown); +} + +loco::TensorShape as_tensor_shape(const loco::FeatureShape &feature_shape, + const TFDataLayout &data_layout) +{ + loco::TensorShape tensor_shape; + + tensor_shape.rank(4); + if (data_layout == "NHWC") + { + tensor_shape.dim(0) = feature_shape.count(); + tensor_shape.dim(1) = feature_shape.height(); + tensor_shape.dim(2) = feature_shape.width(); + tensor_shape.dim(3) = feature_shape.depth(); + } + else if (data_layout == "NCHW") + { + tensor_shape.dim(0) = feature_shape.count(); + tensor_shape.dim(1) = feature_shape.depth(); + tensor_shape.dim(2) = feature_shape.height(); + tensor_shape.dim(3) = feature_shape.width(); + } + else + { + // TODO support for other data_layout if needed + INTERNAL_EXN_V("ShapeInference: Unknown data_format", data_layout); + } + + return tensor_shape; +} + +loco::FeatureShape as_feature_shape(const loco::NodeShape &nodeshape, + const TFDataLayout &data_layout) +{ + if (nodeshape.domain() == loco::Domain::Feature) + return nodeshape.as<loco::FeatureShape>(); + + loco::FeatureShape feature_shape; + + // only convert from tensor to feature + if (nodeshape.domain() != loco::Domain::Tensor) + { + INTERNAL_EXN("ShapeInference: Invalid shape information"); + } + + loco::TensorShape tensor_shape = nodeshape.as<loco::TensorShape>(); + + if (tensor_shape.rank() != 4) + { + INTERNAL_EXN("ShapeInference: Rank is not 4"); + } + + if (data_layout == "NHWC") + { + feature_shape.count() = tensor_shape.dim(0); + feature_shape.height() = tensor_shape.dim(1); + feature_shape.width() = tensor_shape.dim(2); + feature_shape.depth() = tensor_shape.dim(3); + } + else if (data_layout == "NCHW") + { + feature_shape.count() = tensor_shape.dim(0); + feature_shape.depth() = tensor_shape.dim(1); + feature_shape.height() = tensor_shape.dim(2); + feature_shape.width() = tensor_shape.dim(3); + } + else + { + // TODO support for other data_layout if needed + INTERNAL_EXN_V("ShapeInference: Unknown data_format", data_layout); + } + + return feature_shape; +} + +} // namespace moco + +namespace moco +{ + +PlaneShape make_plane_shape(const loco::FeatureShape &feature_shape) +{ + PlaneShape plane_shape; + + plane_shape.height = feature_shape.height(); + plane_shape.width = feature_shape.width(); + + return plane_shape; +} + +FeatureShapeUpdater update(loco::FeatureShape &feature_shape) +{ + return FeatureShapeUpdater{&feature_shape}; +} + +} // namespace moco + +namespace +{ + +/** + * @brief Class to represent TensorFlow "data_format" attr. + */ +enum class DataLayout +{ + NHWC, + NCHW, +}; + +DataLayout as_data_layout(const std::string &tf_layout_str) +{ + if (tf_layout_str == "NHWC") + return DataLayout::NHWC; + else if (tf_layout_str == "NCHW") + return DataLayout::NCHW; + else + /// @note data layout tag in TensorFlow is 'data_format' + INTERNAL_EXN_V("ShapeInference: Unknown data_format", tf_layout_str); +} + +} // namespace + +namespace moco +{ + +loco::Stride<2> stride_of(const TFStrides &strides, const TFDataLayout &datalayout) +{ + loco::Stride<2> stride; + + auto data_layout = as_data_layout(datalayout); + if (data_layout == DataLayout::NHWC) + { + stride.vertical(strides[1]); + stride.horizontal(strides[2]); + } + else if (data_layout == DataLayout::NCHW) + { + stride.vertical(strides[2]); + stride.horizontal(strides[3]); + } + else + { + // TODO add more datalayout supports if needed + INTERNAL_EXN("ShapeInference: Unknown data_format"); + } + + return stride; +} + +loco::Window<2> window_of(const TFKSize &ksize, const TFDataLayout &datalayout) +{ + loco::Window<2> window; + + auto data_layout = as_data_layout(datalayout); + if (data_layout == DataLayout::NHWC) + { + window.vertical(ksize[1]); + window.horizontal(ksize[2]); + } + else if (data_layout == DataLayout::NCHW) + { + window.vertical(ksize[2]); + window.horizontal(ksize[3]); + } + else + { + // TODO add more datalayout supports if needed + INTERNAL_EXN("ShapeInference: Unknown data_format"); + } + + return window; +} + +loco::Window<2> window_of(const loco::TensorShape &shape, const TFDataLayout &datalayout) +{ + loco::Window<2> window; + + if (datalayout == "HWIO") + { + window.vertical(shape.dim(0).value()); + window.horizontal(shape.dim(1).value()); + } + else if (datalayout == "HWCM") + { + window.vertical(shape.dim(0).value()); + window.horizontal(shape.dim(1).value()); + } + else + { + // TODO add more datalayout supports if needed + INTERNAL_EXN_V("ShapeInference: Unknown data_format", datalayout); + } + + return window; +} + +} // namespace moco |