diff options
author | Chunseok Lee <chunseok.lee@samsung.com> | 2022-04-15 19:15:11 +0900 |
---|---|---|
committer | Chunseok Lee <chunseok.lee@samsung.com> | 2022-04-15 19:15:11 +0900 |
commit | 3ad689f0803519e343c36d5700646e86059df961 (patch) | |
tree | 862346c401a5577518fa7f042532aa931b53aa0e /compiler/luci/pass/src/helpers | |
parent | ac6e4dd7b480e83b586ef533d7b29a8a97eb48fe (diff) | |
download | nnfw-3ad689f0803519e343c36d5700646e86059df961.tar.gz nnfw-3ad689f0803519e343c36d5700646e86059df961.tar.bz2 nnfw-3ad689f0803519e343c36d5700646e86059df961.zip |
Imported Upstream version 1.20.0upstream/1.20.0submit/tizen/20220415.103159
Diffstat (limited to 'compiler/luci/pass/src/helpers')
-rw-r--r-- | compiler/luci/pass/src/helpers/LayerInfoMap.cpp | 189 | ||||
-rw-r--r-- | compiler/luci/pass/src/helpers/LayerInfoMap.h | 33 | ||||
-rw-r--r-- | compiler/luci/pass/src/helpers/LayerInfoMap.test.cpp | 201 |
3 files changed, 423 insertions, 0 deletions
diff --git a/compiler/luci/pass/src/helpers/LayerInfoMap.cpp b/compiler/luci/pass/src/helpers/LayerInfoMap.cpp new file mode 100644 index 000000000..ac07f9ec9 --- /dev/null +++ b/compiler/luci/pass/src/helpers/LayerInfoMap.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "LayerInfoMap.h" + +#include <luci/IR/CircleNode.h> + +#include <cassert> + +namespace luci +{ +namespace +{ + +bool is_multiple_output_node(const luci::CircleNode *node) +{ + switch (node->opcode()) + { + // The following nodes have multiple outputs. Output tensors are not produced by themselves but + // by the corresponding *Out nodes. + case luci::CircleOpcode::SPLIT: + case luci::CircleOpcode::SPLIT_V: + case luci::CircleOpcode::TOPK_V2: + case luci::CircleOpcode::UNIQUE: + case luci::CircleOpcode::UNPACK: + return true; + // TODO: Support ops + case luci::CircleOpcode::BIDIRECTIONAL_SEQUENCE_LSTM: + case luci::CircleOpcode::CUSTOM: + case luci::CircleOpcode::IF: + case luci::CircleOpcode::NON_MAX_SUPPRESSION_V4: + case luci::CircleOpcode::NON_MAX_SUPPRESSION_V5: + case luci::CircleOpcode::WHILE: + throw std::runtime_error("Unsupported op now"); + default: + return false; + } +} + +const luci::CircleNode *get_multi_output_node(const luci::CircleNode *node) +{ + if (is_multiple_output_node(node)) + return node; + + switch (node->opcode()) + { + // The following nodes denote outputs of multiple-output nodes. + case luci::CircleOpcode::CIRCLESPLITOUT: + { + const auto split_out = loco::must_cast<const CircleSplitOut *>(node); + return loco::must_cast<luci::CircleNode *>(split_out->input()); + } + case luci::CircleOpcode::CIRCLESPLITVOUT: + { + const auto splitv_out = loco::must_cast<const CircleSplitVOut *>(node); + return loco::must_cast<luci::CircleNode *>(splitv_out->input()); + } + case luci::CircleOpcode::CIRCLETOPKV2OUT: + { + const auto top_kv2_out = loco::must_cast<const CircleTopKV2Out *>(node); + return loco::must_cast<luci::CircleNode *>(top_kv2_out->input()); + } + case luci::CircleOpcode::CIRCLEUNIQUEOUT: + { + const auto unique_out = loco::must_cast<const CircleUniqueOut *>(node); + return loco::must_cast<luci::CircleNode *>(unique_out->input()); + } + case luci::CircleOpcode::CIRCLEUNPACKOUT: + { + const auto unpack_out = loco::must_cast<const CircleUnpackOut *>(node); + return loco::must_cast<luci::CircleNode *>(unpack_out->input()); + } + // TODO: Support these ops + case luci::CircleOpcode::CIRCLEBIDIRECTIONAL_SEQUENCE_LSTM_OUT: + case luci::CircleOpcode::CIRCLECUSTOMOUT: + case luci::CircleOpcode::CIRCLEIFOUT: + case luci::CircleOpcode::CIRCLENONMAXSUPPRESSIONV4OUT: + case luci::CircleOpcode::CIRCLENONMAXSUPPRESSIONV5OUT: + case luci::CircleOpcode::CIRCLEWHILEOUT: + throw std::runtime_error("Unsupported op now"); + default: + return nullptr; + } +} + +bool same_setting(const LayerInfo &left, const LayerInfo &right) +{ + return left.dtype == right.dtype and left.granularity == right.granularity; +} + +void add_multi_output_node(LayerInfoMap &info_by_name, LayerInfo &layer_info, + const luci::CircleNode *node) +{ + assert(is_multiple_output_node(node)); // FIX_CALLER_UNLESS + + const auto succs_nodes = loco::succs(node); + const auto name = node->name(); + + if (info_by_name.find(name) != info_by_name.end()) + { + // Check that all outputs have equal dtype and granularity + for (const auto succs_node : succs_nodes) + { + const auto succs_circle_node = loco::must_cast<luci::CircleNode *>(succs_node); + + const auto it = info_by_name.find(succs_circle_node->name()); + if (it != info_by_name.end() and not same_setting(layer_info, (it->second))) + throw std::runtime_error("Outputs of multiple-output nodes should have equal dtype and " + "granularity. Check the quantization configuration file"); + } + return; + } + + // Add multiple output node to info_by_name + info_by_name[name] = {name, layer_info.dtype, layer_info.granularity}; + + // Add outputs node to info_by_name + for (const auto succs_node : succs_nodes) + { + const auto succs_circle_node = loco::must_cast<luci::CircleNode *>(succs_node); + const auto succs_circle_node_name = succs_circle_node->name(); + info_by_name[succs_circle_node_name] = {succs_circle_node_name, layer_info.dtype, + layer_info.granularity}; + } +} + +} // namespace + +LayerInfoMap layer_info_map(loco::Graph *g, std::vector<LayerInfo> &layers_info) +{ + LayerInfoMap info_by_name; + + for (auto &&info : layers_info) + { + auto name = info.name; + bool found = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + auto cnode = loco::must_cast<luci::CircleNode *>(node); + if (cnode->opcode() == luci::CircleOpcode::CIRCLEOUTPUT) + continue; + + if (cnode->name() == name) + { + // Check and add multiple-output node and its outputs to info_by_name + if (const auto multi_output = get_multi_output_node(cnode)) + { + add_multi_output_node(info_by_name, info, multi_output); + found = true; + continue; + } + + if (info_by_name.find(name) != info_by_name.end()) + { + throw std::runtime_error("Duplicate layer name " + name + + ". Check layer names in the quantization configuration file."); + } + + info_by_name[name] = info; + found = true; + continue; + } + } + + if (not found) + throw std::runtime_error("No such layer named " + name + + ". Check layer names in the quantization configuration file."); + } + + // TODO Check all names in layers_info exist in the info_by_name + // TODO Check names in info_by_name but not in layers_info are from virtual outputs + + return info_by_name; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/helpers/LayerInfoMap.h b/compiler/luci/pass/src/helpers/LayerInfoMap.h new file mode 100644 index 000000000..bb4724a50 --- /dev/null +++ b/compiler/luci/pass/src/helpers/LayerInfoMap.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_PASS_HELPERS_LAYER_INFO_MAP_H__ +#define __LUCI_PASS_HELPERS_LAYER_INFO_MAP_H__ + +#include <luci/Pass/QuantizationParameters.h> + +#include <unordered_map> + +namespace luci +{ + +using LayerInfoMap = std::unordered_map<std::string, luci::LayerInfo>; + +LayerInfoMap layer_info_map(loco::Graph *g, std::vector<LayerInfo> &layers_info); + +} // namespace luci + +#endif // __LUCI_PASS_HELPERS_LAYER_INFO_MAP_H__ diff --git a/compiler/luci/pass/src/helpers/LayerInfoMap.test.cpp b/compiler/luci/pass/src/helpers/LayerInfoMap.test.cpp new file mode 100644 index 000000000..2ed28eda4 --- /dev/null +++ b/compiler/luci/pass/src/helpers/LayerInfoMap.test.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "LayerInfoMap.h" + +#include <luci/IR/CircleNode.h> +#include <luci/test/TestIOGraph.h> + +#include <gtest/gtest.h> + +namespace +{ + +class SoftmaxTestGraph : public luci::test::TestIOGraph +{ +public: + void init(void) + { + TestIOGraph::init({32}, {32}); + _softmax = g()->nodes()->create<luci::CircleSoftmax>(); + { + _softmax->logits(input()); + _softmax->beta(0.1); + _softmax->name("test"); + } + output()->from(_softmax); + } + +private: + luci::CircleSoftmax *_softmax = nullptr; +}; + +class SplitAddTestGraph : public luci::test::TestIOGraph +{ +public: + void init(void) + { + TestIOGraph::init({6, 1, 2}, {3, 1, 2}); + _split_dim = g()->nodes()->create<luci::CircleConst>(); + { + _split_dim->rank(1); + _split_dim->dtype(loco::DataType::S32); + _split_dim->size<loco::DataType::S32>(1); + _split_dim->at<loco::DataType::S32>(0); + _split_dim->shape({1}); + _split_dim->name("split_dim"); + } + + _split = g()->nodes()->create<luci::CircleSplit>(); + { + _split->input(input()); + _split->num_split(2); + _split->split_dim(_split_dim); + _split->name("split0"); + } + + _split_out_1 = g()->nodes()->create<luci::CircleSplitOut>(); + { + _split_out_1->input(_split); + _split_out_1->index(0); + _split_out_1->name("split0"); + } + + _split_out_2 = g()->nodes()->create<luci::CircleSplitOut>(); + { + _split_out_2->input(_split); + _split_out_2->index(1); + _split_out_2->name("split1"); + } + + _add = g()->nodes()->create<luci::CircleAdd>(); + { + _add->x(_split_out_1); + _add->y(_split_out_2); + _add->name("add"); + } + output()->from(_add); + } + +private: + luci::CircleSplit *_split = nullptr; + luci::CircleSplitOut *_split_out_1 = nullptr; + luci::CircleSplitOut *_split_out_2 = nullptr; + luci::CircleConst *_split_dim = nullptr; + luci::CircleAdd *_add = nullptr; +}; + +} // namespace + +TEST(LayerInfoMapTest, simple_test) +{ + SoftmaxTestGraph g; + g.init(); + + luci::LayerInfo info; + { + info.name = "test"; + info.dtype = loco::DataType::U8; + info.granularity = luci::QuantizationGranularity::ChannelWise; + } + std::vector<luci::LayerInfo> v; + v.emplace_back(info); + auto map = luci::layer_info_map(g.g(), v); + + EXPECT_EQ("test", map["test"].name); + EXPECT_EQ(loco::DataType::U8, map["test"].dtype); + EXPECT_EQ(luci::QuantizationGranularity::ChannelWise, map["test"].granularity); +} + +TEST(LayerInfoMapTest, multiple_output_node_test) +{ + SplitAddTestGraph g; + g.init(); + + luci::LayerInfo info; + { + info.name = "split0"; + info.dtype = loco::DataType::U8; + info.granularity = luci::QuantizationGranularity::ChannelWise; + } + std::vector<luci::LayerInfo> v; + v.emplace_back(info); + auto map = luci::layer_info_map(g.g(), v); + + EXPECT_EQ(map.size(), 2); + EXPECT_EQ("split0", map["split0"].name); + EXPECT_EQ("split1", map["split1"].name); + + EXPECT_EQ(loco::DataType::U8, map["split0"].dtype); + EXPECT_EQ(luci::QuantizationGranularity::ChannelWise, map["split0"].granularity); +} + +TEST(LayerInfoMapTest, invalid_layer_info_multiple_output_node_NEG) +{ + SplitAddTestGraph g; + g.init(); + + luci::LayerInfo info_0; + { + info_0.name = "split0"; + info_0.dtype = loco::DataType::U8; + info_0.granularity = luci::QuantizationGranularity::ChannelWise; + } + luci::LayerInfo info_1; + { + info_1.name = "split1"; + info_1.dtype = loco::DataType::S16; + info_1.granularity = luci::QuantizationGranularity::ChannelWise; + } + std::vector<luci::LayerInfo> v; + v.emplace_back(info_0); + v.emplace_back(info_1); + + EXPECT_ANY_THROW(luci::layer_info_map(g.g(), v)); +} + +TEST(LayerInfoMapTest, duplicate_name_NEG) +{ + SoftmaxTestGraph g; + g.init(); + g.input()->name("test"); + + luci::LayerInfo info; + { + info.name = "test"; + info.dtype = loco::DataType::U8; + info.granularity = luci::QuantizationGranularity::ChannelWise; + } + std::vector<luci::LayerInfo> v; + v.emplace_back(info); + EXPECT_ANY_THROW(luci::layer_info_map(g.g(), v)); +} + +TEST(LayerInfoMapTest, no_name_NEG) +{ + SoftmaxTestGraph g; + g.init(); + + luci::LayerInfo info; + { + info.name = "noname"; + info.dtype = loco::DataType::U8; + info.granularity = luci::QuantizationGranularity::ChannelWise; + } + std::vector<luci::LayerInfo> v; + v.emplace_back(info); + EXPECT_ANY_THROW(luci::layer_info_map(g.g(), v)); +} |