2 * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "luci/Service/Validate.h"
19 #include <luci/IR/Nodes/CircleOutput.h>
20 #include <luci/IR/CircleNodeVisitor.h>
22 #include <luci/LogHelper.h>
24 #include <loco/IR/NodeShape.h>
27 #include <unordered_map>
34 std::ostream &operator<<(std::ostream &os, const loco::TensorShape &tensor_shape)
37 for (uint32_t r = 0; r < tensor_shape.rank(); ++r)
42 if (tensor_shape.dim(r).known())
43 os << tensor_shape.dim(r).value();
51 std::ostream &operator<<(std::ostream &os, const luci::CircleNode *circle_node)
54 for (uint32_t r = 0; r < circle_node->rank(); ++r)
59 if (circle_node->dim(r).known())
60 os << circle_node->dim(r).value();
69 * @brief returns a node that is CircleOutput with index is out_index in nodes
71 luci::CircleOutput *find_node(std::vector<loco::Node *> nodes, loco::GraphOutputIndex out_index)
73 for (auto node : nodes)
75 auto circle_output = dynamic_cast<luci::CircleOutput *>(node);
76 if (circle_output != nullptr)
78 if (circle_output->indexed() && circle_output->index() == out_index)
85 bool validate_shape_dtype(loco::Graph *g)
89 auto output_nodes = loco::output_nodes(g);
91 auto count = g->outputs()->size();
92 for (uint32_t out = 0; out < count; ++out)
94 auto graph_out = g->outputs()->at(out);
95 auto out_index = graph_out->index();
97 auto circle_output = find_node(output_nodes, out_index);
98 assert(circle_output != nullptr);
99 assert(circle_output->from() != nullptr);
100 auto circle_node = loco::must_cast<luci::CircleNode *>(circle_output->from());
102 // Shape and dtype validation for CircleOutputExclude is not needed
103 if (dynamic_cast<luci::CircleOutputExclude *>(circle_node))
106 assert(circle_node->shape_status() != luci::ShapeStatus::UNDEFINED);
108 // check if output node shape is same as graph output shape
109 auto go_tensor_shape = graph_out->shape();
110 assert(go_tensor_shape);
112 // NOTE Even if shape of graph output is [] (which means "shape inference was impossible")
113 // but shape of CircleNode is not, it can be valid case because shape inference
114 // algorithm of CircleNode may be upgraded than before. The opposite is possible either.
115 // If such cases are appeared, following validation code should be fixed.
116 bool is_shape_valid = (circle_node->rank() == go_tensor_shape->rank());
117 for (uint32_t i = 0; is_shape_valid && i < circle_node->rank(); ++i)
119 if (!circle_node->dim(i).known() || !go_tensor_shape->dim(i).known())
121 // If at least one of two dimensions is unknown,
122 // the unknown dimension can accept any value.
123 INFO(l) << "Unknown dimension is matched with known dimension" << std::endl;
125 else if (circle_node->dim(i).value() != go_tensor_shape->dim(i).value())
127 is_shape_valid = false;
131 if (is_shape_valid == false)
133 INFO(l) << "[luci] Shape for output #" << out_index << " not same " << std::endl;
134 INFO(l) << "[luci] " << circle_node->name() << " " << circle_node << " vs "
135 << *go_tensor_shape << std::endl;
139 // check if data type match
140 assert(circle_node->dtype() != loco::DataType::Unknown);
141 if (graph_out->dtype() != circle_node->dtype())
143 INFO(l) << "[luci] Type for output #" << out_index << " not same " << std::endl;
151 class MultiOutNodeValidate final : public luci::CircleNodeVisitor<bool>
154 MultiOutNodeValidate() {}
157 template <class T> bool check(const luci::CircleNode *node)
159 auto succs = loco::succs(node);
160 if (succs.size() < 1)
162 for (const auto &cnode : succs)
164 auto const child = dynamic_cast<const T *>(cnode);
165 if (child == nullptr)
172 bool visit(const luci::CircleBidirectionalSequenceLSTM *node) final
174 return check<luci::CircleBidirectionalSequenceLSTMOut>(node);
176 bool visit(const luci::CircleCustom *node) final { return check<luci::CircleCustomOut>(node); }
177 bool visit(const luci::CircleIf *node) final { return check<luci::CircleIfOut>(node); }
178 bool visit(const luci::CircleNonMaxSuppressionV4 *node) final
180 return check<luci::CircleNonMaxSuppressionV4Out>(node);
182 bool visit(const luci::CircleNonMaxSuppressionV5 *node) final
184 return check<luci::CircleNonMaxSuppressionV5Out>(node);
186 bool visit(const luci::CircleSplit *node) final { return check<luci::CircleSplitOut>(node); }
187 bool visit(const luci::CircleSplitV *node) final { return check<luci::CircleSplitVOut>(node); }
188 bool visit(const luci::CircleTopKV2 *node) final { return check<luci::CircleTopKV2Out>(node); }
189 bool visit(const luci::CircleUnique *node) final { return check<luci::CircleUniqueOut>(node); }
190 bool visit(const luci::CircleUnpack *node) final { return check<luci::CircleUnpackOut>(node); }
191 bool visit(const luci::CircleWhile *node) final { return check<luci::CircleWhileOut>(node); }
193 // default true for other nodes
194 bool visit(const luci::CircleNode *) final { return true; }
198 * @brief Validate sequence of multi-output nodes are followed for specific
199 * IRs such as CircleIfOut.
201 bool validate_multi_outs(loco::Graph *g)
205 for (auto node : loco::active_nodes(loco::output_nodes(g)))
207 auto const cnode = loco::must_cast<luci::CircleNode *>(node);
209 MultiOutNodeValidate d;
210 if (cnode->accept(&d))
213 auto const name = cnode->name();
214 INFO(l) << "Node: " << name << ", " << (uint32_t)(cnode->opcode()) << " has invalid successor."
223 class VirtualNodeDetector final : public luci::CircleNodeVisitor<bool>
226 VirtualNodeDetector() {}
229 bool visit(const luci::CircleBidirectionalSequenceLSTMOut *) final { return true; }
230 bool visit(const luci::CircleCustomOut *) final { return true; }
231 bool visit(const luci::CircleIfOut *) final { return true; }
232 bool visit(const luci::CircleNonMaxSuppressionV4Out *) final { return true; }
233 bool visit(const luci::CircleNonMaxSuppressionV5Out *) final { return true; }
234 bool visit(const luci::CircleSplitOut *) final { return true; }
235 bool visit(const luci::CircleSplitVOut *) final { return true; }
236 bool visit(const luci::CircleTopKV2Out *) final { return true; }
237 bool visit(const luci::CircleUnpackOut *) final { return true; }
238 bool visit(const luci::CircleUniqueOut *) final { return true; }
239 bool visit(const luci::CircleWhileOut *) final { return true; }
240 bool visit(const luci::CircleOutputDummy *) final { return true; }
241 bool visit(const luci::CircleOutputExclude *) final { return true; }
243 // Return false by default
244 bool visit(const luci::CircleNode *) final { return false; }
252 bool validate(loco::Graph *g)
257 if (!validate_shape_dtype(g))
260 if (!validate_multi_outs(g))
263 // TODO add more validation
268 bool validate_name(loco::Graph *g)
270 auto nodes = g->nodes();
271 for (uint32_t n = 0; n < nodes->size(); ++n)
273 auto node = loco::must_cast<luci::CircleNode *>(nodes->at(n));
274 // skip virtual nodes
275 VirtualNodeDetector d;
276 if (node->accept(&d))
279 auto name = node->name();
287 bool validate_unique_name(luci::Module *m)
291 std::unordered_map<std::string, bool> names_col;
293 for (size_t g = 0; g < m->size(); ++g)
295 auto graph = m->graph(g);
296 auto nodes = graph->nodes();
297 for (uint32_t n = 0; n < nodes->size(); ++n)
299 auto node = loco::must_cast<luci::CircleNode *>(nodes->at(n));
300 // skip CircleOutput as it may have same name with from() node
301 auto output = dynamic_cast<luci::CircleOutput *>(node);
302 if (output != nullptr)
304 // skip virtual nodes
305 VirtualNodeDetector d;
306 if (node->accept(&d))
309 auto name = node->name();
310 INFO(l) << "Node: " << name << ", " << (uint32_t)(node->opcode()) << std::endl;
311 auto it = names_col.find(name);
312 if (it != names_col.end())
314 INFO(l) << "validate_unique_name: found duplicate " << name << ", " << graph->name()
319 names_col[name] = true;
321 // There can exist same tensor name between different subgraphs.
328 bool validate(luci::Module *module)
332 INFO(l) << "--- validate Module -----------------------------------";
334 for (size_t g = 0; g < module->size(); ++g)
336 auto graph = module->graph(g);
338 INFO(l) << luci::fmt(graph) << std::endl;
340 if (!validate(graph))
342 std::cerr << "ERROR: Invalid circle model" << std::endl;
345 if (!validate_name(graph))
347 std::cerr << "ERROR: circle model has empty name" << std::endl;
352 if (!validate_unique_name(module))
354 std::cerr << "ERROR: circle model has duplicate names" << std::endl;