860cebf6e32d3915936cc732ec6b357f8424bb41
[platform/core/ml/nnfw.git] / compiler / luci / export / src / CircleExporterImpl.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 "CircleExporterImpl.h"
18 #include "Optimize.h"
19 #include "TypeBridge.h"
20 #include "CircleTensorExporter.h"
21 #include "CircleOperationExporter.h"
22 #include "CircleExporterUtils.h"
23
24 #include <oops/InternalExn.h>
25 #include <mio/circle/schema_generated.h>
26 #include <flatbuffers/flatbuffers.h>
27
28 #include <cassert>
29 #include <unordered_map>
30 #include <string>
31 #include <stdexcept>
32
33 namespace
34 {
35
36 luci::CircleInput *input_node(loco::Graph *g, const loco::GraphInputIndex &index)
37 {
38   for (uint32_t n = 0; n < g->nodes()->size(); ++n)
39   {
40     if (auto input = dynamic_cast<luci::CircleInput *>(g->nodes()->at(n)))
41     {
42       if (input->indexed() && input->index() == index)
43       {
44         return input;
45       }
46     }
47   }
48   return nullptr;
49 }
50
51 luci::CircleOutput *output_node(loco::Graph *g, const loco::GraphOutputIndex &index)
52 {
53   for (uint32_t n = 0; n < g->nodes()->size(); ++n)
54   {
55     if (auto output = dynamic_cast<luci::CircleOutput *>(g->nodes()->at(n)))
56     {
57       if (output->indexed() && output->index() == index)
58       {
59         return output;
60       }
61     }
62   }
63   return nullptr;
64 }
65
66 void registerGraphInputTensors(loco::Graph *graph, luci::SubGraphContext &ctx)
67 {
68   for (uint32_t n = 0; n < graph->inputs()->size(); ++n)
69   {
70     auto node = input_node(graph, n);
71     assert(node != nullptr);
72     ctx._inputs.push_back(luci::get_tensor_index(node));
73   }
74 }
75
76 void registerGraphOutputTensors(loco::Graph *graph, luci::SubGraphContext &ctx)
77 {
78   for (uint32_t n = 0; n < graph->outputs()->size(); ++n)
79   {
80     auto push = output_node(graph, n);
81     assert(push != nullptr);
82     auto node = push->from();
83     assert(node != nullptr);
84
85     // Do not export CircleOutput when it's input is CircleOutputExclude
86     if (dynamic_cast<luci::CircleOutputExclude *>(push->from()) != nullptr)
87     {
88       continue;
89     }
90
91     ctx._outputs.push_back(luci::get_tensor_index(node));
92   }
93 }
94
95 } // namespace
96
97 namespace
98 {
99
100 using namespace circle;
101 using namespace flatbuffers;
102
103 Offset<Vector<Offset<OperatorCode>>>
104 encodeOperatorCodes(FlatBufferBuilder &builder, std::unordered_map<luci::OpCode, uint32_t> &opcodes)
105 {
106   std::vector<Offset<OperatorCode>> operator_codes_vec(opcodes.size());
107   for (auto it : opcodes)
108   {
109     uint32_t idx = it.second;
110     if (it.first.opcode != BuiltinOperator_CUSTOM)
111     {
112       operator_codes_vec[idx] = CreateOperatorCode(builder, it.first.opcode, 0, it.first.version);
113     }
114     else
115     {
116       operator_codes_vec[idx] =
117           CreateOperatorCode(builder, it.first.opcode, builder.CreateString(it.first.custom_code));
118     }
119   }
120
121   return builder.CreateVector(operator_codes_vec);
122 }
123
124 } // namespace
125
126 namespace luci
127 {
128
129 using namespace circle;
130 using namespace flatbuffers;
131
132 CircleExporterImpl::CircleExporterImpl(loco::Graph *graph) { exportGraph(graph); }
133 CircleExporterImpl::CircleExporterImpl(Module *module) { exportModule(module); }
134
135 ::flatbuffers::Offset<::circle::SubGraph>
136 CircleExporterImpl::exportSubgraph(SerializedGraphData &gd)
137 {
138   auto tensors = _builder.CreateVector(gd._tensors);
139   auto inputs = _builder.CreateVector(gd._inputs);
140   auto outputs = _builder.CreateVector(gd._outputs);
141   auto operators = _builder.CreateVector(gd._operators);
142   auto name = _builder.CreateString(gd._name);
143   auto df = gd._data_format;
144   auto subgraph = CreateSubGraph(_builder, tensors, inputs, outputs, operators, name, df);
145   return subgraph;
146 }
147
148 void CircleExporterImpl::exportGraph(loco::Graph *graph)
149 {
150   // do graph optimization
151   optimize(graph);
152
153   // copy shape/dtype inference data to CircleNode
154   copy_shape_dtype(graph);
155
156   _builder.Clear();
157
158   SerializedModelData md;
159   SerializedGraphData gd;
160
161   // This version is taken from comment in fbs
162   constexpr uint32_t version = 0;
163
164   // set Subgraph name
165   gd._name = graph->name();
166
167   // TODO set this value properly
168   gd._data_format = circle::DataFormat::DataFormat_CHANNELS_LAST;
169
170   // prepare model data
171   prepareModelData(_builder, md);
172
173   // parse graph into SerializedModelData structure
174   exportOpDefinedTensors(graph, _builder, md, gd);
175
176   // NOTE Invoke these register functions only after each node is annotated with its tensor_index
177   registerGraphInputTensors(graph, gd);
178   registerGraphOutputTensors(graph, gd);
179
180   exportNodes(graph, _builder, md, gd);
181
182   // encode operator codes
183   auto operator_codes = encodeOperatorCodes(_builder, md._operator_codes);
184
185   // Subgraphs
186   Offset<SubGraph> subgraph = exportSubgraph(gd);
187   auto subgraphs = _builder.CreateVector(std::vector<Offset<SubGraph>>{subgraph});
188
189   // Description
190   std::string description_str = "nnpackage";
191   auto description = _builder.CreateString(description_str);
192
193   // create array of buffers
194   auto buffers = _builder.CreateVector(md._buffers);
195
196   // empty metadata
197   std::vector<int> metadata_buffer_vec;
198   auto metadata_buffer = _builder.CreateVector(metadata_buffer_vec);
199
200   // Model
201   auto model_offset = CreateModel(_builder, version, operator_codes, subgraphs, description,
202                                   buffers, metadata_buffer);
203   FinishModelBuffer(_builder, model_offset);
204 }
205
206 void CircleExporterImpl::exportModule(Module *module)
207 {
208   assert(module->size() > 0);
209   // do graph optimization
210
211   SerializedModelData md;
212
213   _builder.Clear();
214
215   // prepare model data
216   prepareModelData(_builder, md);
217
218   std::vector<flatbuffers::Offset<circle::SubGraph>> subgraph_vec;
219
220   for (size_t g = 0; g < module->size(); ++g)
221   {
222     auto graph = module->graph(g);
223
224     optimize(graph);
225
226     // copy shape/dtype inference data to CircleNode
227     copy_shape_dtype(graph);
228
229     SerializedGraphData gd;
230
231     // set Subgraph name
232     gd._name = graph->name();
233
234     // TODO set this value properly
235     gd._data_format = circle::DataFormat::DataFormat_CHANNELS_LAST;
236
237     // parse graph into SerializedModelData structure
238     exportOpDefinedTensors(graph, _builder, md, gd);
239
240     // NOTE Invoke these register functions only after each node is annotated with its tensor_index
241     registerGraphInputTensors(graph, gd);
242     registerGraphOutputTensors(graph, gd);
243
244     exportNodes(graph, _builder, md, gd);
245
246     // Subgraphs
247     Offset<SubGraph> subgraph = exportSubgraph(gd);
248     subgraph_vec.push_back(subgraph);
249   }
250
251   auto subgraphs = _builder.CreateVector(std::vector<Offset<SubGraph>>{subgraph_vec});
252
253   // encode operator codes
254   auto operator_codes = encodeOperatorCodes(_builder, md._operator_codes);
255
256   // Description
257   std::string description_str = "nnpackage";
258   auto description = _builder.CreateString(description_str);
259
260   // create array of buffers
261   auto buffers = _builder.CreateVector(md._buffers);
262
263   // empty metadata
264   std::vector<int> metadata_buffer_vec;
265   auto metadata_buffer = _builder.CreateVector(metadata_buffer_vec);
266
267   // This version is taken from comment in fbs
268   constexpr uint32_t version = 0;
269
270   // Model
271   auto model_offset = CreateModel(_builder, version, operator_codes, subgraphs, description,
272                                   buffers, metadata_buffer);
273   FinishModelBuffer(_builder, model_offset);
274 }
275
276 const char *CircleExporterImpl::getBufferPointer() const
277 {
278   return reinterpret_cast<const char *>(_builder.GetBufferPointer());
279 }
280
281 size_t CircleExporterImpl::getBufferSize() const { return _builder.GetSize(); }
282
283 } // namespace luci