diff options
author | Chunseok Lee <chunseok.lee@samsung.com> | 2021-04-20 18:01:41 +0900 |
---|---|---|
committer | Chunseok Lee <chunseok.lee@samsung.com> | 2021-04-20 18:01:41 +0900 |
commit | 589bb1db6db6784efe21b3fbbfbfdb79aaa5f14e (patch) | |
tree | 47a2b23ce4220e3a4150c8b12ed941555272fb0c /compiler/luci/partition | |
parent | 62529acabbafce7730601ed01d5709d7bc0d378a (diff) | |
download | nnfw-589bb1db6db6784efe21b3fbbfbfdb79aaa5f14e.tar.gz nnfw-589bb1db6db6784efe21b3fbbfbfdb79aaa5f14e.tar.bz2 nnfw-589bb1db6db6784efe21b3fbbfbfdb79aaa5f14e.zip |
Imported Upstream version 1.15.0upstream/1.15.0submit/tizen/20210427.093759submit/tizen/20210423.055448submit/tizen/20210422.015846submit/tizen/20210421.062230accepted/tizen/unified/20210428.040443
Diffstat (limited to 'compiler/luci/partition')
43 files changed, 3023 insertions, 0 deletions
diff --git a/compiler/luci/partition/CMakeLists.txt b/compiler/luci/partition/CMakeLists.txt new file mode 100644 index 000000000..838642b6e --- /dev/null +++ b/compiler/luci/partition/CMakeLists.txt @@ -0,0 +1,29 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(luci_partition SHARED ${SOURCES}) +target_include_directories(luci_partition PRIVATE src) +target_include_directories(luci_partition PUBLIC include) +target_link_libraries(luci_partition PUBLIC luci_lang) +target_link_libraries(luci_partition PRIVATE luci_service) +target_link_libraries(luci_partition PRIVATE luci_log) +target_link_libraries(luci_partition PRIVATE luci_logex) +target_link_libraries(luci_partition PRIVATE mio_circle) +target_link_libraries(luci_partition PRIVATE nncc_common) +target_link_libraries(luci_partition PRIVATE oops) + +install(TARGETS luci_partition DESTINATION lib) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(luci_partition_test ${TESTS}) +target_include_directories(luci_partition_test PRIVATE src) +target_link_libraries(luci_partition_test luci_lang) +target_link_libraries(luci_partition_test luci_partition) +target_link_libraries(luci_partition_test luci_testhelper) +target_link_libraries(luci_partition_test luci_service) diff --git a/compiler/luci/partition/README.md b/compiler/luci/partition/README.md new file mode 100644 index 000000000..40a46bc56 --- /dev/null +++ b/compiler/luci/partition/README.md @@ -0,0 +1,4 @@ +# luci-partition + +`luci-partition` provides partition of a model to two or more sub models and +its connection configuration having same computational results. diff --git a/compiler/luci/partition/include/luci/Partition.h b/compiler/luci/partition/include/luci/Partition.h new file mode 100644 index 000000000..cf90e448b --- /dev/null +++ b/compiler/luci/partition/include/luci/Partition.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LUCI_PARTITION_H__ +#define __LUCI_PARTITION_H__ + +#include <luci/IR/Module.h> + +#include <memory> +#include <string> +#include <unordered_map> +#include <vector> + +namespace luci +{ + +/** + * @brief PartitionTable holds partition information + */ +struct PartitionTable +{ + std::vector<std::string> groups; + std::string default_group; + + // assign by opcode name: OPCODENAME=group + std::unordered_map<std::string /* OPCODENAME */, std::string /* group */> byopcodes; + + // TODO add assign by OP name +}; + +/** + * @brief PartedModule holds partitioned module and group name + */ +struct PartedModule +{ + std::unique_ptr<Module> module; + // group name used to partition this module + std::string group; + + // unique name(filename) of this module + std::string name; +}; + +struct PartedModules +{ + std::vector<PartedModule> pmodules; + + // TODO add connections ? +}; + +/** + * @brief Method to do paritioning from module and PartitionTable to produce PartedModules + */ +PartedModules apply(Module *module, const PartitionTable &partition); + +} // namespace luci + +#endif // __LUCI_PARTITION_H__ diff --git a/compiler/luci/partition/src/CircleOpCode.cpp b/compiler/luci/partition/src/CircleOpCode.cpp new file mode 100644 index 000000000..86694fa40 --- /dev/null +++ b/compiler/luci/partition/src/CircleOpCode.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CircleOpCode.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/IR/CircleNodeVisitor.h> + +#include <mio/circle/schema_generated.h> + +namespace +{ + +using namespace luci; +using namespace circle; + +class QueryOpCode final : public CircleNodeVisitor<BuiltinOperator> +{ +public: +// NOTE only circle operator may have BuiltinOperator_XXX +#define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) \ + BuiltinOperator visit(const CIRCLE_CLASS *) final { return BuiltinOperator_##OPCODE; } +#define CIRCLE_VNODE(OPCODE, CIRCLE_CLASS) + +#include "luci/IR/CircleNodes.lst" +#undef CIRCLE_VNODE +#undef CIRCLE_NODE + + // NOTE only builtin operators should be called (NOT virtual nodes) +}; + +class QueryCircleName final : public luci::CircleNodeVisitor<const char *> +{ +public: +// NOTE provide names for circle virtual nodes +#define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) +#define CIRCLE_VNODE(OPCODE, CIRCLE_CLASS) \ + const char *visit(const CIRCLE_CLASS *) final { return #OPCODE; } + +#include "luci/IR/CircleNodes.lst" +#undef CIRCLE_VNODE +#undef CIRCLE_NODE + + // default is null + const char *visit(const luci::CircleNode *) final { return nullptr; } +}; + +} // namespace + +namespace luci +{ + +std::string opcode_name(const CircleNode *node) +{ + QueryCircleName qcn; + auto cname = node->accept(&qcn); + if (cname != nullptr) + return std::string(cname); + + QueryOpCode qoc; + auto opcode = node->accept(&qoc); + auto name = circle::EnumNameBuiltinOperator(opcode); + return std::string(name); +} + +} // namespace luci diff --git a/compiler/luci/partition/src/CircleOpCode.h b/compiler/luci/partition/src/CircleOpCode.h new file mode 100644 index 000000000..d17b09261 --- /dev/null +++ b/compiler/luci/partition/src/CircleOpCode.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LUCI_PARTITION_CIRCLE_OP_CODE_H__ +#define __LUCI_PARTITION_CIRCLE_OP_CODE_H__ + +#include <luci/IR/CircleNode.h> + +#include <string> + +namespace luci +{ + +std::string opcode_name(const CircleNode *node); + +} // namespace luci + +#endif // __LUCI_PARTITION_CIRCLE_OP_CODE_H__ diff --git a/compiler/luci/partition/src/CircleOpCode.test.cpp b/compiler/luci/partition/src/CircleOpCode.test.cpp new file mode 100644 index 000000000..d2524a2ef --- /dev/null +++ b/compiler/luci/partition/src/CircleOpCode.test.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CircleOpCode.h" + +// NOTE any node will do for testing +#include <luci/IR/Nodes/CircleSqrt.h> + +#include <gtest/gtest.h> + +TEST(CircleOpCodeTest, name) +{ + auto g = loco::make_graph(); + auto node = g->nodes()->create<luci::CircleSqrt>(); + + auto name = luci::opcode_name(node); + ASSERT_EQ(name, "SQRT"); +} diff --git a/compiler/luci/partition/src/ConnectNode.cpp b/compiler/luci/partition/src/ConnectNode.cpp new file mode 100644 index 000000000..336be7c57 --- /dev/null +++ b/compiler/luci/partition/src/ConnectNode.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConnectNode.h" + +#include <oops/UserExn.h> + +namespace luci +{ + +void clone_connect(const luci::CircleNode *node, luci::CloneContext &clonecontext) +{ + ConnectNode cn(clonecontext); + node->accept(&cn); +} + +luci::CircleNode *ConnectNode::find_clone(const luci::CircleNode *node) +{ + auto it = _clonecontext.find(node); + if (it == _clonecontext.end()) + throw oops::UserExn("Invalid node in ConnectNode"); + return it->second; +} + +} // namespace luci diff --git a/compiler/luci/partition/src/ConnectNode.h b/compiler/luci/partition/src/ConnectNode.h new file mode 100644 index 000000000..017c587e5 --- /dev/null +++ b/compiler/luci/partition/src/ConnectNode.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LUCI_PARTITION_CONNECT_NODE_H__ +#define __LUCI_PARTITION_CONNECT_NODE_H__ + +#include <luci/IR/CircleNode.h> +#include <luci/IR/CircleNodeVisitor.h> + +namespace luci +{ + +/** + * @note MapNode2Clone is used as a map from original node to cloned node + * to find input of a cloned node + * + * (Original) (Clone) + * + * [A] [A'] + * | [B] | [B'] + * | | | | + * \ / \ / + * [C] [C'] + * + * From view of [C'] we need to find [A'] and [B']. We know [C] from [C'], + * then we can get from input of [C] as [A], [B] then [A]->[A'] and [B]->[B'] + * from the map. + */ +using MapNode2Clone = std::map<const CircleNode * /* ORG */, CircleNode * /* CLONE */>; + +struct CloneContext +{ + std::pair<MapNode2Clone::iterator, bool> emplace(const CircleNode *org, CircleNode *clone) + { + return node2clone.emplace(org, clone); + } + MapNode2Clone::iterator find(const CircleNode *org) { return node2clone.find(org); } + MapNode2Clone::iterator end(void) { return node2clone.end(); } + + MapNode2Clone node2clone; +}; + +class ConnectNode final : public luci::CircleNodeVisitor<void> +{ +public: + ConnectNode(luci::CloneContext &clonecontext) : _clonecontext(clonecontext){}; + +public: + // void visit(const luci::CircleAbs *) final; + void visit(const luci::CircleAdd *) final; + // void visit(const luci::CircleAddN *) final; + // void visit(const luci::CircleArgMax *) final; + // void visit(const luci::CircleArgMin *) final; + // void visit(const luci::CircleAveragePool2D *) final; + // void visit(const luci::CircleBatchMatMul *) final; + // void visit(const luci::CircleBatchToSpaceND *) final; + // void visit(const luci::CircleCast *) final; + // void visit(const luci::CircleCeil *) final; + // void visit(const luci::CircleConcatenation *) final; + void visit(const luci::CircleConst *) final; + // void visit(const luci::CircleConv2D *) final; + // void visit(const luci::CircleCos *) final; + // void visit(const luci::CircleCustom *) final; + // void visit(const luci::CircleDepthToSpace *) final; + // void visit(const luci::CircleDepthwiseConv2D *) final; + // void visit(const luci::CircleDequantize *) final; + void visit(const luci::CircleDiv *) final; + // void visit(const luci::CircleElu *) final; + // void visit(const luci::CircleEqual *) final; + // void visit(const luci::CircleExp *) final; + // void visit(const luci::CircleExpandDims *) final; + // void visit(const luci::CircleFakeQuant *) final; + // void visit(const luci::CircleFill *) final; + // void visit(const luci::CircleFloor *) final; + // void visit(const luci::CircleFloorDiv *) final; + // void visit(const luci::CircleFloorMod *) final; + // void visit(const luci::CircleFullyConnected *) final; + // void visit(const luci::CircleGather *) final; + // void visit(const luci::CircleGatherNd *) final; + // void visit(const luci::CircleGreater *) final; + // void visit(const luci::CircleGreaterEqual *) final; + // void visit(const luci::CircleIf *) final; + // void visit(const luci::CircleL2Normalize *) final; + // void visit(const luci::CircleL2Pool2D *) final; + // void visit(const luci::CircleLeakyRelu *) final; + // void visit(const luci::CircleLess *) final; + // void visit(const luci::CircleLessEqual *) final; + // void visit(const luci::CircleLocalResponseNormalization *) final; + // void visit(const luci::CircleLog *) final; + // void visit(const luci::CircleLogicalAnd *) final; + // void visit(const luci::CircleLogicalNot *) final; + // void visit(const luci::CircleLogicalOr *) final; + // void visit(const luci::CircleLogistic *) final; + // void visit(const luci::CircleLogSoftmax *) final; + // void visit(const luci::CircleMatrixDiag *) final; + // void visit(const luci::CircleMatrixSetDiag *) final; + // void visit(const luci::CircleMaximum *) final; + // void visit(const luci::CircleMaxPool2D *) final; + void visit(const luci::CircleMean *) final; + // void visit(const luci::CircleMinimum *) final; + // void visit(const luci::CircleMirrorPad *) final; + void visit(const luci::CircleMul *) final; + // void visit(const luci::CircleNeg *) final; + // void visit(const luci::CircleNonMaxSuppressionV4 *) final; + // void visit(const luci::CircleNonMaxSuppressionV5 *) final; + // void visit(const luci::CircleNotEqual *) final; + // void visit(const luci::CircleOneHot *) final; + // void visit(const luci::CirclePack *) final; + // void visit(const luci::CirclePad *) final; + // void visit(const luci::CirclePadV2 *) final; + void visit(const luci::CirclePow *) final; + // void visit(const luci::CirclePRelu *) final; + // void visit(const luci::CircleRange *) final; + // void visit(const luci::CircleRank *) final; + // void visit(const luci::CircleReduceAny *) final; + // void visit(const luci::CircleReduceMax *) final; + // void visit(const luci::CircleReduceMin *) final; + // void visit(const luci::CircleReduceProd *) final; + // void visit(const luci::CircleRelu *) final; + // void visit(const luci::CircleRelu6 *) final; + // void visit(const luci::CircleReluN1To1 *) final; + // void visit(const luci::CircleReshape *) final; + // void visit(const luci::CircleResizeBilinear *) final; + // void visit(const luci::CircleResizeNearestNeighbor *) final; + // void visit(const luci::CircleReverseSequence *) final; + // void visit(const luci::CircleReverseV2 *) final; + // void visit(const luci::CircleRound *) final; + void visit(const luci::CircleRsqrt *) final; + // void visit(const luci::CircleScatterNd *) final; + // void visit(const luci::CircleSegmentSum *) final; + // void visit(const luci::CircleSelect *) final; + // void visit(const luci::CircleSelectV2 *) final; + // void visit(const luci::CircleShape *) final; + // void visit(const luci::CircleSin *) final; + // void visit(const luci::CircleSlice *) final; + // void visit(const luci::CircleSoftmax *) final; + // void visit(const luci::CircleSpaceToBatchND *) final; + // void visit(const luci::CircleSpaceToDepth *) final; + // void visit(const luci::CircleSparseToDense *) final; + // void visit(const luci::CircleSplit *) final; + // void visit(const luci::CircleSplitV *) final; + void visit(const luci::CircleSqrt *) final; + // void visit(const luci::CircleSquare *) final; + void visit(const luci::CircleSquaredDifference *) final; + // void visit(const luci::CircleSqueeze *) final; + // void visit(const luci::CircleStridedSlice *) final; + void visit(const luci::CircleSub *) final; + // void visit(const luci::CircleSum *) final; + // void visit(const luci::CircleTanh *) final; + // void visit(const luci::CircleTile *) final; + // void visit(const luci::CircleTopKV2 *) final; + // void visit(const luci::CircleTranspose *) final; + // void visit(const luci::CircleTransposeConv *) final; + // void visit(const luci::CircleUnidirectionalSequenceLSTM *) final; + // void visit(const luci::CircleUnique *) final; + // void visit(const luci::CircleUnpack *) final; + // void visit(const luci::CircleWhere *) final; + // void visit(const luci::CircleWhile *) final; + // void visit(const luci::CircleZerosLike *) final; + + // Circle Only + // void visit(const luci::CircleBCQFullyConnected *) final; + // void visit(const luci::CircleBCQGather *) final; + // void visit(const luci::CircleInstanceNorm *) final; + + // Virtual + // void visit(const luci::CircleCustomOut *) final; + // void visit(const luci::CircleIfOut *) final; + // void visit(const luci::CircleInput *) final; + // void visit(const luci::CircleNonMaxSuppressionV4Out *) final; + // void visit(const luci::CircleNonMaxSuppressionV5Out *) final; + // void visit(const luci::CircleOutput *) final; + // void visit(const luci::CircleOutputDummy *) final; + // void visit(const luci::CircleOutputExclude *) final; + // void visit(const luci::CircleSplitOut *) final; + // void visit(const luci::CircleSplitVOut *) final; + // void visit(const luci::CircleTopKV2Out *) final; + // void visit(const luci::CircleUniqueOut *) final; + // void visit(const luci::CircleUnpackOut *) final; + // void visit(const luci::CircleWhileOut *) final; + +public: + luci::CircleNode *find_clone(const luci::CircleNode *node); + +protected: + luci::CloneContext &_clonecontext; +}; + +/** + * @brief Connect cloned node from input node + */ +void clone_connect(const luci::CircleNode *node, luci::CloneContext &clonecontext); + +} // namespace luci + +#endif // __LUCI_PARTITION_CONNECT_NODE_H__ diff --git a/compiler/luci/partition/src/ConnectNode.test.cpp b/compiler/luci/partition/src/ConnectNode.test.cpp new file mode 100644 index 000000000..a2009c654 --- /dev/null +++ b/compiler/luci/partition/src/ConnectNode.test.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConnectNode.test.h" + +// This file validates "ConnectNode.test.h". Please DO NOT remove this file. diff --git a/compiler/luci/partition/src/ConnectNode.test.h b/compiler/luci/partition/src/ConnectNode.test.h new file mode 100644 index 000000000..f7333ff99 --- /dev/null +++ b/compiler/luci/partition/src/ConnectNode.test.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONNECT_NODE_TEST_H__ +#define __CONNECT_NODE_TEST_H__ + +#include "ConnectNode.h" + +#include <luci/Service/CircleNodeClone.h> +#include <luci/test/TestIOGraph.h> + +#include <loco/IR/Graph.h> + +#include <initializer_list> +#include <memory> +#include <stdexcept> +#include <vector> + +namespace luci +{ +namespace test +{ + +template <unsigned N> class TestIsOGraph : public TestIsGraphlet<N>, public TestOGraphlet +{ +public: + TestIsOGraph() = default; + +public: + virtual void init(const std::initializer_list<ShapeU32> shape_in, const ShapeU32 shape_out) + { + if (shape_in.size() != N) + throw std::runtime_error("Failed to init TestIsOGraph"); + + TestIsGraphlet<N>::init(TestIsGraphlet<N>::g(), shape_in); + TestOGraphlet::init(TestIsGraphlet<N>::g(), shape_out); + } +}; + +template <class T> class NodeGraphletT +{ +public: + virtual void init(loco::Graph *g) + { + _node = g->nodes()->create<T>(); + _node->dtype(loco::DataType::S32); + _node->name("node"); + } + + T *node(void) const { return _node; } + +protected: + T *_node{nullptr}; +}; + +template <class T> class NodeIsGraphletT +{ +public: + virtual void init(loco::Graph *g, uint32_t n) + { + _node = g->nodes()->create<T>(n); + _node->dtype(loco::DataType::S32); + _node->name("node"); + } + + T *node(void) const { return _node; } + +protected: + T *_node{nullptr}; +}; + +/** + * @brief ConnectionTestHelper provides common framework for testing + * cloned CircleNode connection + */ +class ConnectionTestHelper +{ +public: + ConnectionTestHelper() { _graph_clone = loco::make_graph(); } + +public: + template <unsigned N> void prepare_inputs(TestIsOGraph<N> *isograph) + { + assert(N == isograph->num_inputs()); + + for (uint32_t i = 0; i < N; ++i) + { + auto *input = _graph_clone->nodes()->create<luci::CircleInput>(); + luci::copy_common_attributes(isograph->input(i), input); + _clonectx.emplace(isograph->input(i), input); + _inputs.push_back(input); + } + } + + /** + * @note prepare_inputs_miss is for negative testing + */ + template <unsigned N> void prepare_inputs_miss(TestIsOGraph<N> *isograph) + { + assert(N == isograph->num_inputs()); + + for (uint32_t i = 0; i < N; ++i) + { + auto *input = _graph_clone->nodes()->create<luci::CircleInput>(); + luci::copy_common_attributes(isograph->input(i), input); + if (i != 0) + _clonectx.emplace(isograph->input(i), input); + _inputs.push_back(input); + } + } + + void clone_connect(luci::CircleNode *node, luci::CircleNode *clone) + { + _clonectx.emplace(node, clone); + + luci::clone_connect(node, _clonectx); + } + +public: + loco::Graph *graph_clone(void) { return _graph_clone.get(); } + + luci::CircleNode *inputs(uint32_t idx) { return _inputs.at(idx); } + +protected: + luci::CloneContext _clonectx; + std::vector<luci::CircleInput *> _inputs; + std::unique_ptr<loco::Graph> _graph_clone; // graph for clones +}; + +} // namespace test +} // namespace luci + +#endif // __CONNECT_NODE_TEST_H__ diff --git a/compiler/luci/partition/src/Nodes/CircleAdd.cpp b/compiler/luci/partition/src/Nodes/CircleAdd.cpp new file mode 100644 index 000000000..d393997e9 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleAdd.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConnectNode.h" + +namespace +{ + +void connect(luci::ConnectNode *cn, const luci::CircleAdd *node) +{ + auto *cloned = loco::must_cast<luci::CircleAdd *>(cn->find_clone(node)); + + luci::CircleNode *x = loco::must_cast<luci::CircleNode *>(node->x()); + luci::CircleNode *y = loco::must_cast<luci::CircleNode *>(node->y()); + + cloned->x(cn->find_clone(x)); + cloned->y(cn->find_clone(y)); +} + +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CircleAdd *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleAdd.test.cpp b/compiler/luci/partition/src/Nodes/CircleAdd.test.cpp new file mode 100644 index 000000000..e457b83d2 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleAdd.test.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConnectNode.h" + +#include "ConnectNode.test.h" + +#include <luci/Service/CircleNodeClone.h> + +#include <gtest/gtest.h> + +namespace +{ + +using namespace luci::test; + +class NodeGraphlet : public NodeGraphletT<luci::CircleAdd> +{ +public: + NodeGraphlet() = default; + +public: + void init(loco::Graph *g) override + { + NodeGraphletT<luci::CircleAdd>::init(g); + + _node->fusedActivationFunction(luci::FusedActFunc::RELU); + } +}; + +class TestNodeGraph : public TestIsOGraph<2>, public NodeGraphlet +{ +public: + TestNodeGraph() = default; + +public: + void init(const ShapeU32 shape) + { + TestIsOGraph<2>::init({shape, shape}, shape); + NodeGraphlet::init(g()); + + node()->x(input(0)); + node()->y(input(1)); + + output()->from(node()); + } +}; + +} // namespace + +TEST(ConnectNodeTest, connect_Add) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast<luci::CircleAdd *>(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast<luci::CircleAdd *>(clone)); + + cth.clone_connect(node, clone); + + ASSERT_EQ(2, clone->arity()); + ASSERT_EQ(cth.inputs(0), clone->arg(0)); + ASSERT_EQ(cth.inputs(1), clone->arg(1)); +} + +TEST(ConnectNodeTest, connect_Add_NEG) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs_miss(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast<luci::CircleAdd *>(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast<luci::CircleAdd *>(clone)); + + EXPECT_ANY_THROW(cth.clone_connect(node, clone)); +} diff --git a/compiler/luci/partition/src/Nodes/CircleConst.cpp b/compiler/luci/partition/src/Nodes/CircleConst.cpp new file mode 100644 index 000000000..118cd8de2 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleConst.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConnectNode.h" + +namespace luci +{ + +void ConnectNode::visit(const luci::CircleConst *) +{ + // Nothing to do +} + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleDiv.cpp b/compiler/luci/partition/src/Nodes/CircleDiv.cpp new file mode 100644 index 000000000..480338542 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleDiv.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConnectNode.h" + +namespace +{ + +void connect(luci::ConnectNode *cn, const luci::CircleDiv *node) +{ + auto *cloned = loco::must_cast<luci::CircleDiv *>(cn->find_clone(node)); + + luci::CircleNode *x = loco::must_cast<luci::CircleNode *>(node->x()); + luci::CircleNode *y = loco::must_cast<luci::CircleNode *>(node->y()); + + cloned->x(cn->find_clone(x)); + cloned->y(cn->find_clone(y)); +} + +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CircleDiv *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleDiv.test.cpp b/compiler/luci/partition/src/Nodes/CircleDiv.test.cpp new file mode 100644 index 000000000..226932337 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleDiv.test.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConnectNode.h" + +#include "ConnectNode.test.h" + +#include <luci/Service/CircleNodeClone.h> + +#include <gtest/gtest.h> + +namespace +{ + +using namespace luci::test; + +class NodeGraphlet : public NodeGraphletT<luci::CircleDiv> +{ +public: + NodeGraphlet() = default; + +public: + void init(loco::Graph *g) override + { + NodeGraphletT<luci::CircleDiv>::init(g); + + _node->fusedActivationFunction(luci::FusedActFunc::RELU); + } +}; + +class TestNodeGraph : public TestIsOGraph<2>, public NodeGraphlet +{ +public: + TestNodeGraph() = default; + +public: + void init(const ShapeU32 shape) + { + TestIsOGraph<2>::init({shape, shape}, shape); + NodeGraphlet::init(g()); + + node()->x(input(0)); + node()->y(input(1)); + + output()->from(node()); + } +}; + +} // namespace + +TEST(ConnectNodeTest, connect_Div) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast<luci::CircleDiv *>(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast<luci::CircleDiv *>(clone)); + + cth.clone_connect(node, clone); + + ASSERT_EQ(2, clone->arity()); + ASSERT_EQ(cth.inputs(0), clone->arg(0)); + ASSERT_EQ(cth.inputs(1), clone->arg(1)); +} + +TEST(ConnectNodeTest, connect_Div_NEG) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs_miss(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast<luci::CircleDiv *>(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast<luci::CircleDiv *>(clone)); + + EXPECT_ANY_THROW(cth.clone_connect(node, clone)); +} diff --git a/compiler/luci/partition/src/Nodes/CircleMean.cpp b/compiler/luci/partition/src/Nodes/CircleMean.cpp new file mode 100644 index 000000000..b634e5838 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleMean.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConnectNode.h" + +namespace +{ + +void connect(luci::ConnectNode *cn, const luci::CircleMean *node) +{ + auto *cloned = loco::must_cast<luci::CircleMean *>(cn->find_clone(node)); + + luci::CircleNode *input = loco::must_cast<luci::CircleNode *>(node->input()); + luci::CircleNode *reduction_indices = + loco::must_cast<luci::CircleNode *>(node->reduction_indices()); + + cloned->input(cn->find_clone(input)); + cloned->reduction_indices(cn->find_clone(reduction_indices)); +} + +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CircleMean *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleMul.cpp b/compiler/luci/partition/src/Nodes/CircleMul.cpp new file mode 100644 index 000000000..2cd2b4038 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleMul.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConnectNode.h" + +namespace +{ + +void connect(luci::ConnectNode *cn, const luci::CircleMul *node) +{ + auto *cloned = loco::must_cast<luci::CircleMul *>(cn->find_clone(node)); + + luci::CircleNode *x = loco::must_cast<luci::CircleNode *>(node->x()); + luci::CircleNode *y = loco::must_cast<luci::CircleNode *>(node->y()); + + cloned->x(cn->find_clone(x)); + cloned->y(cn->find_clone(y)); +} + +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CircleMul *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleMul.test.cpp b/compiler/luci/partition/src/Nodes/CircleMul.test.cpp new file mode 100644 index 000000000..99cf0824d --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleMul.test.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConnectNode.h" + +#include "ConnectNode.test.h" + +#include <luci/Service/CircleNodeClone.h> + +#include <gtest/gtest.h> + +namespace +{ + +using namespace luci::test; + +class NodeGraphlet : public NodeGraphletT<luci::CircleMul> +{ +public: + NodeGraphlet() = default; + +public: + void init(loco::Graph *g) + { + NodeGraphletT<luci::CircleMul>::init(g); + + _node->fusedActivationFunction(luci::FusedActFunc::RELU); + } +}; + +class TestNodeGraph : public TestIsOGraph<2>, public NodeGraphlet +{ +public: + TestNodeGraph() = default; + +public: + void init(const ShapeU32 shape) + { + TestIsOGraph<2>::init({shape, shape}, shape); + NodeGraphlet::init(g()); + + node()->x(input(0)); + node()->y(input(1)); + + output()->from(node()); + } +}; + +} // namespace + +TEST(ConnectNodeTest, connect_Mul) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast<luci::CircleMul *>(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast<luci::CircleMul *>(clone)); + + cth.clone_connect(node, clone); + + ASSERT_EQ(2, clone->arity()); + ASSERT_EQ(cth.inputs(0), clone->arg(0)); + ASSERT_EQ(cth.inputs(1), clone->arg(1)); +} + +TEST(ConnectNodeTest, connect_Mul_NEG) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs_miss(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast<luci::CircleMul *>(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast<luci::CircleMul *>(clone)); + + EXPECT_ANY_THROW(cth.clone_connect(node, clone)); +} diff --git a/compiler/luci/partition/src/Nodes/CirclePow.cpp b/compiler/luci/partition/src/Nodes/CirclePow.cpp new file mode 100644 index 000000000..fb180ee69 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CirclePow.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConnectNode.h" + +namespace +{ + +void connect(luci::ConnectNode *cn, const luci::CirclePow *node) +{ + auto *cloned = loco::must_cast<luci::CirclePow *>(cn->find_clone(node)); + + luci::CircleNode *x = loco::must_cast<luci::CircleNode *>(node->x()); + luci::CircleNode *y = loco::must_cast<luci::CircleNode *>(node->y()); + + cloned->x(cn->find_clone(x)); + cloned->y(cn->find_clone(y)); +} + +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CirclePow *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleRsqrt.cpp b/compiler/luci/partition/src/Nodes/CircleRsqrt.cpp new file mode 100644 index 000000000..03e64aad0 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleRsqrt.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConnectNode.h" + +namespace +{ + +void connect(luci::ConnectNode *cn, const luci::CircleRsqrt *node) +{ + auto *cloned = loco::must_cast<luci::CircleRsqrt *>(cn->find_clone(node)); + + luci::CircleNode *x = loco::must_cast<luci::CircleNode *>(node->x()); + + cloned->x(cn->find_clone(x)); +} + +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CircleRsqrt *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleSqrt.cpp b/compiler/luci/partition/src/Nodes/CircleSqrt.cpp new file mode 100644 index 000000000..f737aac8d --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleSqrt.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConnectNode.h" + +namespace +{ + +void connect(luci::ConnectNode *cn, const luci::CircleSqrt *node) +{ + auto *cloned = loco::must_cast<luci::CircleSqrt *>(cn->find_clone(node)); + + luci::CircleNode *x = loco::must_cast<luci::CircleNode *>(node->x()); + + cloned->x(cn->find_clone(x)); +} + +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CircleSqrt *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleSquaredDifference.cpp b/compiler/luci/partition/src/Nodes/CircleSquaredDifference.cpp new file mode 100644 index 000000000..40dd31706 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleSquaredDifference.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConnectNode.h" + +namespace +{ + +void connect(luci::ConnectNode *cn, const luci::CircleSquaredDifference *node) +{ + auto *cloned = loco::must_cast<luci::CircleSquaredDifference *>(cn->find_clone(node)); + + luci::CircleNode *x = loco::must_cast<luci::CircleNode *>(node->x()); + luci::CircleNode *y = loco::must_cast<luci::CircleNode *>(node->y()); + + cloned->x(cn->find_clone(x)); + cloned->y(cn->find_clone(y)); +} + +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CircleSquaredDifference *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleSub.cpp b/compiler/luci/partition/src/Nodes/CircleSub.cpp new file mode 100644 index 000000000..8ac294b7b --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleSub.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConnectNode.h" + +namespace +{ + +void connect(luci::ConnectNode *cn, const luci::CircleSub *node) +{ + auto *cloned = loco::must_cast<luci::CircleSub *>(cn->find_clone(node)); + + luci::CircleNode *x = loco::must_cast<luci::CircleNode *>(node->x()); + luci::CircleNode *y = loco::must_cast<luci::CircleNode *>(node->y()); + + cloned->x(cn->find_clone(x)); + cloned->y(cn->find_clone(y)); +} + +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CircleSub *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleSub.test.cpp b/compiler/luci/partition/src/Nodes/CircleSub.test.cpp new file mode 100644 index 000000000..7c0d83745 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleSub.test.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ConnectNode.h" + +#include "ConnectNode.test.h" + +#include <luci/Service/CircleNodeClone.h> + +#include <gtest/gtest.h> + +namespace +{ + +using namespace luci::test; + +class NodeGraphlet : public NodeGraphletT<luci::CircleSub> +{ +public: + NodeGraphlet() = default; + +public: + void init(loco::Graph *g) + { + NodeGraphletT<luci::CircleSub>::init(g); + + _node->fusedActivationFunction(luci::FusedActFunc::RELU); + } +}; + +class TestNodeGraph : public TestIsOGraph<2>, public NodeGraphlet +{ +public: + TestNodeGraph() = default; + +public: + void init(const ShapeU32 shape) + { + TestIsOGraph<2>::init({shape, shape}, shape); + NodeGraphlet::init(g()); + + node()->x(input(0)); + node()->y(input(1)); + + output()->from(node()); + } +}; + +} // namespace + +TEST(ConnectNodeTest, connect_Sub) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast<luci::CircleSub *>(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast<luci::CircleSub *>(clone)); + + cth.clone_connect(node, clone); + + ASSERT_EQ(2, clone->arity()); + ASSERT_EQ(cth.inputs(0), clone->arg(0)); + ASSERT_EQ(cth.inputs(1), clone->arg(1)); +} + +TEST(ConnectNodeTest, connect_Sub_NEG) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs_miss(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast<luci::CircleSub *>(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast<luci::CircleSub *>(clone)); + + EXPECT_ANY_THROW(cth.clone_connect(node, clone)); +} diff --git a/compiler/luci/partition/src/Partition.cpp b/compiler/luci/partition/src/Partition.cpp new file mode 100644 index 000000000..cc7106ca9 --- /dev/null +++ b/compiler/luci/partition/src/Partition.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PartitionIR.h" +#include "PartitionIRDump.h" +#include "PartitionPGroups.h" +#include "PartitionMerge.h" +#include "PartitionCleanup.h" +#include "PartitionPModules.h" +#include "PartitionPModulesDump.h" + +#include "luci/Partition.h" +#include "luci/Log.h" + +#include <cassert> + +namespace luci +{ + +/** + * @brief This will return Partitioned Modules object + */ +PartedModules apply(Module *source, const PartitionTable &partition) +{ + assert(source != nullptr); + + LOGGER(l); + + auto pgroups = produce_pgroups(source, partition); + INFO(l) << "--- Partition Graph (1)------------------------"; + INFO(l) << pgroups.get(); + + auto mpgroups = merge_pgroups(pgroups.get()); + INFO(l) << "--- Partition Graph (2)------------------------"; + INFO(l) << mpgroups.get(); + + remove_unused_inputoutputs(mpgroups.get(), source); + INFO(l) << "--- Partition Graph (3)------------------------"; + INFO(l) << mpgroups.get(); + + auto pmodules = produce_pmodules(mpgroups.get()); + INFO(l) << "--- Modules -----------------------------------"; + INFO(l) << &pmodules; + + return pmodules; +} + +} // namespace luci diff --git a/compiler/luci/partition/src/Partition.test.cpp b/compiler/luci/partition/src/Partition.test.cpp new file mode 100644 index 000000000..9e24c441c --- /dev/null +++ b/compiler/luci/partition/src/Partition.test.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "luci/Partition.h" + +#include <luci/test/TestIOGraph.h> + +#include <luci/IR/Nodes/CircleSqrt.h> + +#include <gtest/gtest.h> + +namespace +{ + +using namespace luci::test; + +class SqrtGraphlet +{ +public: + SqrtGraphlet() = default; + +public: + void init(loco::Graph *g, const ShapeU32 input_shape) + { + _sqrt = g->nodes()->create<luci::CircleSqrt>(); + _sqrt->dtype(loco::DataType::S32); + _sqrt->name("sqrt"); + } + +protected: + luci::CircleSqrt *_sqrt = nullptr; +}; + +class SqrtGraph : public TestIOGraph, public SqrtGraphlet +{ +public: + SqrtGraph() = default; + +public: + void init(const ShapeU32 shape) + { + TestIOGraph::init(shape, shape); + SqrtGraphlet::init(g(), shape); + + _sqrt->x(input()); + + output()->from(_sqrt); + } +}; + +} // namespace + +TEST(PartitionTest, simple_apply) +{ + luci::Module module; + + SqrtGraph g; + g.init({3, 3}); + g.transfer_to(&module); + + luci::PartitionTable pt; + pt.default_group = "A"; + + auto pms = apply(&module, pt); + + ASSERT_EQ(1, pms.pmodules.size()); + + auto &pm = *pms.pmodules.begin(); + ASSERT_NE(nullptr, pm.module->graph()); +} diff --git a/compiler/luci/partition/src/PartitionCleanup.cpp b/compiler/luci/partition/src/PartitionCleanup.cpp new file mode 100644 index 000000000..6545295df --- /dev/null +++ b/compiler/luci/partition/src/PartitionCleanup.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PartitionCleanup.h" + +#include "luci/Log.h" + +namespace +{ + +using CircleNodes = std::vector<luci::CircleNode *>; + +/** + * @note Original source outputs should be outputs + */ +void gather_graph_outputs(CircleNodes &nodes, const luci::Module *source) +{ + // graph outputs are treated as used + auto graph = source->graph(); + for (uint32_t n = 0; n < graph->outputs()->size(); ++n) + { + auto output = luci::output_node(graph, n); // output is CircleOutput + assert(output != nullptr); + + auto node = loco::must_cast<luci::CircleNode *>(output->from()); + + nodes.push_back(node); + } + + // TODO add unused virtual outputs +} + +/** + * @note If one PGroup requires an input, that input should be an output + * from another PGroup + */ +void gather_pgroups_outputs(CircleNodes &nodes, const luci::PGroups *pgroups) +{ + // input of a pgroup is used output + for (auto &pgroup : pgroups->pgroups) + { + for (auto input : pgroup->inputs) + { + nodes.push_back(input); + } + } +} + +} // namespace + +namespace luci +{ + +void remove_unused_inputoutputs(luci::PGroups *pgroups, const luci::Module *source) +{ + assert(source != nullptr); + assert(pgroups != nullptr); + + LOGGER(l); + + // TODO support multiple subgraph + assert(source->size() == 1); + + INFO(l) << "--- Cleanup unused inputs/outputs"; + + // remove input within same pgroup + for (auto &pgroup : pgroups->pgroups) + { + bool changed; + do + { + changed = false; + for (auto it = pgroup->inputs.begin(); it != pgroup->inputs.end(); ++it) + { + auto input = *it; + if (pgroups->pgroup_of(input) == pgroup.get()) + { + INFO(l) << " Cleanup input " << input->name() << " from group " << pgroup->group; + pgroup->inputs.erase(it); + changed = true; + break; + } + // NOTE CircleConst is one of input type, as they are registered as + // input to some node and then (should be) merged. + // Remove if this input is CircleConst + if (dynamic_cast<CircleConst *>(input) != nullptr) + { + INFO(l) << " Cleanup CircleConst " << input->name() << " from group " << pgroup->group; + pgroup->inputs.erase(it); + changed = true; + break; + } + } + } while (changed); + } + + // remove unused output(s) + // 'used_outputs' will hold actual used outputs for all PGroups + CircleNodes used_outputs; + + gather_graph_outputs(used_outputs, source); + gather_pgroups_outputs(used_outputs, pgroups); + + for (auto &pgroup : pgroups->pgroups) + { + bool changed; + do + { + changed = false; + for (auto it = pgroup->outputs.begin(); it != pgroup->outputs.end(); ++it) + { + auto output = *it; + auto oit = std::find(used_outputs.begin(), used_outputs.end(), output); + if (oit == used_outputs.end()) + { + INFO(l) << " Cleanup output " << output->name() << " from group " << pgroup->group; + pgroup->outputs.erase(it); + changed = true; + break; + } + } + } while (changed); + } +} + +} // namespace luci diff --git a/compiler/luci/partition/src/PartitionCleanup.h b/compiler/luci/partition/src/PartitionCleanup.h new file mode 100644 index 000000000..f81b4a7cb --- /dev/null +++ b/compiler/luci/partition/src/PartitionCleanup.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LUCI_PARTITON_CLEANUP_H__ +#define __LUCI_PARTITON_CLEANUP_H__ + +#include "PartitionIR.h" + +#include <luci/IR/Module.h> + +namespace luci +{ + +/** + * @brief This will remove unused inputs/outputs in each pgroup of pgroups + */ +void remove_unused_inputoutputs(luci::PGroups *, const luci::Module *); + +} // namespace luci + +#endif // __LUCI_PARTITON_CLEANUP_H__ diff --git a/compiler/luci/partition/src/PartitionIR.cpp b/compiler/luci/partition/src/PartitionIR.cpp new file mode 100644 index 000000000..ebd6b25fa --- /dev/null +++ b/compiler/luci/partition/src/PartitionIR.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PartitionIR.h" +#include "CircleOpCode.h" + +#include "luci/Log.h" + +#include <cassert> +#include <ostream> +#include <iostream> + +namespace luci +{ + +std::unique_ptr<PGroups> PGroups::make_copy(void) const +{ + auto d_pgroups = std::make_unique<luci::PGroups>(); + + for (auto &s_pgroup : pgroups) + { + // make a copy of s_pgroup to d_pgroup + std::unique_ptr<luci::PGroup> d_pgroup = std::make_unique<luci::PGroup>(); + + d_pgroup->group = s_pgroup->group; + d_pgroup->id = s_pgroup->id; + + for (auto &pnode : s_pgroup->pnodes) + { + auto pnodec = std::make_unique<luci::PNode>(); + pnodec->node = pnode->node; + pnodec->group = pnode->group; + pnodec->pgroup = d_pgroup.get(); + d_pgroup->pnodes.push_back(std::move(pnodec)); + } + + for (auto &input : s_pgroup->inputs) + d_pgroup->inputs.push_back(input); + + for (auto &output : s_pgroup->outputs) + d_pgroup->outputs.push_back(output); + + // copy node2group + for (auto it = node2group.begin(); it != node2group.end(); ++it) + d_pgroups->node2group[it->first] = it->second; + + // build id2pgroup + d_pgroups->id2pgroup[d_pgroup->id] = d_pgroup.get(); + + d_pgroups->pgroups.push_back(std::move(d_pgroup)); + // note: d_pgroup is now nullptr as it's moved + } + + return std::move(d_pgroups); +} + +std::string PGroups::group_of(luci::CircleNode *node) const +{ + assert(node != nullptr); + + LOGGER(l); + + auto it = node2group.find(node); + if (it == node2group.end()) + { + INFO(l) << "PGroups::group_of " << node << "(" << node->name() << ") not found" << std::endl; + return ""; + } + return it->second; +} + +const PGroup *PGroups::pgroup_of(luci::CircleNode *node) const +{ + assert(node != nullptr); + + for (auto &pgroup : pgroups) + { + for (auto &pnode : pgroup->pnodes) + { + if (node == pnode->node) + return pgroup.get(); + } + } + // node maybe graph input (CircleInput) + return nullptr; +} + +} // namespace luci diff --git a/compiler/luci/partition/src/PartitionIR.h b/compiler/luci/partition/src/PartitionIR.h new file mode 100644 index 000000000..852e38cc0 --- /dev/null +++ b/compiler/luci/partition/src/PartitionIR.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LUCI_PARTITION_IR_H__ +#define __LUCI_PARTITION_IR_H__ + +#include <luci/IR/CircleNodes.h> + +#include <map> +#include <memory> +#include <string> +#include <vector> + +namespace luci +{ + +struct PGroup; + +/** + * @brief Partition Node with CircleNode with group name + * @note node just points to source luci::CircleNode, NOT the cloned node + * CloneContext is used to find cloned node from source node + */ +struct PNode +{ + const luci::CircleNode *node = nullptr; + std::string group; + + const PGroup *pgroup = nullptr; +}; + +/** + * @brief Partition Group with Partition Nodes of same group and I/Os nodes + */ +struct PGroup +{ + std::vector<std::unique_ptr<PNode>> pnodes; + std::string group; + uint32_t id = 0; + + // I/O while partitioning + std::vector<luci::CircleNode *> inputs; + std::vector<luci::CircleNode *> outputs; +}; + +struct PGroups +{ + std::vector<std::unique_ptr<PGroup>> pgroups; + + // node2group is to find group key from source node + std::map<const luci::CircleNode *, std::string> node2group; + + // id2pngroup is to find *pngroup from pngroup id + std::map<uint32_t, PGroup *> id2pgroup; + + // default group key for reference + std::string default_group; + +public: + /** + * @brief return a copy of PGroups + */ + std::unique_ptr<PGroups> make_copy(void) const; + + /** + * @brief return group key of node, empty string if not found + */ + std::string group_of(luci::CircleNode *node) const; + + /** + * @brief return holding pgroup of node, nullptr if not found + */ + const PGroup *pgroup_of(luci::CircleNode *node) const; +}; + +} // namespace luci + +#endif // __LUCI_PARTITION_IR_H__ diff --git a/compiler/luci/partition/src/PartitionIR.test.cpp b/compiler/luci/partition/src/PartitionIR.test.cpp new file mode 100644 index 000000000..4c051a96d --- /dev/null +++ b/compiler/luci/partition/src/PartitionIR.test.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PartitionIR.h" + +// NOTE any node will do for testing +#include <luci/IR/Nodes/CircleAdd.h> + +#include <gtest/gtest.h> + +#include <memory> + +TEST(PartitionIRTest, PNode_ctor) +{ + auto g = loco::make_graph(); + auto node = g->nodes()->create<luci::CircleAdd>(); + + luci::PNode pnode; + pnode.node = node; + + ASSERT_NE(nullptr, pnode.node); + ASSERT_EQ(nullptr, pnode.pgroup); +} + +// TODO add more tests with luci::PNode + +TEST(PartitionIRTest, PGroup_ctor) +{ + auto g = loco::make_graph(); + auto node = g->nodes()->create<luci::CircleAdd>(); + + luci::PGroup pgroup; + auto pnode = std::make_unique<luci::PNode>(); + pnode->node = node; + + pgroup.pnodes.push_back(std::move(pnode)); + + ASSERT_NE(pgroup.pnodes.end(), pgroup.pnodes.begin()); + ASSERT_EQ(0, pgroup.inputs.size()); + ASSERT_EQ(0, pgroup.outputs.size()); +} + +// TODO add more tests with luci::PGroup + +TEST(PartitionIRTest, PGroups_ctor) +{ + auto g = loco::make_graph(); + auto node = g->nodes()->create<luci::CircleAdd>(); + + auto pnode = std::make_unique<luci::PNode>(); + pnode->node = node; + + auto pgroup = std::make_unique<luci::PGroup>(); + pgroup->pnodes.push_back(std::move(pnode)); + + luci::PGroups pgroups; + pgroups.pgroups.push_back(std::move(pgroup)); + + ASSERT_NE(pgroups.pgroups.end(), pgroups.pgroups.begin()); +} + +// TODO add more tests with luci::PGroups diff --git a/compiler/luci/partition/src/PartitionIRDump.cpp b/compiler/luci/partition/src/PartitionIRDump.cpp new file mode 100644 index 000000000..4f2c26800 --- /dev/null +++ b/compiler/luci/partition/src/PartitionIRDump.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PartitionIRDump.h" + +#include "CircleOpCode.h" + +#include <iostream> + +namespace luci +{ + +void dump(std::ostream &os, const PNode *pnode) +{ + os << "PNode: " << pnode->group << ", " << pnode->node << ":" << luci::opcode_name(pnode->node) + << ":" << pnode->node->name() << std::endl; +} + +void dump(std::ostream &os, const PGroup *pgroup) +{ + os << "--- PGroup: " << pgroup->group << std::endl; + os << "Input(s): "; + for (auto &node_in : pgroup->inputs) + os << node_in->name() << " "; + os << std::endl; + for (auto &pnode : pgroup->pnodes) + { + dump(os, pnode.get()); + } + os << "Output(s): "; + for (auto &node_out : pgroup->outputs) + os << node_out->name() << " "; + os << std::endl; +} + +void dump(std::ostream &os, const PGroups *pgroups) +{ + for (auto &pgroup : pgroups->pgroups) + { + dump(os, pgroup.get()); + } + os << "--- Node2Group items: " << std::endl; + for (auto it = pgroups->node2group.begin(); it != pgroups->node2group.end(); ++it) + { + auto node = it->first; + auto group = it->second; + os << " Node: " << node << "(" << node->name() << "): " << group << std::endl; + } +} + +} // namespace luci + +std::ostream &operator<<(std::ostream &os, const luci::PGroups *pgroups) +{ + luci::dump(os, pgroups); + return os; +} diff --git a/compiler/luci/partition/src/PartitionIRDump.h b/compiler/luci/partition/src/PartitionIRDump.h new file mode 100644 index 000000000..8a4b3f579 --- /dev/null +++ b/compiler/luci/partition/src/PartitionIRDump.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LUCI_PARTITION_IR_DUMP_H__ +#define __LUCI_PARTITION_IR_DUMP_H__ + +#include "PartitionIR.h" + +#include <iostream> + +namespace luci +{ + +void dump(std::ostream &os, const PNode *pnode); +void dump(std::ostream &os, const PGroup *pgroup); +void dump(std::ostream &os, const PGroups *pgroups); + +} // namespace luci + +std::ostream &operator<<(std::ostream &os, const luci::PGroups *pgroups); + +#endif // __LUCI_PARTITION_IR_DUMP_H__ diff --git a/compiler/luci/partition/src/PartitionMerge.cpp b/compiler/luci/partition/src/PartitionMerge.cpp new file mode 100644 index 000000000..038fc2a0c --- /dev/null +++ b/compiler/luci/partition/src/PartitionMerge.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PartitionMerge.h" + +#include <algorithm> + +namespace +{ + +/** + * @brief return true if pgroup_i output is one of the inputs of pgroup + */ +bool is_input_of(const luci::PGroup *pgroup_i, const luci::PGroup *pgroup) +{ + for (auto *output : pgroup_i->outputs) + { + for (auto *input : pgroup->inputs) + { + if (input == output) + return true; + } + } + return false; +} + +/** + * @brief return true if there is only one input or all the inputs have same group + * @note pgroups is used to find group of pgroup + */ +bool is_input_same(const luci::PGroup *pgroup, const luci::PGroups *pgroups) +{ + assert(pgroups != nullptr); + assert(pgroup != nullptr); + + const luci::PGroup *input_pgroup = nullptr; + std::string group; + for (auto &input : pgroup->inputs) + { + auto input_group = pgroups->group_of(input); + // NOTE: all the nodes should be registered and return should be valid group. + // convert_to_proups() should ensure this. + // assert here to find if there is any problem with this. + assert(not input_group.empty()); + if (input_group.empty()) + input_group = pgroups->default_group; + + if (group.empty()) + group = input_group; + else + { + if (group != input_group) + return false; + } + // if there are multiple inputs, all the inputs should be in same pgroup + // https://github.com/Samsung/ONE/issues/6230#issuecomment-801618150 + // https://github.com/Samsung/ONE/issues/6230#issuecomment-801680531 + auto pgroup_input = pgroups->pgroup_of(input); + if (pgroup_input != nullptr) + { + if (input_pgroup == nullptr) + input_pgroup = pgroup_input; + else + { + if (input_pgroup != pgroup_input) + return false; + } + } + } + return true; +} + +/** + * @brief merge pgroup into pgroup_i + * @note output of pgroup_i should be input of pgroup + */ +void merge_into(luci::PGroup *pgroup, luci::PGroup *pgroup_i) +{ + for (auto &pnode : pgroup->pnodes) + { + // update pgroup for this pnode + pnode->pgroup = pgroup_i; + assert(pnode->group == pgroup_i->group); + + // we don't need to add this in topological order: + // all the nodes will be created first then connection will be held + pgroup_i->pnodes.push_back(std::move(pnode)); + // note: pnode is now nullptr as it's moved into pgroup_i->pnodes + } + + for (auto &input : pgroup->inputs) + { + // add inputs of pgroup to pgroup_i if not member of pgroup_i + bool found_in_pgroup_i = false; + for (auto &pnode : pgroup_i->pnodes) + { + if (input == pnode->node) + { + found_in_pgroup_i = true; + break; + } + } + // skip if this input is already in the inputs + auto fit = std::find(pgroup_i->inputs.begin(), pgroup_i->inputs.end(), input); + if (fit != pgroup_i->inputs.end()) + { + found_in_pgroup_i = true; + } + // note: if we force found_in_pgroup_i to false, for testing there will be + // unnecessary inputs + if (not found_in_pgroup_i) + { + // node input maybe in another pgroup + pgroup_i->inputs.push_back(input); + } + } + // add outputs of pgroup to pgroup_i outputs if not exist + for (auto &output : pgroup->outputs) + { + auto it = std::find(pgroup_i->outputs.begin(), pgroup_i->outputs.end(), output); + if (it == pgroup_i->outputs.end()) + { + pgroup_i->outputs.push_back(output); + } + } +} + +} // namespace + +namespace luci +{ + +/** + * @brief This will merge pgroups with same group values in topological order + */ +std::unique_ptr<luci::PGroups> merge_pgroups(const luci::PGroups *s_pgroups) +{ + // Make a copy of pgroups to apply merge action + // Q) do we really need a copy? + auto d_pgroups = s_pgroups->make_copy(); + + // Merge partition graphs + // - This is initial implementation that works for limited networks + // - if A and B is same group -> if A is input of B -> ... -> merge B into A + auto &pgroups = d_pgroups->pgroups; + bool changed; + do + { + changed = false; + for (auto &pgroup_i : pgroups) + { + bool merged = false; + for (auto it = pgroups.begin(); it != pgroups.end(); ++it) + { + auto &pgroup = *it; + + // skip if same object + if (pgroup->id == pgroup_i->id) + continue; + // skip if different group + if (pgroup->group != pgroup_i->group) + continue; + // skip if not connected + if (!is_input_of(pgroup_i.get(), pgroup.get())) + continue; + // skip if there are multiple inputs but inputs differ in group + if (!is_input_same(pgroup.get(), d_pgroups.get())) + continue; + // TODO add more condition may be needed + + merge_into(pgroup.get(), pgroup_i.get()); + + auto eit = d_pgroups->id2pgroup.find(pgroup->id); + assert(eit != d_pgroups->id2pgroup.end()); + d_pgroups->id2pgroup.erase(eit); + + // remove merged pgroup from pgroups + pgroups.erase(it); + + merged = true; + break; + } + if (merged) + { + changed = true; + break; + } + } + } while (changed); + + return std::move(d_pgroups); +} + +} // namespace luci diff --git a/compiler/luci/partition/src/PartitionMerge.h b/compiler/luci/partition/src/PartitionMerge.h new file mode 100644 index 000000000..5c9fec2d2 --- /dev/null +++ b/compiler/luci/partition/src/PartitionMerge.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LUCI_PARTITON_MERGE_H__ +#define __LUCI_PARTITON_MERGE_H__ + +#include "PartitionIR.h" + +#include <memory> + +namespace luci +{ + +std::unique_ptr<luci::PGroups> merge_pgroups(const luci::PGroups *s_pgroups); + +} // namespace luci + +#endif // __LUCI_PARTITON_MERGE_H__ diff --git a/compiler/luci/partition/src/PartitionPGroups.cpp b/compiler/luci/partition/src/PartitionPGroups.cpp new file mode 100644 index 000000000..594ed6c40 --- /dev/null +++ b/compiler/luci/partition/src/PartitionPGroups.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PartitionPGroups.h" +#include "PartitionIR.h" +#include "CircleOpCode.h" + +#include "luci/Partition.h" +#include "luci/Log.h" +#include "luci/LogHelper.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/IR/CircleNodeVisitor.h> + +#include <loco.h> + +namespace +{ + +class IsVirtualNode final : public luci::CircleNodeVisitor<bool> +{ +public: + bool visit(const luci::CircleInput *) final { return true; } + bool visit(const luci::CircleOutput *) final { return true; } + // TODO add all virtual nodes + + // default is false + bool visit(const luci::CircleNode *) final { return false; } +}; + +bool check_allocate_partition(const luci::CircleNode *node) +{ + IsVirtualNode query; + if (node->accept(&query)) + return false; + /** + * @note About CircleConst + * CirleConst acts like a part of some CircleNode and managing mulitiple + * used(referenced) CircleConst is a bit difficult if it's used across + * different PGroup. So we treat this different to other types. + * https://github.com/Samsung/ONE/issues/6230#issuecomment-809802813 + */ + if (dynamic_cast<const luci::CircleConst *>(node) != nullptr) + return false; + return true; +} + +} // namespace + +namespace luci +{ + +std::unique_ptr<luci::PGroups> produce_pgroups(const luci::Module *source, + const luci::PartitionTable &partition) +{ + assert(source != nullptr); + // TODO support multiple subgraphs + assert(source->size() == 1); + + LOGGER(l); + + auto pgroups = std::make_unique<luci::PGroups>(); + + pgroups->default_group = partition.default_group; + + // Create a PGroup per CircleNode: each PGroup will have one CircleNode + auto graph = source->graph(); + auto nodes = graph->nodes(); + for (uint32_t idx = 0; idx < nodes->size(); ++idx) + { + auto node = loco::must_cast<luci::CircleNode *>(nodes->at(idx)); + + // check if node is normal node that we are interested + if (check_allocate_partition(node)) + { + auto opcodename = luci::opcode_name(node); + assert(!opcodename.empty()); + + auto group = partition.default_group; + auto it = partition.byopcodes.find(opcodename); + if (it != partition.byopcodes.end()) + group = it->second; + + INFO(l) << "Op: " << node->name() << ": " << opcodename << ", " << node << ", " << group + << std::endl; + + auto pgroup = std::make_unique<luci::PGroup>(); + pgroup->group = group; + pgroup->id = idx + 1; + + auto pnode = std::make_unique<luci::PNode>(); + pnode->node = node; + pnode->group = group; + pnode->pgroup = pgroup.get(); + + pgroup->pnodes.push_back(std::move(pnode)); + + // Set input of PGroup + for (uint32_t in = 0; in < node->arity(); ++in) + { + auto input = loco::must_cast<luci::CircleNode *>(node->arg(in)); + // this input maybe CircleInput in source graph + // --> not confident this is safe + pgroup->inputs.push_back(input); + } + // Set output of PGroup: node itself or multiple virtual outputs + // TODO support multiple virtual outputs + pgroup->outputs.push_back(node); + + pgroups->node2group[node] = group; + pgroups->id2pgroup[pgroup->id] = pgroup.get(); + + pgroups->pgroups.push_back(std::move(pgroup)); + } + else + { + INFO(l) << "Skip Op: " << node->name() << std::endl; + // record as default group + pgroups->node2group[node] = partition.default_group; + } + } + + return std::move(pgroups); +} + +} // namespace luci diff --git a/compiler/luci/partition/src/PartitionPGroups.h b/compiler/luci/partition/src/PartitionPGroups.h new file mode 100644 index 000000000..998e11cbd --- /dev/null +++ b/compiler/luci/partition/src/PartitionPGroups.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LUCI_PARTITON_PGROUPS_H__ +#define __LUCI_PARTITON_PGROUPS_H__ + +#include "PartitionIR.h" + +#include "luci/Partition.h" + +#include <luci/IR/Module.h> + +namespace luci +{ + +/** + * @brief This will produce a PGroups from Module and PartitionTable. + * @note Each PGroup will hold one CircleNode and partition key value as group. + * Supports only single Graph in the Module for now. + */ +std::unique_ptr<luci::PGroups> produce_pgroups(const luci::Module *source, + const luci::PartitionTable &partition); + +} // namespace luci + +#endif // __LUCI_PARTITON_PGROUPS_H__ diff --git a/compiler/luci/partition/src/PartitionPGroups.test.cpp b/compiler/luci/partition/src/PartitionPGroups.test.cpp new file mode 100644 index 000000000..960f3cde9 --- /dev/null +++ b/compiler/luci/partition/src/PartitionPGroups.test.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PartitionPGroups.h" + +#include <luci/test/TestIOGraph.h> + +#include <luci/IR/Nodes/CircleSqrt.h> + +#include <gtest/gtest.h> + +namespace +{ + +using namespace luci::test; + +class SqrtGraphlet +{ +public: + SqrtGraphlet() = default; + +public: + void init(loco::Graph *g, const ShapeU32 input_shape) + { + _sqrt = g->nodes()->create<luci::CircleSqrt>(); + _sqrt->dtype(loco::DataType::S32); + _sqrt->name("sqrt"); + } + +protected: + luci::CircleSqrt *_sqrt = nullptr; +}; + +class SqrtGraph : public TestIOGraph, public SqrtGraphlet +{ +public: + SqrtGraph() = default; + +public: + void init(const ShapeU32 shape) + { + TestIOGraph::init(shape, shape); + SqrtGraphlet::init(g(), shape); + + _sqrt->x(input()); + + output()->from(_sqrt); + } +}; + +} // namespace + +TEST(PartitionPGroupsTest, simple_produce) +{ + luci::Module module; + + SqrtGraph g; + g.init({3, 3}); + g.transfer_to(&module); + + luci::PartitionTable pt; + pt.default_group = "A"; + + auto pgs = produce_pgroups(&module, pt); + + ASSERT_EQ(1, pgs->pgroups.size()); +} diff --git a/compiler/luci/partition/src/PartitionPModules.cpp b/compiler/luci/partition/src/PartitionPModules.cpp new file mode 100644 index 000000000..36f4d47a4 --- /dev/null +++ b/compiler/luci/partition/src/PartitionPModules.cpp @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PartitionPModules.h" +#include "ConnectNode.h" + +#include "luci/Service/CircleNodeClone.h" +#include "luci/Log.h" + +#include <loco.h> + +namespace +{ + +void add_graph_input(loco::Graph *graph, luci::CircleInput *input_node) +{ + assert(graph != nullptr); + assert(input_node != nullptr); + + auto graph_input = graph->inputs()->create(); + graph_input->name(input_node->name()); + + // Set GraphInputOutputIndex for graph + input_node->index(graph_input->index()); + + // Data type + graph_input->dtype(input_node->dtype()); + + // Shape of GraphInput + auto input_shape = std::make_unique<loco::TensorShape>(); + input_shape->rank(input_node->rank()); + for (uint32_t r = 0; r < input_node->rank(); ++r) + { + if (input_node->dim(r).known()) + input_shape->dim(r).set(input_node->dim(r).value()); + } + graph_input->shape(std::move(input_shape)); +} + +void add_graph_output(loco::Graph *graph, luci::CircleOutput *output_node) +{ + assert(graph != nullptr); + assert(output_node != nullptr); + + auto graph_output = graph->outputs()->create(); + graph_output->name(output_node->name()); + + // Set GraphInputOutputIndex for graph + output_node->index(graph_output->index()); + + // Data type + graph_output->dtype(output_node->dtype()); + + // Shape of GraphOutput + auto output_shape = std::make_unique<loco::TensorShape>(); + output_shape->rank(output_node->rank()); + for (uint32_t r = 0; r < output_node->rank(); ++r) + { + if (output_node->dim(r).known()) + output_shape->dim(r).set(output_node->dim(r).value()); + } + graph_output->shape(std::move(output_shape)); +} + +/** + * @brief Build loco::graph from pgroup into graph + */ +void build_graph(loco::Graph *graph, const luci::PGroup *pgroup) +{ + LOGGER(l); + + luci::CloneContext clonectx; + + // add input node(s) + for (auto *input : pgroup->inputs) + { + auto *input_clone = graph->nodes()->create<luci::CircleInput>(); + luci::copy_common_attributes(input, input_clone); + + add_graph_input(graph, input_clone); + clonectx.emplace(input, input_clone); + + INFO(l) << "MAP: " + << " input(" << input << ") -> " << input_clone << "(" << input_clone->name() << ")"; + } + + // add CircleConst for inputs + for (auto &pnode : pgroup->pnodes) + { + auto node = pnode->node; + uint32_t arity = node->arity(); + for (uint32_t a = 0; a < arity; ++a) + { + auto in_a_const = dynamic_cast<luci::CircleConst *>(node->arg(a)); + if (in_a_const != nullptr) + { + auto it = clonectx.find(in_a_const); + if (it == clonectx.end()) + { + auto *clone = clone_node(in_a_const, graph); + clonectx.emplace(in_a_const, clone); + + INFO(l) << "MAP: " + << " const(" << in_a_const << ") -> " << clone << "(" << clone->name() << ")"; + } + } + } + } + + // add nodes + for (auto &pnode : pgroup->pnodes) + { + auto *clone = clone_node(pnode->node, graph); + clonectx.emplace(pnode->node, clone); + + INFO(l) << "MAP: " + << " node(" << pnode->node << ") -> " << clone << "(" << clone->name() << ")"; + } + // connect nodes + for (auto &pnode : pgroup->pnodes) + { + clone_connect(pnode->node, clonectx); + } + + // add output node(s) + for (auto *output : pgroup->outputs) + { + auto *output_clone = graph->nodes()->create<luci::CircleOutput>(); + luci::copy_common_attributes(output, output_clone); + // note: we don't add output_clone to clonectx. + // logically, output is not used as an input to any other nodes. + + auto it = clonectx.find(output); + assert(it != clonectx.end()); + output_clone->from(it->second); + + add_graph_output(graph, output_clone); + + INFO(l) << "MAP: " + << "output(" << output << ") -> " << output_clone << "(" << output_clone->name() << ")" + << ": from " << it->second << "(" << it->second->name() << ")"; + } +} + +std::string make_name(const luci::PGroup *pgroup) +{ + auto &first_pnode = *pgroup->pnodes.begin(); + auto *first_node = first_pnode->node; + std::string name = first_node->graph()->name(); + name = name + "_" + pgroup->group; + return name; +} + +} // namespace + +namespace luci +{ + +/** + * @brief This will produce list of luci::Module as PartedModules from pgroups + */ +luci::PartedModules produce_pmodules(const luci::PGroups *pgroups) +{ + LOGGER(l); + + luci::PartedModules pms; + + for (auto &pgroup : pgroups->pgroups) + { + luci::PartedModule pm; + pm.module = std::make_unique<luci::Module>(); + pm.group = pgroup->group; + + auto graph = loco::make_graph(); + + auto graph_name = make_name(pgroup.get()); + graph->name(graph_name); + + INFO(l) << "--- Partition Graph build----------------------"; + INFO(l) << "--- name: " << graph_name; + build_graph(graph.get(), pgroup.get()); + + pm.module->add(std::move(graph)); + pms.pmodules.emplace_back(std::move(pm)); + } + + return pms; +} + +} // namespace luci diff --git a/compiler/luci/partition/src/PartitionPModules.h b/compiler/luci/partition/src/PartitionPModules.h new file mode 100644 index 000000000..628ada56c --- /dev/null +++ b/compiler/luci/partition/src/PartitionPModules.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LUCI_PARTITON_PMODULES_H__ +#define __LUCI_PARTITON_PMODULES_H__ + +#include "PartitionIR.h" + +#include "luci/Partition.h" + +namespace luci +{ + +luci::PartedModules produce_pmodules(const luci::PGroups *pgroups); + +} // namespace luci + +#endif // __LUCI_PARTITON_PMODULES_H__ diff --git a/compiler/luci/partition/src/PartitionPModules.test.cpp b/compiler/luci/partition/src/PartitionPModules.test.cpp new file mode 100644 index 000000000..99c39e839 --- /dev/null +++ b/compiler/luci/partition/src/PartitionPModules.test.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PartitionPModules.h" +#include "PartitionPGroups.h" + +#include <luci/test/TestIOGraph.h> + +#include <luci/IR/Nodes/CircleSqrt.h> + +#include <gtest/gtest.h> + +namespace +{ + +using namespace luci::test; + +class SqrtGraphlet +{ +public: + SqrtGraphlet() = default; + +public: + void init(loco::Graph *g, const ShapeU32 input_shape) + { + _sqrt = g->nodes()->create<luci::CircleSqrt>(); + _sqrt->dtype(loco::DataType::S32); + _sqrt->name("sqrt"); + } + +protected: + luci::CircleSqrt *_sqrt = nullptr; +}; + +class SqrtGraph : public TestIOGraph, public SqrtGraphlet +{ +public: + SqrtGraph() = default; + +public: + void init(const ShapeU32 shape) + { + TestIOGraph::init(shape, shape); + SqrtGraphlet::init(g(), shape); + + _sqrt->x(input()); + + output()->from(_sqrt); + } +}; + +} // namespace + +TEST(PartitionPModulesTest, simple_convert) +{ + luci::Module module; + + SqrtGraph g; + g.init({3, 3}); + g.transfer_to(&module); + + luci::PartitionTable pt; + pt.default_group = "A"; + + auto pgs = produce_pgroups(&module, pt); + auto pms = produce_pmodules(pgs.get()); + + ASSERT_EQ(1, pms.pmodules.size()); +} diff --git a/compiler/luci/partition/src/PartitionPModulesDump.cpp b/compiler/luci/partition/src/PartitionPModulesDump.cpp new file mode 100644 index 000000000..ee50bc6fb --- /dev/null +++ b/compiler/luci/partition/src/PartitionPModulesDump.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PartitionPModulesDump.h" + +#include "luci/LogHelper.h" + +#include <iostream> + +namespace luci +{ + +void dump(std::ostream &os, const PartedModule *pmodule) +{ + os << "--- PartedModule: " << pmodule->group << std::endl; + os << luci::fmt(pmodule->module->graph()); +} + +void dump(std::ostream &os, const PartedModules *pmodules) +{ + for (auto &pmodule : pmodules->pmodules) + { + dump(os, &pmodule); + } + os << std::endl; +} + +} // namespace luci + +std::ostream &operator<<(std::ostream &os, const luci::PartedModules *pmodules) +{ + luci::dump(os, pmodules); + return os; +} diff --git a/compiler/luci/partition/src/PartitionPModulesDump.h b/compiler/luci/partition/src/PartitionPModulesDump.h new file mode 100644 index 000000000..e77b235f4 --- /dev/null +++ b/compiler/luci/partition/src/PartitionPModulesDump.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LUCI_PARTITION_PMODULES_DUMP_H__ +#define __LUCI_PARTITION_PMODULES_DUMP_H__ + +#include "luci/Partition.h" + +#include <iostream> + +namespace luci +{ + +void dump(std::ostream &os, const PartedModule *pmodule); +void dump(std::ostream &os, const PartedModules *pmodules); + +} // namespace luci + +std::ostream &operator<<(std::ostream &os, const luci::PartedModules *pmodules); + +#endif // __LUCI_PARTITION_PMODULES_DUMP_H__ |