summaryrefslogtreecommitdiff
path: root/compiler/luci/pass/src/helpers
diff options
context:
space:
mode:
authorChunseok Lee <chunseok.lee@samsung.com>2022-04-15 19:15:11 +0900
committerChunseok Lee <chunseok.lee@samsung.com>2022-04-15 19:15:11 +0900
commit3ad689f0803519e343c36d5700646e86059df961 (patch)
tree862346c401a5577518fa7f042532aa931b53aa0e /compiler/luci/pass/src/helpers
parentac6e4dd7b480e83b586ef533d7b29a8a97eb48fe (diff)
downloadnnfw-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.cpp189
-rw-r--r--compiler/luci/pass/src/helpers/LayerInfoMap.h33
-rw-r--r--compiler/luci/pass/src/helpers/LayerInfoMap.test.cpp201
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));
+}