1429d2810249f535b96c1526b02461864995ddb9
[platform/core/ml/nnfw.git] / compiler / luci / export / src / CircleTensorExporter.cpp
1 /*
2  * Copyright (c) 2020 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 "CircleTensorExporter.h"
18 #include "TypeBridge.h"
19
20 #include <luci/IR/CircleNodes.h>
21 #include <luci/IR/CircleNodeVisitor.h>
22 #include <luci/IR/CircleShapeSignature.h>
23 #include <luci/Service/CircleTypeInference.h>
24 #include <luci/Service/CircleShapeInference.h>
25 #include <luci/Log.h>
26
27 #include <loco/IR/Algorithm.h>
28 #include <loco/IR/CanonicalNode.h>
29 #include <loco/IR/CanonicalNodeVisitor.h>
30 #include <loco/IR/DataTypeTraits.h>
31 #include <oops/InternalExn.h>
32
33 using namespace circle;
34 using namespace flatbuffers;
35
36 namespace
37 {
38
39 using namespace luci;
40
41 class CircleTensoInfo
42 {
43 public:
44   CircleTensoInfo() = default;
45
46 public:
47   void name(const std::string &name) { _name = name; }
48   const std::string &name(void) const { return _name; }
49
50 public:
51   const circle::TensorType &dtype(void) const { return _dtype; }
52   void dtype(const circle::TensorType &dtype) { _dtype = dtype; }
53
54   const ShapeDescription &shape(void) const { return _shape; }
55   void shape(const ShapeDescription &shape) { _shape = shape; }
56
57   const ShapeSignature &shape_signature(void) const { return _shape_signature; }
58   void shape_signature(const ShapeSignature &ss) { _shape_signature = ss; }
59
60   luci::ShapeStatus shape_status(void) const { return _shape_status; }
61   void shape_status(luci::ShapeStatus ss) { _shape_status = ss; }
62
63 public:
64   luci::CircleConst *content(void) const { return _content; }
65   void content(luci::CircleConst *c) { _content = c; }
66
67   luci::CircleQuantParam *quantparam(void) const { return _quantparam; }
68   void quantparam(luci::CircleQuantParam *qp) { _quantparam = qp; }
69
70   luci::SparsityParam *sparsityparam(void) const { return _sparsityparam; }
71   void sparsityparam(luci::SparsityParam *sp) { _sparsityparam = sp; }
72
73 private:
74   std::string _name;
75
76   circle::TensorType _dtype{circle::TensorType_FLOAT32};
77   ShapeDescription _shape{};
78   ShapeSignature _shape_signature;
79   luci::ShapeStatus _shape_status{luci::ShapeStatus::UNDEFINED};
80
81   luci::CircleConst *_content = nullptr;
82   luci::CircleQuantParam *_quantparam = nullptr;
83   luci::SparsityParam *_sparsityparam = nullptr;
84 };
85
86 using CircleTensorContext = std::vector<CircleTensoInfo>;
87
88 struct NoOpDetector final : public luci::CircleNodeMutableVisitor<bool>
89 {
90   // Input is Virtual but does produce a Tensor
91   // Output is Virtual that does not produce any Tensor
92   bool visit(luci::CircleOutput *) final { return true; }
93   bool visit(luci::CircleOutputExclude *) final { return true; }
94
95   // Return false by default
96   bool visit(luci::CircleNode *) final { return false; }
97 };
98
99 void allocateCircleTensorInfo(CircleNode *node, CircleTensorContext &ctx)
100 {
101   LOGGER(l);
102
103   auto tensor_index = static_cast<CircleTensorIndex>(ctx.size());
104   // TODO Use Graph-level metadata for Input & Output
105   // auto tensor_name = "t_" + std::to_string(tensor_index);
106   std::string tensor_name = node->name();
107   if (tensor_name.empty())
108     tensor_name = "t_" + std::to_string(tensor_index);
109   INFO(l) << "[luci] Tensor for " << tensor_name << ": " << tensor_index << std::endl;
110
111   CircleTensoInfo tensor_info;
112
113   tensor_info.name(tensor_name);
114   tensor_info.dtype(to_circle_tensortype(luci::node_dtype(node)));
115   tensor_info.shape_signature(node->shape_signature());
116   if (node->shape_status() == ShapeStatus::VALID)
117     tensor_info.shape(to_shape_description(luci::node_shape(node)));
118   tensor_info.shape_status(node->shape_status());
119
120   tensor_info.content(dynamic_cast<luci::CircleConst *>(node));
121   tensor_info.quantparam(node->quantparam());
122   tensor_info.sparsityparam(node->sparsityparam());
123
124   set_tensor_index(node, tensor_index);
125
126   ctx.emplace_back(tensor_info);
127 }
128
129 class MultiOutputDetector final : public luci::CircleNodeMutableVisitor<bool>
130 {
131 public:
132   MultiOutputDetector(CircleTensorContext &ctx) : _ctx(ctx) {}
133
134 private:
135   void store_outputs(luci::CircleNode *node, uint32_t count)
136   {
137     auto outs = loco::succs(node);
138     assert(outs.size() == count);
139     (void)count; // for unused variable error in release build
140     for (auto out : outs)
141     {
142       auto circle_out = loco::must_cast<luci::CircleNode *>(out);
143       allocateCircleTensorInfo(circle_out, _ctx);
144     }
145     set_tensor_index(node, -1);
146   }
147
148 public:
149   bool visit(luci::CircleIfOut *) final { return true; }
150   bool visit(luci::CircleSplitOut *) final { return true; }
151   bool visit(luci::CircleSplitVOut *) final { return true; }
152   bool visit(luci::CircleTopKV2Out *) final { return true; }
153   bool visit(luci::CircleUnpackOut *) final { return true; }
154   bool visit(luci::CircleWhileOut *) final { return true; }
155
156   bool visit(luci::CircleIf *node) final
157   {
158     store_outputs(node, node->output_count());
159     return true;
160   }
161
162   bool visit(luci::CircleSplit *node) final
163   {
164     store_outputs(node, uint32_t(node->num_split()));
165     return true;
166   }
167
168   bool visit(luci::CircleSplitV *node) final
169   {
170     store_outputs(node, uint32_t(node->num_split()));
171     return true;
172   }
173
174   bool visit(luci::CircleTopKV2 *node) final
175   {
176     store_outputs(node, 2);
177     return true;
178   }
179
180   bool visit(luci::CircleUnpack *node) final
181   {
182     store_outputs(node, node->num());
183     return true;
184   }
185
186   bool visit(luci::CircleWhile *node) final
187   {
188     store_outputs(node, node->output_count());
189     return true;
190   }
191
192   // Return false by default
193   bool visit(luci::CircleNode *) final { return false; }
194
195 private:
196   CircleTensorContext &_ctx;
197 };
198
199 void allocateCircleTensor(CircleNode *node, CircleTensorContext &ctx)
200 {
201   if (node == nullptr)
202     throw std::runtime_error("allocateCIrcleTensor Failed : node is nullptr");
203
204   auto isNoOp = [](loco::Node *node) {
205     if (auto circle_node = dynamic_cast<luci::CircleNode *>(node))
206     {
207       NoOpDetector d;
208       return circle_node->accept(&d);
209     }
210     return false;
211   };
212
213   if (isNoOp(node))
214   {
215     set_tensor_index(node, -1);
216     return;
217   }
218
219   // TODO revise this when loco supports multiple outputs
220   // NOTE this will store all virtual output tensors and skip for the real node
221   if (auto circle_node = dynamic_cast<luci::CircleNode *>(node))
222   {
223     MultiOutputDetector d(ctx);
224     if (circle_node->accept(&d))
225       return;
226   }
227
228   allocateCircleTensorInfo(node, ctx);
229 }
230
231 } // namespace
232
233 namespace
234 {
235
236 flatbuffers::Offset<Vector<int32_t>> encodeShape(FlatBufferBuilder &builder,
237                                                  const ShapeDescription &shape)
238 {
239   assert(shape._rank_known && "unknown number of dimensions is not supported");
240   return builder.CreateVector(shape._dims);
241 }
242
243 flatbuffers::Offset<Vector<int32_t>> encodeShapeSignature(FlatBufferBuilder &builder,
244                                                           const ShapeSignature &shape_signature)
245 {
246   return builder.CreateVector(shape_signature.as_vector());
247 }
248
249 flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder)
250 {
251   return CreateBuffer(builder);
252 }
253
254 template <typename NodeT>
255 flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder, NodeT *)
256 {
257   return CreateBuffer(builder);
258 }
259
260 template <loco::DataType DT>
261 flatbuffers::Offset<circle::Buffer> encodeOpBufferByDType(FlatBufferBuilder &builder,
262                                                           luci::CircleConst *c)
263 {
264   using NativeType = typename loco::DataTypeImpl<DT>::Type;
265
266   std::vector<NativeType> raw_data;
267   const uint32_t size = c->size<DT>();
268   raw_data.reserve(size);
269   for (uint32_t i = 0; i < size; ++i)
270   {
271     raw_data.push_back(c->at<DT>(i));
272   }
273   const size_t raw_size = size * sizeof(NativeType);
274   auto array_offset = builder.CreateVector(reinterpret_cast<uint8_t *>(raw_data.data()), raw_size);
275   return CreateBuffer(builder, array_offset);
276 }
277
278 template <>
279 flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder, luci::CircleConst *c)
280 {
281   switch (c->dtype())
282   {
283     case loco::DataType::FLOAT32:
284       return encodeOpBufferByDType<loco::DataType::FLOAT32>(builder, c);
285     case loco::DataType::S8:
286       return encodeOpBufferByDType<loco::DataType::S8>(builder, c);
287     case loco::DataType::S16:
288       return encodeOpBufferByDType<loco::DataType::S16>(builder, c);
289     case loco::DataType::S32:
290       return encodeOpBufferByDType<loco::DataType::S32>(builder, c);
291     case loco::DataType::S64:
292       return encodeOpBufferByDType<loco::DataType::S64>(builder, c);
293     case loco::DataType::U8:
294       return encodeOpBufferByDType<loco::DataType::U8>(builder, c);
295     case loco::DataType::BOOL:
296       return encodeOpBufferByDType<loco::DataType::BOOL>(builder, c);
297     default:
298       break;
299   }
300
301   INTERNAL_EXN_V("Unsupported datatype", oops::to_uint32(c->dtype()));
302 }
303
304 flatbuffers::Offset<circle::QuantizationParameters>
305 encodeQuantizationParameters(FlatBufferBuilder &builder, luci::CircleQuantParam *quantparam)
306 {
307   if (quantparam == nullptr)
308     return 0;
309
310   flatbuffers::Offset<flatbuffers::Vector<float>> min;
311   flatbuffers::Offset<flatbuffers::Vector<float>> max;
312   flatbuffers::Offset<flatbuffers::Vector<float>> scale;
313   flatbuffers::Offset<flatbuffers::Vector<int64_t>> zero_point;
314   if (quantparam->min.size() && quantparam->max.size())
315   {
316     min = builder.CreateVector(quantparam->min);
317     max = builder.CreateVector(quantparam->max);
318   }
319   if (quantparam->scale.size() && quantparam->zerop.size())
320   {
321     scale = builder.CreateVector(quantparam->scale);
322     zero_point = builder.CreateVector(quantparam->zerop);
323   }
324   // Note: QuantizationDetails is not supported
325   return circle::CreateQuantizationParameters(builder, min, max, scale, zero_point,
326                                               circle::QuantizationDetails::QuantizationDetails_NONE,
327                                               0, quantparam->quantized_dimension);
328 }
329
330 flatbuffers::Offset<circle::SparsityParameters>
331 encodeSparsityParameters(FlatBufferBuilder &builder, luci::SparsityParam *sparsityparam)
332 {
333   if (sparsityparam == nullptr)
334     return 0;
335
336   std::vector<flatbuffers::Offset<circle::DimensionMetadata>> dim_metadata_vec;
337   auto luci_dim_metadata = sparsityparam->dim_metadata;
338   for (auto it : luci_dim_metadata)
339   {
340     // array_segments
341     auto circle_array_segments = to_circle_sparse_index_vector(builder, it.array_segments());
342     auto circle_array_segments_type =
343         to_circle_sparse_index_vector_type(it.array_segments().type());
344
345     // array_indices
346     auto circle_array_indices = to_circle_sparse_index_vector(builder, it.array_indices());
347     auto circle_array_indices_type = to_circle_sparse_index_vector_type(it.array_indices().type());
348     auto dim_metadata = circle::CreateDimensionMetadata(
349         builder, to_circle_dimensiontype(it.format()), it.dense_size(), circle_array_segments_type,
350         circle_array_segments, circle_array_indices_type, circle_array_indices);
351     dim_metadata_vec.emplace_back(dim_metadata);
352   }
353
354   return circle::CreateSparsityParametersDirect(builder, &sparsityparam->traversal_order,
355                                                 &sparsityparam->block_map, &dim_metadata_vec);
356 }
357
358 bool has_same_values(luci::CircleConst *lhs, luci::CircleConst *rhs)
359 {
360   if (lhs->dtype() != rhs->dtype())
361     return false;
362
363   if (lhs->rank() != rhs->rank())
364     return false;
365
366   for (uint32_t i = 0; i < lhs->rank(); ++i)
367     if (!(lhs->dim(i) == rhs->dim(i)))
368       return false;
369
370   switch (lhs->dtype())
371   {
372     case loco::DataType::FLOAT32:
373       for (uint32_t i = 0; i < lhs->size<loco::DataType::FLOAT32>(); ++i)
374         if (lhs->at<loco::DataType::FLOAT32>(i) != rhs->at<loco::DataType::FLOAT32>(i))
375           return false;
376       break;
377
378     case loco::DataType::S32:
379       for (uint32_t i = 0; i < lhs->size<loco::DataType::S32>(); ++i)
380         if (lhs->at<loco::DataType::S32>(i) != rhs->at<loco::DataType::S32>(i))
381           return false;
382       break;
383
384     case loco::DataType::S64:
385       for (uint32_t i = 0; i < lhs->size<loco::DataType::S64>(); ++i)
386         if (lhs->at<loco::DataType::S64>(i) != rhs->at<loco::DataType::S64>(i))
387           return false;
388       break;
389
390     case loco::DataType::BOOL:
391       for (uint32_t i = 0; i < lhs->size<loco::DataType::BOOL>(); ++i)
392         if (lhs->at<loco::DataType::BOOL>(i) != rhs->at<loco::DataType::BOOL>(i))
393           return false;
394       break;
395
396     default:
397       return false;
398   }
399
400   return true;
401 }
402
403 uint32_t get_buffer_id(FlatBufferBuilder &builder, SerializedModelData &md, luci::CircleConst *node)
404 {
405   if (node != nullptr)
406   {
407     // When buffer with same values is found, use the buffer id.
408     for (auto key_value : md._cached_buffer_id)
409     {
410       if (has_same_values(key_value.first, node))
411         return key_value.second;
412     }
413
414     // When buffer with same values is not found, generate new buffer
415     auto buffer = encodeOpBuffer(builder, node);
416
417     auto buffer_id = static_cast<uint32_t>(md._buffers.size());
418     md._buffers.push_back(buffer);
419
420     // Cache the newly generated buffer id
421     md._cached_buffer_id.insert({node, buffer_id});
422
423     return buffer_id;
424   }
425   else
426   {
427     // When there is no CircleConst, the operation do not use buffer.
428     // So return buffer id as 0 which means empty buffer in circle schema.
429     return 0;
430   }
431 }
432
433 void exportOpDefinedTensor(const CircleTensoInfo &info, FlatBufferBuilder &builder,
434                            SerializedModelData &md, SerializedGraphData &gd)
435 {
436   // Create and register output tensor shape
437   flatbuffers::Offset<Vector<int32_t>> shape_offset;
438   if (info.shape_status() == ShapeStatus::VALID)
439     shape_offset = encodeShape(builder, info.shape());
440
441   auto quantparam = encodeQuantizationParameters(builder, info.quantparam());
442
443   auto sparsityparam = encodeSparsityParameters(builder, info.sparsityparam());
444
445   auto shape_signature_offset = encodeShapeSignature(builder, info.shape_signature());
446
447   auto buffer_id = get_buffer_id(builder, md, info.content());
448
449   auto name_offset = builder.CreateString(info.name());
450   auto tensor_offset =
451       CreateTensor(builder, shape_offset, info.dtype(), buffer_id, name_offset, quantparam,
452                    /*is_variable*/ false, sparsityparam, shape_signature_offset);
453   gd._tensors.push_back(tensor_offset);
454 }
455
456 } // namespace
457
458 namespace luci
459 {
460
461 void prepareModelData(FlatBufferBuilder &builder, SerializedModelData &md)
462 {
463   // add one empty buffer
464   //   note: this follows TFLite
465   //   note: there's a comment in tflite fbs file
466   //   - Note the 0th entry of this array must be an empty buffer (sentinel).
467   //   - This is a convention so that tensors without a buffer can provide 0 as
468   //   - their buffer.
469   auto buffer = encodeOpBuffer(builder);
470   md._buffers.push_back(buffer);
471 }
472
473 void exportOpDefinedTensors(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &md,
474                             SerializedGraphData &gd)
475 {
476   CircleTensorContext tensor_ctx;
477
478   // NOTE There may exist dangle CircleInput that is not visited with postorder_traversal()
479   //      All dangle CircleOutput should be visited by postorder_traversal()
480   auto nodes = g->nodes();
481   for (uint32_t n = 0; n < nodes->size(); ++n)
482   {
483     auto node = dynamic_cast<luci::CircleInput *>(nodes->at(n));
484     if (node != nullptr)
485       allocateCircleTensor(node, tensor_ctx);
486   }
487
488   for (auto node : loco::postorder_traversal(loco::output_nodes(g)))
489   {
490     CircleNode *circle_node = loco::must_cast<luci::CircleNode *>(node);
491     if (dynamic_cast<const luci::CircleInput *>(circle_node) != nullptr)
492       continue;
493     allocateCircleTensor(circle_node, tensor_ctx);
494   }
495
496   for (const auto &tensor_info : tensor_ctx)
497   {
498     exportOpDefinedTensor(tensor_info, builder, md, gd);
499   }
500 }
501
502 } // namespace luci