Imported Upstream version 1.7.0
[platform/core/ml/nnfw.git] / compiler / locop / src / FormattedGraph.cpp
1 /*
2  * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
3  *
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
7  *
8  *    http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include "locop/FormattedGraph.h"
18 #include "locop/FormattedTensorShape.h"
19 #include "locop/GenericNodeSummaryBuilder.h"
20
21 #include <loco/Service/TypeInference.h>
22 #include <loco/Service/ShapeInference.h>
23
24 #include <pp/Format.h>
25
26 #include <stdex/Memory.h>
27
28 #include <map>
29 #include <set>
30
31 #include <cassert>
32
33 using locop::SymbolTable;
34
35 namespace
36 {
37
38 std::string str(const loco::DataType &dtype)
39 {
40   switch (dtype)
41   {
42     case loco::DataType::Unknown:
43       return "Unknown";
44
45     case loco::DataType::U8:
46       return "U8";
47     case loco::DataType::U16:
48       return "U16";
49     case loco::DataType::U32:
50       return "U32";
51     case loco::DataType::U64:
52       return "U64";
53
54     case loco::DataType::S8:
55       return "S8";
56     case loco::DataType::S16:
57       return "S16";
58     case loco::DataType::S32:
59       return "S32";
60     case loco::DataType::S64:
61       return "S64";
62
63     case loco::DataType::FLOAT16:
64       return "FLOAT16";
65     case loco::DataType::FLOAT32:
66       return "FLOAT32";
67     case loco::DataType::FLOAT64:
68       return "FLOAT64";
69
70     case loco::DataType::BOOL:
71       return "BOOL";
72
73     default:
74       break;
75   };
76
77   throw std::invalid_argument{"dtype"};
78 }
79
80 std::string str(const loco::Domain &domain)
81 {
82   // TODO Generate!
83   switch (domain)
84   {
85     case loco::Domain::Unknown:
86       return "Unknown";
87     case loco::Domain::Tensor:
88       return "Tensor";
89     case loco::Domain::Feature:
90       return "Feature";
91     case loco::Domain::Filter:
92       return "Filter";
93     case loco::Domain::DepthwiseFilter:
94       return "DWFilter";
95     case loco::Domain::Bias:
96       return "Bias";
97     default:
98       break;
99   }
100
101   throw std::invalid_argument{"domain"};
102 }
103
104 std::string str(const loco::NodeShape &node_shape)
105 {
106   using namespace locop;
107
108   switch (node_shape.domain())
109   {
110     case loco::Domain::Tensor:
111     {
112       auto tensor_shape = node_shape.as<loco::TensorShape>();
113       return pp::fmt(locop::fmt<TensorShapeFormat::Plain>(&tensor_shape));
114     }
115     // TODO Show details
116     case loco::Domain::Feature:
117     case loco::Domain::Filter:
118     case loco::Domain::DepthwiseFilter:
119     case loco::Domain::Bias:
120       return "...";
121
122     default:
123       break;
124   }
125
126   throw std::invalid_argument{"domain"};
127 }
128
129 // TODO Use locop::fmt<TensorShapeFormat ...>
130 locop::FormattedTensorShape<locop::TensorShapeFormat::Bracket>
131 formatted_tensor_shape(const loco::TensorShape *ptr)
132 {
133   return locop::FormattedTensorShape<locop::TensorShapeFormat::Bracket>{ptr};
134 }
135
136 } // namespace
137
138 namespace
139 {
140
141 struct NodeDesc : public locop::NodeDesc
142 {
143 public:
144   NodeDesc() = default;
145   NodeDesc(const locop::OpName &opname) : locop::NodeDesc{opname}
146   {
147     // DO NOTHING
148   }
149
150 public:
151   // DEPRECATED
152   const locop::OpName &name(void) const { return opname(); }
153
154   // DEPRECATED
155   uint32_t arg_size(void) const { return args().count(); }
156   // DEPRECATED
157   const locop::ArgElem &arg(uint32_t n) const { return args().at(n); }
158   // DEPRECATED
159   void arg(const locop::ArgName &name, const locop::ArgValue &value) { args().append(name, value); }
160 };
161
162 } // namespace
163
164 // TODO Remove this workaround
165 namespace locop
166 {
167
168 std::ostream &operator<<(std::ostream &os, const NodeDesc &d)
169 {
170   assert(d.state() != NodeDesc::State::Invalid);
171
172   std::vector<std::string> values;
173
174   for (uint32_t n = 0; n < d.args().count(); ++n)
175   {
176     values.emplace_back(d.args().at(n).first + ": " + d.args().at(n).second);
177   }
178
179   if (d.state() == NodeDesc::State::PartiallyKnown)
180   {
181     values.emplace_back("...");
182   }
183
184   os << d.opname();
185   os << "(";
186   if (values.size() > 0)
187   {
188     os << values.at(0);
189     for (uint32_t n = 1; n < values.size(); ++n)
190     {
191       os << ", " << values.at(n);
192     }
193   }
194   os << ")";
195
196   return os;
197 }
198
199 } // namespace locop
200
201 namespace locop
202 {
203
204 std::ostream &operator<<(std::ostream &os, const FormattedGraph &fmt)
205 {
206   fmt.dump(os);
207   return os;
208 }
209
210 } // namespace locop
211
212 namespace locop
213 {
214
215 void FormattedGraphImpl<Formatter::LinearV1>::dump(std::ostream &os) const
216 {
217   struct SymbolTableImpl final : public SymbolTable
218   {
219     std::string lookup(const loco::Node *node) const final
220     {
221       if (node == nullptr)
222       {
223         return "(null)";
224       }
225
226       return _content.at(node);
227     }
228
229     std::map<const loco::Node *, std::string> _content;
230   };
231
232   SymbolTableImpl symbols;
233
234   auto symbol = [&symbols](const loco::Node *node) { return symbols.lookup(node); };
235
236   for (uint32_t n = 0; n < _graph->nodes()->size(); ++n)
237   {
238     symbols._content[_graph->nodes()->at(n)] = pp::fmt("%", n);
239   }
240
241   // Find the disjoint node clusters
242   //
243   // TODO Move this implementation into loco Algorithms.h
244   std::map<loco::Node *, loco::Node *> parents;
245
246   for (auto node : loco::all_nodes(_graph))
247   {
248     parents[node] = nullptr;
249   }
250
251   for (auto node : loco::all_nodes(_graph))
252   {
253     for (uint32_t n = 0; n < node->arity(); ++n)
254     {
255       if (auto arg = node->arg(n))
256       {
257         parents[arg] = node;
258       }
259     }
260   }
261
262   auto find = [&parents](loco::Node *node) {
263     loco::Node *cur = node;
264
265     while (parents.at(cur) != nullptr)
266     {
267       cur = parents.at(cur);
268     }
269
270     return cur;
271   };
272
273   std::set<loco::Node *> roots;
274
275   for (auto node : loco::all_nodes(_graph))
276   {
277     roots.insert(find(node));
278   }
279
280   std::map<loco::Node *, std::set<loco::Node *>> clusters;
281
282   // Create clusters
283   for (auto root : roots)
284   {
285     clusters[root] = std::set<loco::Node *>{};
286   }
287
288   for (auto node : loco::all_nodes(_graph))
289   {
290     clusters.at(find(node)).insert(node);
291   }
292
293   std::unique_ptr<locop::NodeSummaryBuilder> node_summary_builder;
294
295   if (_factory)
296   {
297     // Use User-defined NodeSummaryBuilder if NodeSummaryBuilderFactory is present
298     node_summary_builder = _factory->create(&symbols);
299   }
300   else
301   {
302     // Use Built-in NodeSummaryBuilder otherwise
303     node_summary_builder = stdex::make_unique<GenericNodeSummaryBuilder>(&symbols);
304   }
305
306   // Print Graph Input(s)
307   for (uint32_t n = 0; n < _graph->inputs()->size(); ++n)
308   {
309     auto input = _graph->inputs()->at(n);
310
311     std::string name = input->name();
312
313     std::string shape = "?";
314     if (input->shape() != nullptr)
315     {
316       shape = pp::fmt(formatted_tensor_shape(input->shape()));
317     }
318
319     // TODO Print dtype
320     os << pp::fmt("In #", n, " { name: ", name, ", shape: ", shape, " }") << std::endl;
321   }
322
323   // Print Graph Output(s)
324   for (uint32_t n = 0; n < _graph->outputs()->size(); ++n)
325   {
326     auto output = _graph->outputs()->at(n);
327
328     std::string name = output->name();
329
330     std::string shape = "?";
331     if (output->shape() != nullptr)
332     {
333       shape = pp::fmt(formatted_tensor_shape(output->shape()));
334     }
335
336     // TODO Print dtype
337     os << pp::fmt("Out #", n, " { name: ", name, ", shape: ", shape, " }") << std::endl;
338   }
339
340   if (_graph->inputs()->size() + _graph->outputs()->size() != 0)
341   {
342     os << std::endl;
343   }
344
345   for (auto it = clusters.begin(); it != clusters.end(); ++it)
346   {
347     std::vector<loco::Node *> cluster_outputs;
348
349     for (auto node : it->second)
350     {
351       // NOTE This is inefficient but anyway working :)
352       if (loco::succs(node).empty())
353       {
354         cluster_outputs.emplace_back(node);
355       }
356     }
357
358     for (auto node : loco::postorder_traversal(cluster_outputs))
359     {
360       locop::NodeSummary node_summary;
361
362       // Build a node summary
363       if (!node_summary_builder->build(node, node_summary))
364       {
365         throw std::runtime_error{"Fail to build a node summary"};
366       }
367
368       for (uint32_t n = 0; n < node_summary.comments().count(); ++n)
369       {
370         os << "; " << node_summary.comments().at(n) << std::endl;
371       }
372
373       os << symbol(node);
374
375       if (loco::shape_known(node))
376       {
377         auto node_shape = loco::shape_get(node);
378         os << " : " << str(node_shape.domain());
379         os << "<";
380         os << str(node_shape);
381         os << ", ";
382         // Show DataType
383         os << (loco::dtype_known(node) ? str(loco::dtype_get(node)) : std::string{"?"});
384         os << ">";
385       }
386
387       os << " = " << node_summary << std::endl;
388     }
389     os << std::endl;
390   }
391 }
392
393 } // namespace locop