ONE - On-device Neural Engine
Loading...
Searching...
No Matches
CircleTensorExporter.cpp
Go to the documentation of this file.
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
18
19#include "CircleExporterUtils.h"
20
21#include <luci/IR/CircleNodes.h>
25#include <luci/Log.h>
26
27#include <loco/IR/Algorithm.h>
31#include <oops/InternalExn.h>
32
33#include <string.h>
34
35using namespace circle;
36using namespace flatbuffers;
37
38namespace
39{
40
41using namespace luci;
42
43class CircleTensorInfo
44{
45public:
46 CircleTensorInfo() = default;
47
48public:
49 void name(const std::string &name) { _name = name; }
50 const std::string &name(void) const { return _name; }
51
52public:
53 const circle::TensorType &dtype(void) const { return _dtype; }
54 void dtype(const circle::TensorType &dtype) { _dtype = dtype; }
55
56 const ShapeDescription &shape(void) const { return _shape; }
57 void shape(const ShapeDescription &shape) { _shape = shape; }
58
59 luci::ShapeStatus shape_status(void) const { return _shape_status; }
60 void shape_status(luci::ShapeStatus ss) { _shape_status = ss; }
61
62public:
63 luci::CircleConst *content(void) const { return _content; }
64 void content(luci::CircleConst *c) { _content = c; }
65
66 luci::CircleQuantParam *quantparam(void) const { return _quantparam; }
67 void quantparam(luci::CircleQuantParam *qp) { _quantparam = qp; }
68
69 luci::SparsityParam *sparsityparam(void) const { return _sparsityparam; }
70 void sparsityparam(luci::SparsityParam *sp) { _sparsityparam = sp; }
71
72 bool is_variable(void) const { return _is_variable; }
73 void is_variable(bool v) { _is_variable = v; }
74
75private:
76 std::string _name;
77
78 circle::TensorType _dtype{circle::TensorType_FLOAT32};
79 ShapeDescription _shape{};
81
82 luci::CircleConst *_content = nullptr;
83 luci::CircleQuantParam *_quantparam = nullptr;
84 luci::SparsityParam *_sparsityparam = nullptr;
85
86 bool _is_variable = false;
87};
88
89class CircleTensorContext
90{
91public:
92 CircleTensorContext() = default;
93
94public:
95 void emplace_back(CircleTensorInfo &ti)
96 {
97 assert(_names.find(ti.name()) == _names.end());
98 _tis.emplace_back(ti);
99 _names.insert(ti.name());
100 }
101 size_t size(void) const { return _tis.size(); }
102 std::vector<CircleTensorInfo>::iterator begin(void) { return _tis.begin(); }
103 std::vector<CircleTensorInfo>::iterator end(void) { return _tis.end(); }
104
105public:
106 bool exist(const std::string &name) const { return _names.find(name) != _names.end(); }
107
108private:
109 std::vector<CircleTensorInfo> _tis;
110 std::set<std::string> _names;
111};
112
113struct NoOpDetector final : public luci::CircleNodeMutableVisitor<bool>
114{
115 // Input is Virtual but does produce a Tensor
116 // Output is Virtual that does not produce any Tensor
117 bool visit(luci::CircleOutput *) final { return true; }
118 bool visit(luci::CircleOutputExclude *) final { return true; }
119
120 // Return false by default
121 bool visit(luci::CircleNode *) final { return false; }
122};
123
124void allocateCircleTensorInfo(CircleNode *node, CircleTensorContext &ctx)
125{
126 LOGGER(l);
127
128 auto tensor_index = static_cast<CircleTensorIndex>(ctx.size());
129 // TODO Use Graph-level metadata for Input & Output
130 std::string tensor_name = node->name();
131 // NOTE tensor_name maybe empty. this assertion will alert when this happens.
132 // currently we require tensor should have a name.
133 // TODO if this breaks, fix the cause or permit empty tensor_name.
134 assert(!tensor_name.empty());
135 if (ctx.exist(tensor_name))
136 {
137 // NOTE this should assign unique name for a Tensor.
138 tensor_name = tensor_name + "_" + std::to_string(tensor_index);
139 assert(!ctx.exist(tensor_name));
140 }
141 INFO(l) << "[luci] Tensor for " << tensor_name << ": " << tensor_index << std::endl;
142
143 CircleTensorInfo tensor_info;
144
145 tensor_info.name(tensor_name);
146 tensor_info.dtype(to_circle_tensortype(node->dtype()));
147 if (node->shape_status() == ShapeStatus::VALID)
148 tensor_info.shape(to_shape_description(node));
149 tensor_info.shape_status(node->shape_status());
150
151 tensor_info.content(dynamic_cast<luci::CircleConst *>(node));
152 tensor_info.quantparam(node->quantparam());
153 tensor_info.sparsityparam(node->sparsityparam());
154
155 tensor_info.is_variable(dynamic_cast<luci::CircleVariable *>(node) != nullptr);
156
157 set_tensor_index(node, tensor_index);
158
159 ctx.emplace_back(tensor_info);
160}
161
162class MultiOutputDetector final : public luci::CircleNodeMutableVisitor<bool>
163{
164public:
165 MultiOutputDetector(CircleTensorContext &ctx) : _ctx(ctx) {}
166
167private:
168 void store_outputs(luci::CircleNode *node, uint32_t count)
169 {
170 auto outs = loco::succs(node);
171 assert(outs.size() == count);
172 (void)count; // for unused variable error in release build
173 for (auto out : outs)
174 {
175 auto circle_out = loco::must_cast<luci::CircleNode *>(out);
176 allocateCircleTensorInfo(circle_out, _ctx);
177 }
178 set_tensor_index(node, -1);
179 }
180
181public:
182 bool visit(luci::CircleBidirectionalSequenceLSTMOut *) final { return true; }
183 bool visit(luci::CircleCustomOut *) final { return true; }
184 bool visit(luci::CircleIfOut *) final { return true; }
185 bool visit(luci::CircleNonMaxSuppressionV4Out *) final { return true; }
186 bool visit(luci::CircleNonMaxSuppressionV5Out *) final { return true; }
187 bool visit(luci::CircleSplitOut *) final { return true; }
188 bool visit(luci::CircleSplitVOut *) final { return true; }
189 bool visit(luci::CircleTopKV2Out *) final { return true; }
190 bool visit(luci::CircleUnpackOut *) final { return true; }
191 bool visit(luci::CircleUniqueOut *) final { return true; }
192 bool visit(luci::CircleWhileOut *) final { return true; }
193
195 {
196 if (node->merge_outputs())
197 {
198 store_outputs(node, 1);
199 }
200 else
201 {
202 store_outputs(node, 2);
203 }
204 return true;
205 }
206
207 bool visit(luci::CircleCustom *node) final
208 {
209 store_outputs(node, node->numOutputs());
210 return true;
211 }
212
213 bool visit(luci::CircleIf *node) final
214 {
215 store_outputs(node, node->output_count());
216 return true;
217 }
218
219 bool visit(luci::CircleNonMaxSuppressionV4 *node) final
220 {
221 store_outputs(node, 2);
222 return true;
223 }
224
225 bool visit(luci::CircleNonMaxSuppressionV5 *node) final
226 {
227 store_outputs(node, 3);
228 return true;
229 }
230
231 bool visit(luci::CircleSplit *node) final
232 {
233 store_outputs(node, uint32_t(node->num_split()));
234 return true;
235 }
236
237 bool visit(luci::CircleSplitV *node) final
238 {
239 store_outputs(node, uint32_t(node->num_split()));
240 return true;
241 }
242
243 bool visit(luci::CircleTopKV2 *node) final
244 {
245 store_outputs(node, 2);
246 return true;
247 }
248
249 bool visit(luci::CircleUnpack *node) final
250 {
251 store_outputs(node, node->num());
252 return true;
253 }
254
255 bool visit(luci::CircleUnique *node) final
256 {
257 store_outputs(node, 2);
258 return true;
259 }
260
261 bool visit(luci::CircleWhile *node) final
262 {
263 store_outputs(node, node->output_count());
264 return true;
265 }
266
267 // Return false by default
268 bool visit(luci::CircleNode *) final { return false; }
269
270private:
271 CircleTensorContext &_ctx;
272};
273
274void allocateCircleTensor(CircleNode *node, CircleTensorContext &ctx)
275{
276 if (node == nullptr)
277 throw std::runtime_error("allocateCIrcleTensor Failed : node is nullptr");
278
279 auto isNoOp = [](loco::Node *node) {
280 if (auto circle_node = dynamic_cast<luci::CircleNode *>(node))
281 {
282 NoOpDetector d;
283 return circle_node->accept(&d);
284 }
285 return false;
286 };
287
288 if (isNoOp(node))
289 {
290 set_tensor_index(node, -1);
291 return;
292 }
293
294 // TODO revise this when loco supports multiple outputs
295 // NOTE this will store all virtual output tensors and skip for the real node
296 if (auto circle_node = dynamic_cast<luci::CircleNode *>(node))
297 {
298 MultiOutputDetector d(ctx);
299 if (circle_node->accept(&d))
300 return;
301 }
302
303 allocateCircleTensorInfo(node, ctx);
304}
305
306} // namespace
307
308namespace
309{
310
312 const ShapeDescription &shape)
313{
314 assert(shape._rank_known && "unknown number of dimensions is not supported");
315
316 std::vector<int32_t> encoded_shape;
317 encoded_shape.resize(shape._dims.size());
318 for (uint32_t i = 0; i < shape._dims.size(); ++i)
319 encoded_shape.at(i) = shape._dims.at(i) == -1 ? 1 : shape._dims.at(i);
320
321 return builder.CreateVector(encoded_shape);
322}
323
324flatbuffers::Offset<Vector<int32_t>> encodeShapeSignature(FlatBufferBuilder &builder,
325 const ShapeDescription &shape)
326{
327 assert(shape._rank_known && "unknown number of dimensions is not supported");
328
329 // shape_signature is set if and only if at least one of dimensions are unknown.
330 for (uint32_t i = 0; i < shape._dims.size(); ++i)
331 if (shape._dims.at(i) == -1)
332 return builder.CreateVector(shape._dims);
333
335}
336
338{
339 return CreateBuffer(builder);
340}
341
342template <typename NodeT>
344 SerializedModelData &, NodeT *)
345{
346 return CreateBuffer(builder);
347}
348
349template <loco::DataType DT>
351encodeOpBufferByDType(FlatBufferBuilder &builder, SerializedModelData &md, luci::CircleConst *c)
352{
353 using NativeType = typename loco::DataTypeImpl<DT>::Type;
354
355 std::vector<NativeType> raw_data;
356 const uint32_t size = c->size<DT>();
357 raw_data.reserve(size);
358 for (uint32_t i = 0; i < size; ++i)
359 {
360 raw_data.push_back(c->at<DT>(i));
361 }
362 const size_t raw_size = size * sizeof(NativeType);
363
364 if (md._ext_buffer)
365 {
366 // TODO optimize this if this operation takes long or much memory
368 buffer_data.resize(raw_size);
369 std::memcpy(buffer_data.data(), raw_data.data(), raw_size);
370
371 int32_t buffer_index = md._buffers.size();
372 md._buffer_data_map.emplace(buffer_index, buffer_data);
373
374 // create fake indicator buffer
375 return circle::CreateBuffer(builder, 0 /* data */, 1 /* offset */, 1 /* size */);
376 }
377 if (check_size_limit(builder, raw_size))
378 {
379 md._require_ext_buffer = true;
380 return md._empty_buffer;
381 }
382
383 auto array_offset = builder.CreateVector(reinterpret_cast<uint8_t *>(raw_data.data()), raw_size);
384 return CreateBuffer(builder, array_offset);
385}
386
387template <>
389encodeOpBufferByDType<loco::DataType::STRING>(FlatBufferBuilder &builder, SerializedModelData &,
391{
392 const uint32_t count = c->size<loco::DataType::STRING>();
393 uint32_t raw_size = sizeof(int32_t) * (count + 2);
394 for (uint32_t i = 0; i < count; ++i)
395 {
396 auto &value = c->at<loco::DataType::STRING>(i);
397 raw_size += value.length();
398 }
399
400 // serialize string data
401 // int32_t count
402 // int32_t offsets[count + 1]
403 // string values[count]
404 std::vector<uint8_t> raw_data;
405 raw_data.reserve(raw_size);
406
407 auto *i32d = reinterpret_cast<int32_t *>(raw_data.data());
408 int32_t start = sizeof(int32_t) * (count + 2);
409 int32_t offset = start;
410 std::vector<int32_t> offsets;
411
412 *i32d++ = count;
413 *i32d++ = start;
414 offsets.push_back(start);
415 for (uint32_t i = 0; i < count; ++i)
416 {
417 auto &value = c->at<loco::DataType::STRING>(i);
418 offset += value.length();
419 *i32d++ = offset;
420 offsets.push_back(offset);
421 }
422
423 auto *data = reinterpret_cast<uint8_t *>(i32d);
424 for (uint32_t i = 0; i < count; ++i)
425 {
426 int32_t length = offsets[i + 1] - offsets[i];
427 auto &value = c->at<loco::DataType::STRING>(i);
428 memcpy(data, value.c_str(), length);
429 data += length;
430 }
431
432 auto array_offset = builder.CreateVector(reinterpret_cast<uint8_t *>(raw_data.data()), raw_size);
433 return CreateBuffer(builder, array_offset);
434}
435
436template <loco::DataType DT>
438encodeOpBufferPack4bit(FlatBufferBuilder &builder, SerializedModelData &, luci::CircleConst *c)
439{
440 const uint32_t size = c->size<DT>();
441 const uint32_t raw_size = (size + 1) / 2;
442 std::vector<uint8_t> raw_data(raw_size);
443
444 for (uint32_t i = 0; i < raw_size; ++i)
445 {
446 uint32_t sidx = i * 2;
447 uint8_t data = static_cast<uint8_t>(c->at<DT>(sidx));
448 raw_data[i] = data & 0x0f;
449 sidx++;
450 if (sidx < size)
451 {
452 data = static_cast<uint8_t>(c->at<DT>(sidx));
453 raw_data[i] |= data << 4;
454 }
455 }
456
457 auto array_offset = builder.CreateVector(raw_data.data(), raw_size);
458 return CreateBuffer(builder, array_offset);
459}
460
461template <>
464{
465 switch (c->dtype())
466 {
467 case loco::DataType::FLOAT32:
468 return encodeOpBufferByDType<loco::DataType::FLOAT32>(builder, md, c);
469 case loco::DataType::S4:
470 return encodeOpBufferPack4bit<loco::DataType::S4>(builder, md, c);
471 case loco::DataType::S8:
472 return encodeOpBufferByDType<loco::DataType::S8>(builder, md, c);
473 case loco::DataType::S16:
474 return encodeOpBufferByDType<loco::DataType::S16>(builder, md, c);
475 case loco::DataType::S32:
476 return encodeOpBufferByDType<loco::DataType::S32>(builder, md, c);
477 case loco::DataType::S64:
478 return encodeOpBufferByDType<loco::DataType::S64>(builder, md, c);
479 case loco::DataType::U4:
480 return encodeOpBufferPack4bit<loco::DataType::U4>(builder, md, c);
481 case loco::DataType::U8:
482 return encodeOpBufferByDType<loco::DataType::U8>(builder, md, c);
483 case loco::DataType::BOOL:
484 return encodeOpBufferByDType<loco::DataType::BOOL>(builder, md, c);
485 case loco::DataType::STRING:
486 return encodeOpBufferByDType<loco::DataType::STRING>(builder, md, c);
487 default:
488 break;
489 }
490
491 // NOTE loco::DataType::FLOAT16 is added but we do not export this type
492 // as backends currently don't support this type.
493 // currently this is supported only for "Tensor(Float16) - Dequantize"
494 // sequence so that after 'fold_dequantize' option this Tensor is
495 // converted to FLOAT32.
496
497 INTERNAL_EXN_V("Unsupported datatype", oops::to_uint32(c->dtype()));
498}
499
501encodeQuantizationParameters(FlatBufferBuilder &builder, luci::CircleQuantParam *quantparam)
502{
503 if (quantparam == nullptr)
504 return 0;
505
510 if (quantparam->min.size() && quantparam->max.size())
511 {
512 min = builder.CreateVector(quantparam->min);
513 max = builder.CreateVector(quantparam->max);
514 }
515 if (quantparam->scale.size() && quantparam->zerop.size())
516 {
517 scale = builder.CreateVector(quantparam->scale);
518 zero_point = builder.CreateVector(quantparam->zerop);
519 }
520 // Note: QuantizationDetails is not supported
521 return circle::CreateQuantizationParameters(builder, min, max, scale, zero_point,
522 circle::QuantizationDetails::QuantizationDetails_NONE,
523 0, quantparam->quantized_dimension);
524}
525
527encodeSparsityParameters(FlatBufferBuilder &builder, luci::SparsityParam *sparsityparam)
528{
529 if (sparsityparam == nullptr)
530 return 0;
531
532 std::vector<flatbuffers::Offset<circle::DimensionMetadata>> dim_metadata_vec;
533 auto luci_dim_metadata = sparsityparam->dim_metadata;
534 for (const auto &it : luci_dim_metadata)
535 {
536 // array_segments
537 auto circle_array_segments = to_circle_sparse_index_vector(builder, it.array_segments());
538 auto circle_array_segments_type =
539 to_circle_sparse_index_vector_type(it.array_segments().type());
540
541 // array_indices
542 auto circle_array_indices = to_circle_sparse_index_vector(builder, it.array_indices());
543 auto circle_array_indices_type = to_circle_sparse_index_vector_type(it.array_indices().type());
544 auto dim_metadata = circle::CreateDimensionMetadata(
545 builder, to_circle_dimensiontype(it.format()), it.dense_size(), circle_array_segments_type,
546 circle_array_segments, circle_array_indices_type, circle_array_indices);
547 dim_metadata_vec.emplace_back(dim_metadata);
548 }
549
550 return circle::CreateSparsityParametersDirect(builder, &sparsityparam->traversal_order,
551 &sparsityparam->block_map, &dim_metadata_vec);
552}
553
554template <loco::DataType DT> bool has_same_elements(luci::CircleConst *lhs, luci::CircleConst *rhs)
555{
556 assert(lhs->dtype() == DT);
557 assert(rhs->dtype() == DT);
558 assert(lhs->size<DT>() == rhs->size<DT>());
559
560 for (uint32_t i = 0; i < lhs->size<DT>(); ++i)
561 if (lhs->at<DT>(i) != rhs->at<DT>(i))
562 return false;
563 return true;
564}
565
566bool has_same_values(luci::CircleConst *lhs, luci::CircleConst *rhs)
567{
568 if (lhs->dtype() != rhs->dtype())
569 return false;
570
571 if (lhs->rank() != rhs->rank())
572 return false;
573
574 for (uint32_t i = 0; i < lhs->rank(); ++i)
575 if (!(lhs->dim(i) == rhs->dim(i)))
576 return false;
577
578 switch (lhs->dtype())
579 {
580 case loco::DataType::FLOAT32:
581 return has_same_elements<loco::DataType::FLOAT32>(lhs, rhs);
582
583 case loco::DataType::S4:
584 return has_same_elements<loco::DataType::S4>(lhs, rhs);
585
586 case loco::DataType::S8:
587 return has_same_elements<loco::DataType::S8>(lhs, rhs);
588
589 case loco::DataType::S16:
590 return has_same_elements<loco::DataType::S16>(lhs, rhs);
591
592 case loco::DataType::S32:
593 return has_same_elements<loco::DataType::S32>(lhs, rhs);
594
595 case loco::DataType::S64:
596 return has_same_elements<loco::DataType::S64>(lhs, rhs);
597
598 case loco::DataType::U4:
599 return has_same_elements<loco::DataType::U4>(lhs, rhs);
600
601 case loco::DataType::U8:
602 return has_same_elements<loco::DataType::U8>(lhs, rhs);
603
604 case loco::DataType::BOOL:
605 return has_same_elements<loco::DataType::BOOL>(lhs, rhs);
606
607 default:
608 break;
609 }
610
611 return false;
612}
613
614uint32_t get_buffer_id(FlatBufferBuilder &builder, SerializedModelData &md, luci::CircleConst *node)
615{
616 if (node != nullptr)
617 {
618 // When buffer with same values is found, use the buffer id.
619 for (auto key_value : md._cached_buffer_id)
620 {
621 if (has_same_values(key_value.first, node))
622 return key_value.second;
623 }
624
625 // When buffer with same values is not found, generate new buffer
626 auto buffer = encodeOpBuffer(builder, md, node);
627
628 auto buffer_id = static_cast<uint32_t>(md._buffers.size());
629 md._buffers.push_back(buffer);
630
631 // Cache the newly generated buffer id
632 md._cached_buffer_id.insert({node, buffer_id});
633
634 return buffer_id;
635 }
636 else
637 {
638 // When there is no CircleConst, there is nothing to cache.
639 // So return new buffer id.
640 auto buffer = encodeOpBuffer(builder);
641
642 auto buffer_id = static_cast<uint32_t>(md._buffers.size());
643 md._buffers.push_back(buffer);
644
645 return buffer_id;
646 }
647}
648
649void exportOpDefinedTensor(const CircleTensorInfo &info, FlatBufferBuilder &builder,
651{
652 // Create and register output tensor shape
654 flatbuffers::Offset<Vector<int32_t>> shape_signature_offset;
655 if (info.shape_status() == ShapeStatus::VALID)
656 {
657 shape_offset = encodeShape(builder, info.shape());
658 shape_signature_offset = encodeShapeSignature(builder, info.shape());
659 }
660
661 auto quantparam = encodeQuantizationParameters(builder, info.quantparam());
662
663 auto sparsityparam = encodeSparsityParameters(builder, info.sparsityparam());
664
665 auto buffer_id = get_buffer_id(builder, md, info.content());
666
667 auto name_offset = builder.CreateString(info.name());
668
669 auto is_variable = info.is_variable();
670
671 auto tensor_offset = CreateTensor(builder, shape_offset, info.dtype(), buffer_id, name_offset,
672 quantparam, is_variable, sparsityparam, shape_signature_offset);
673 gd._tensors.push_back(tensor_offset);
674}
675
676} // namespace
677
678namespace luci
679{
680
682{
683 md.clear();
684
685 // add one empty buffer
686 // note: this follows TFLite
687 // note: there's a comment in tflite fbs file
688 // - Note the 0th entry of this array must be an empty buffer (sentinel).
689 // - This is a convention so that tensors without a buffer can provide 0 as
690 // - their buffer.
691 md._empty_buffer = encodeOpBuffer(builder);
692 md._buffers.push_back(md._empty_buffer);
693}
694
697{
698 CircleTensorContext tensor_ctx;
699
700 // NOTE There may exist dangle CircleInput that is not visited with postorder_traversal()
701 // All dangle CircleOutput should be visited by postorder_traversal()
702 auto nodes = g->nodes();
703 for (uint32_t n = 0; n < nodes->size(); ++n)
704 {
705 auto node = dynamic_cast<luci::CircleInput *>(nodes->at(n));
706 if (node != nullptr)
707 allocateCircleTensor(node, tensor_ctx);
708 }
709
711 {
712 CircleNode *circle_node = loco::must_cast<luci::CircleNode *>(node);
713 if (dynamic_cast<const luci::CircleInput *>(circle_node) != nullptr)
714 continue;
715 allocateCircleTensor(circle_node, tensor_ctx);
716 }
717
718 for (const auto &tensor_info : tensor_ctx)
719 {
720 exportOpDefinedTensor(tensor_info, builder, md, gd);
721 }
722}
723
725{
726 auto nodes = g->nodes();
727 for (uint32_t n = 0; n < nodes->size(); ++n)
728 {
729 auto node = loco::must_cast<luci::CircleNode *>(nodes->at(n));
730 clear_tensor_index(node);
731 }
732}
733
734} // namespace luci
#define INTERNAL_EXN_V(msg, val)
@ brief throw internal exception with message and value
Definition InternalExn.h:28
#define LOGGER(name)
Definition Log.h:65
#define INFO(name)
Definition Log.h:68
Helper class to hold data needed in creation of a FlatBuffer. To serialize data, you typically call o...
Offset< String > CreateString(const char *str, size_t len)
Store a string in the buffer, which can contain any binary data.
Offset< Vector< T > > CreateVector(const T *v, size_t len)
Serialize an array into a FlatBuffer vector.
A neural network graph.
Definition Graph.h:161
Logical unit of computation.
Definition Node.h:54
BIDIRECTIONAL_SEQUENCE_LSTM in Circle.
Virtual CIRCLEBIDIRECTIONAL_SEQUENCE_LSTM_OUT in Circle.
Class to build tensor data.
Definition CircleConst.h:35
const loco::DataTypeImpl< DT >::Type & at(uint32_t n) const
uint32_t size(void) const
CUSTOM in Circle.
Virtual CIRCLECUSTOMOUT in Circle.
IF in Circle.
Definition CircleIf.h:34
Virtual CIRCLEIFOUT in Circle.
Definition CircleIfOut.h:32
CircleNode used for Input of the Graph.
Definition CircleInput.h:36
NON_MAX_SUPPRESSION_V4 in Circle.
Virtual NONMAXSUPPRESSIONV4OUT in Circle.
NON_MAX_SUPPRESSION_V5 in Circle.
Virtual NONMAXSUPPRESSIONV5OUT in Circle.
CircleOutputExclude is used to specifying not exported nodes.
CircleNode for Output of the Graph.
SPLIT in Circle.
Definition CircleSplit.h:32
Virtual CIRCLESPLITOUT in Circle.
SPLIT_V in Circle.
Virtual CIRCLESPLITVOUT in Circle.
TOPK_V2 in Circle.
Virtual CIRCLETOPKV2OUT in Circle.
Unique in Circle.
Virtual CIRCLEUNIQUEOUT in Circle.
UNPACK in Circle.
Virtual CIRCLEUNPACKOUT in Circle.
Virtual CircleVariable in Circle for 'variable' Tensor.
WHILE in Circle.
Definition CircleWhile.h:34
Virtual CIRCLEWHILEOUT in Circle.
__global uchar * offset(const Image *img, int x, int y)
Definition helpers.h:540
volatile const char info[]
void exportOpDefinedTensor(const TFLTensorInfo &info, FlatBufferBuilder &builder, SerializedModelData &gd)
const T * data(const std::vector< T, Alloc > &v)
std::vector< loco::Node * > postorder_traversal(const std::vector< loco::Node * > &roots)
Generate postorder traversal sequence starting from "roots".
Definition Algorithm.cpp:53
std::set< Node * > succs(const Node *node)
Enumerate all the successors of a given node.
Definition Node.cpp:46
std::vector< Node * > output_nodes(Graph *)
Definition Graph.cpp:101
flatbuffers::Offset< void > to_circle_sparse_index_vector(flatbuffers::FlatBufferBuilder &fb, const SparseIndexVector &sparse_idx_vec)
bool check_size_limit(const flatbuffers::FlatBufferBuilder &fb, const uint64_t data_size)
void set_tensor_index(loco::Node *node, const CircleTensorIndex &tensor_id)
ShapeDescription to_shape_description(const luci::CircleNode *node)
circle::DimensionType to_circle_dimensiontype(luci::DimensionType type)
const char * tensor_name(const circle::Tensor *tensor)
circle::SparseIndexVector to_circle_sparse_index_vector_type(luci::SparseIndexVectorType type)
int32_t CircleTensorIndex
circle::TensorType to_circle_tensortype(loco::DataType type)
void exportOpDefinedTensors(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &md, SerializedGraphData &gd)
create Tensors corresponding to results of all nodes in graph
void clear_tensor_index(loco::Node *node)
void prepareModelData(FlatBufferBuilder &builder, SerializedModelData &md)
one time preparation for SerializedModelData
void clearExportInfo(loco::Graph *g)
clear temporary export information annotated to graph nodes
ShapeStatus
ShapeStatus is to remember circle node shape status.
ShapeIterator end(const Shape &s)
uint32_t to_uint32(T a)
Definition InternalExn.h:33
int32_t size[5]
Definition Slice.cpp:35
int32_t begin[5]
Definition Slice.cpp:33
C++ scalar type corresponding to each DataType.
CircleQuantParam * quantparam(void) const
SparsityParam * sparsityparam(void) const
NodeName name(void) const
ShapeStatus shape_status(void) const
virtual T visit(CircleNode *)
Default fallback.
std::vector< float > scale
std::vector< float > min
std::vector< float > max
std::vector< int64_t > zerop
std::vector< flatbuffers::Offset< circle::Tensor > > _tensors
std::map< luci::CircleConst *, uint32_t > _cached_buffer_id
flatbuffers::Offset< circle::Buffer > _empty_buffer
std::vector< flatbuffers::Offset< circle::Buffer > > _buffers
std::vector< uint8_t > BufferData
std::vector< int32_t > _dims
std::vector< DimMetaData > dim_metadata
std::vector< int32_t > block_map
std::vector< int32_t > traversal_order