Imported Upstream version 1.25.0
[platform/core/ml/nnfw.git] / runtime / onert / api / src / nnfw_api_internal.cc
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 "nnfw_api_internal.h"
18 #include "CustomKernelRegistry.h"
19 #include "compiler/CompilerFactory.h"
20 #include "util/ConfigSource.h"
21 #include "util/Exceptions.h"
22 #include "util/logging.h"
23 #include "exec/Execution.h"
24 #include "circle_loader.h"
25 #include "tflite_loader.h"
26 #include "trix_loader.h"
27 #include "json/json.h"
28 #include "ir/NNPkg.h"
29 #include "ir/OpCode.h"
30 #include "util/TracingCtx.h"
31 #include "odc/QuantizeManager.h"
32
33 #include <fstream>
34 #include <iostream>
35 #include <string>
36 #include <vector>
37 #include <dirent.h>
38 #include <misc/string_helpers.h>
39
40 /*
41  * API does not accept string argument longer than max length below
42  */
43 #define MAX_BACKEND_NAME_LENGTH 32
44 #define MAX_OP_NAME_LENGTH 64
45 #define MAX_PATH_LENGTH 1024
46 #define MAX_TENSOR_NAME_LENGTH 64
47
48 namespace
49 {
50
51 // Is null-terminating in length ?
52 bool null_terminating(const char *str, uint32_t length)
53 {
54   for (uint32_t i = 0; i < length; i++)
55   {
56     if (*(str + i) == '\0')
57     {
58       return true;
59     }
60   }
61   return false;
62 }
63
64 onert::ir::Layout convertLayout(NNFW_LAYOUT layout)
65 {
66   if (layout == NNFW_LAYOUT_CHANNELS_LAST)
67   {
68     return onert::ir::Layout::NHWC;
69   }
70   else if (layout == NNFW_LAYOUT_CHANNELS_FIRST)
71   {
72     return onert::ir::Layout::NCHW;
73   }
74   return onert::ir::Layout::UNKNOWN;
75 }
76
77 NNFW_STATUS getTensorIndexImpl(const onert::ir::IGraph &graph, const char *tensorname,
78                                uint32_t *index, bool is_input)
79 {
80   if (!tensorname || !index)
81     return NNFW_STATUS_UNEXPECTED_NULL;
82
83   if (!null_terminating(tensorname, MAX_TENSOR_NAME_LENGTH))
84   {
85     std::cerr << "nnpackage path is too long" << std::endl;
86     return NNFW_STATUS_ERROR;
87   }
88
89   auto ind_found = is_input ? graph.getInputIndex(tensorname) : graph.getOutputIndex(tensorname);
90
91   if (ind_found.undefined())
92   {
93     // Not found
94     return NNFW_STATUS_ERROR;
95   }
96   else
97   {
98     *index = ind_found.value();
99     return NNFW_STATUS_NO_ERROR;
100   }
101 }
102
103 std::string trim(const std::string &value)
104 {
105   std::string whitespace = " \t";
106   auto begin = value.find_first_not_of(whitespace);
107   if (begin == std::string::npos)
108     return ""; // no content
109
110   auto end = value.find_last_not_of(whitespace);
111   auto range = end - begin + 1;
112   return value.substr(begin, range);
113 }
114
115 bool loadConfigure(const std::string cfgfile, onert::util::CfgKeyValues &keyValues)
116 {
117   std::ifstream ifs(cfgfile);
118   if (ifs.is_open())
119   {
120     std::string line;
121     while (std::getline(ifs, line))
122     {
123       auto cmtpos = line.find('#');
124       if (cmtpos != std::string::npos)
125       {
126         line = line.substr(0, cmtpos);
127       }
128       std::istringstream isline(line);
129       std::string key;
130       if (std::getline(isline, key, '='))
131       {
132         std::string value;
133         if (std::getline(isline, value))
134         {
135           key = trim(key);
136           keyValues[key] = trim(value);
137         }
138       }
139     }
140     ifs.close();
141     return true;
142   }
143   return false;
144 }
145
146 NNFW_TYPE datatype_to_nnfw_dtype(onert::ir::DataType dt)
147 {
148   using onert::ir::DataType;
149   switch (dt)
150   {
151     case DataType::FLOAT32:
152       return NNFW_TYPE_TENSOR_FLOAT32;
153     case DataType::INT32:
154       return NNFW_TYPE_TENSOR_INT32;
155     case DataType::QUANT_UINT8_ASYMM:
156       return NNFW_TYPE_TENSOR_QUANT8_ASYMM;
157     case DataType::BOOL8:
158       return NNFW_TYPE_TENSOR_BOOL;
159     case DataType::UINT8:
160       return NNFW_TYPE_TENSOR_UINT8;
161     case DataType::INT64:
162       return NNFW_TYPE_TENSOR_INT64;
163     case DataType::QUANT_INT8_ASYMM:
164       return NNFW_TYPE_TENSOR_QUANT8_ASYMM_SIGNED;
165     case DataType::QUANT_INT16_SYMM:
166       return NNFW_TYPE_TENSOR_QUANT16_SYMM_SIGNED;
167     case DataType::UINT32:
168     case DataType::QUANT_INT8_SYMM:
169     default:
170       throw std::runtime_error("Error: Model has type that runtime API does not support.");
171   }
172 }
173
174 void fillTensorInfo(nnfw_tensorinfo *ti, const onert::ir::Shape &shape,
175                     const onert::ir::DataType &dtype)
176 {
177   ti->rank = shape.rank();
178   for (int j = 0; j < ti->rank; ++j)
179   {
180     ti->dims[j] = shape.dim(j);
181   }
182   ti->dtype = datatype_to_nnfw_dtype(dtype);
183 }
184
185 std::unique_ptr<onert::ir::Model> loadModel(const std::string filename,
186                                             const std::string model_type)
187 {
188   if (model_type == "tflite")
189     return onert::tflite_loader::loadModel(filename.c_str());
190   if (model_type == "circle")
191     return onert::circle_loader::loadModel(filename.c_str());
192   if (model_type == "tvn")
193     return onert::trix_loader::loadModel(filename.c_str());
194
195   std::cerr << "Unsupported model type" << std::endl;
196   return std::unique_ptr<onert::ir::Model>(nullptr);
197 }
198
199 #ifdef ONERT_TRAIN
200 uint64_t getBufSize(const nnfw_tensorinfo *info)
201 {
202   static int elmsize[] = {
203     sizeof(float),   /* NNFW_TYPE_TENSOR_FLOAT32 = 0 */
204     sizeof(int),     /* NNFW_TYPE_TENSOR_INT32 = 1 */
205     sizeof(uint8_t), /* NNFW_TYPE_TENSOR_QUANT8_ASYMM = 2 */
206     sizeof(bool),    /* NNFW_TYPE_TENSOR_BOOL = 3 */
207     sizeof(uint8_t), /* NNFW_TYPE_TENSOR_UINT8 = 4 */
208     sizeof(int64_t), /* NNFW_TYPE_TENSOR_INT64 = 5 */
209     sizeof(int8_t),  /* NNFW_TYPE_TENSOR_QUANT8_ASYMM_SIGNED = 6 */
210     sizeof(int16_t), /* NNFW_TYPE_TENSOR_QUANT16_SYMM_SIGNED = 7 */
211   };
212
213   uint64_t n = 1;
214   for (int32_t i = 0; i < info->rank; ++i)
215   {
216     assert(info->dims[i] >= 0);
217     n *= info->dims[i];
218   }
219   return elmsize[info->dtype] * n;
220 }
221 #endif // ONERT_TRAIN
222 } // namespace
223
224 nnfw_session::nnfw_session()
225   : _nnpkg{nullptr}, _coptions{}, _compiler_artifact{nullptr}, _execution{nullptr},
226     _kernel_registry{nullptr}, _quant_manager{nullptr}
227 {
228   // DO NOTHING
229 }
230
231 NNFW_STATUS nnfw_session::create(nnfw_session **session)
232 {
233   if (session == nullptr)
234     return NNFW_STATUS_UNEXPECTED_NULL;
235   try
236   {
237     auto new_session = std::unique_ptr<nnfw_session>(new nnfw_session());
238     new_session->_kernel_registry = std::make_shared<onert::api::CustomKernelRegistry>();
239     *session = new_session.release();
240   }
241   catch (const std::bad_alloc &e)
242   {
243     std::cerr << "Error during session creation" << std::endl;
244     *session = nullptr; // Set nullptr on error to keep the old behavior
245     return NNFW_STATUS_OUT_OF_MEMORY;
246   }
247   catch (const std::exception &e)
248   {
249     std::cerr << "Error during session initialization : " << e.what() << std::endl;
250     *session = nullptr; // Set nullptr on error to keep the old behavior
251     return NNFW_STATUS_ERROR;
252   }
253   return NNFW_STATUS_NO_ERROR;
254 }
255
256 nnfw_session::~nnfw_session() = default;
257
258 NNFW_STATUS nnfw_session::load_circle_from_buffer(uint8_t *buffer, size_t size)
259 {
260   if (!isStateInitialized())
261     return NNFW_STATUS_INVALID_STATE;
262
263   if (!buffer)
264     return NNFW_STATUS_UNEXPECTED_NULL;
265
266   if (size == 0)
267     return NNFW_STATUS_ERROR;
268
269   try
270   {
271     auto model = onert::circle_loader::loadModel(buffer, size);
272     _nnpkg = std::make_shared<onert::ir::NNPkg>(std::move(model));
273     _coptions.push_back(onert::compiler::CompilerOptions::fromGlobalConfig());
274     _state = State::MODEL_LOADED;
275   }
276   catch (const std::exception &e)
277   {
278     std::cerr << "Error during model loading : " << e.what() << std::endl;
279     return NNFW_STATUS_ERROR;
280   }
281   return NNFW_STATUS_NO_ERROR;
282 }
283
284 NNFW_STATUS nnfw_session::load_model_from_modelfile(const char *model_file_path)
285 {
286   if (!isStateInitialized())
287     return NNFW_STATUS_INVALID_STATE;
288
289   if (!model_file_path)
290   {
291     std::cerr << "Model file path is null." << std::endl;
292     return NNFW_STATUS_UNEXPECTED_NULL;
293   }
294
295   // Create quantize manager
296   _quant_manager = std::make_unique<onert::odc::QuantizeManager>(std::string(model_file_path));
297
298   std::string filename{model_file_path};
299   // TODO: Use std::filesystem::path when we can use c++17.
300   auto dotidx = filename.find_last_of('.');
301   if (dotidx == std::string::npos)
302   {
303     std::cerr << "Invalid model file path. Please use file with extension." << std::endl;
304     return NNFW_STATUS_ERROR;
305   }
306   std::string model_type = filename.substr(dotidx + 1); // + 1 to exclude dot
307   try
308   {
309     auto model = loadModel(filename, model_type);
310     if (model == nullptr)
311       return NNFW_STATUS_ERROR;
312     _nnpkg = std::make_shared<onert::ir::NNPkg>(std::move(model));
313     _coptions.push_back(onert::compiler::CompilerOptions::fromGlobalConfig());
314     _state = State::MODEL_LOADED;
315   }
316   catch (const std::exception &e)
317   {
318     std::cerr << "Error during model loading : " << e.what() << std::endl;
319     return NNFW_STATUS_ERROR;
320   }
321   return NNFW_STATUS_NO_ERROR;
322 }
323
324 NNFW_STATUS nnfw_session::load_model_from_nnpackage(const char *package_dir)
325 {
326   if (!isStateInitialized())
327     return NNFW_STATUS_INVALID_STATE;
328
329   if (!package_dir)
330   {
331     std::cerr << "package_dir is null." << std::endl;
332     return NNFW_STATUS_UNEXPECTED_NULL;
333   }
334
335   if (!null_terminating(package_dir, MAX_PATH_LENGTH))
336   {
337     std::cerr << "nnpackage path is too long" << std::endl;
338     return NNFW_STATUS_ERROR;
339   }
340
341   // TODO : add support for zipped package file load
342   DIR *dir;
343   if (!(dir = opendir(package_dir)))
344   {
345     std::cerr << "invalid nnpackge directory: " << package_dir << std::endl;
346     return NNFW_STATUS_ERROR;
347   }
348   closedir(dir);
349
350   try
351   {
352     std::string package_path(package_dir);
353     std::string manifest_file_name = package_path + "/metadata/MANIFEST";
354     std::ifstream mfs(manifest_file_name);
355
356     // extract the filename of the first(index 0) model
357     // e.g. In MANIFEST file, { "models" : [ "firstmodel.tflite", "2nd.tflite" ] }
358     Json::Value root;
359     mfs >> root;
360     const Json::Value &models = root["models"];
361     const Json::Value &model_types = root["model-types"];
362     const Json::Value &configs = root["configs"];
363
364     if (!configs.empty() && !configs[0].empty())
365     {
366       auto filepath = package_path + std::string("/metadata/") + configs[0].asString();
367
368       onert::util::CfgKeyValues keyValues;
369       if (loadConfigure(filepath, keyValues))
370       {
371         onert::util::setConfigKeyValues(keyValues);
372       }
373     }
374     _nnpkg = std::make_shared<onert::ir::NNPkg>();
375     auto num_models = models.size();
376     if (num_models == 0 || (num_models - 1) > onert::ir::ModelIndex::max())
377     {
378       std::cerr << "Invalid model size - " << std::to_string(num_models) << std::endl;
379       return NNFW_STATUS_ERROR;
380     }
381
382     // Create quantize manager
383     // TODO Support multiple models
384     auto const model_filename = package_path + std::string("/") + models[0].asString();
385     _quant_manager = std::make_unique<onert::odc::QuantizeManager>(model_filename);
386
387     for (uint16_t i = 0; i < num_models; ++i)
388     {
389       auto model_file_path = package_path + std::string("/") + models[i].asString();
390       auto model_type = model_types[i].asString();
391       auto model = loadModel(model_file_path, model_type);
392       if (model == nullptr)
393         return NNFW_STATUS_ERROR;
394       model->bindKernelBuilder(_kernel_registry->getBuilder());
395       _nnpkg->push(onert::ir::ModelIndex{i}, std::move(model));
396       _coptions.push_back(onert::compiler::CompilerOptions::fromGlobalConfig());
397     }
398
399     auto toIODesc = [](std::string str) {
400       auto indices = nnfw::misc::split(str, ':');
401       if (indices.size() != 3)
402       {
403         std::cerr << "IODesc should be 3-tuple." << std::endl;
404         return onert::ir::IODesc{};
405       }
406       auto model_idx = static_cast<uint32_t>(std::stoi(indices.at(0)));
407       auto subgraph_idx = static_cast<uint32_t>(std::stoi(indices.at(1)));
408       auto operand_idx = static_cast<uint32_t>(std::stoi(indices.at(2)));
409       return onert::ir::IODesc{model_idx, subgraph_idx, operand_idx};
410     };
411     // read pkg-inputs and pkg-outputs
412     const Json::Value &pkg_inputs = root["pkg-inputs"];
413     for (uint32_t i = 0; i < pkg_inputs.size(); ++i)
414       _nnpkg->addInput(toIODesc(pkg_inputs[i].asString()));
415     const Json::Value &pkg_outputs = root["pkg-outputs"];
416     for (uint32_t i = 0; i < pkg_outputs.size(); ++i)
417       _nnpkg->addOutput(toIODesc(pkg_outputs[i].asString()));
418     // read model-connect
419     const Json::Value &fromtos = root["model-connect"];
420     for (uint32_t i = 0; i < fromtos.size(); ++i)
421     {
422       const Json::Value &tos = fromtos[i]["to"];
423       for (uint32_t j = 0; j < tos.size(); ++j)
424         _nnpkg->addEdge(toIODesc(fromtos[i]["from"].asString()), toIODesc(tos[j].asString()));
425     }
426
427     _nnpkg->verify();
428     _state = State::MODEL_LOADED;
429   }
430   catch (const std::exception &e)
431   {
432     std::cerr << "Error during model loading : " << e.what() << std::endl;
433     return NNFW_STATUS_ERROR;
434   }
435   return NNFW_STATUS_NO_ERROR;
436 }
437
438 NNFW_STATUS nnfw_session::prepare()
439 {
440   // NOTE. If users want to run prepare() more than one time, this could be removed.
441   if (!isStateModelLoaded())
442   {
443     std::cerr << "Error during model prepare : ";
444     if (isStateInitialized())
445     {
446       std::cerr << "prepare should be run once";
447     }
448     else
449     {
450       std::cerr << "invalid state";
451     }
452     std::cerr << std::endl;
453     return NNFW_STATUS_INVALID_STATE;
454   }
455
456   try
457   {
458     auto compiler = onert::compiler::CompilerFactory::get().create(_nnpkg, _coptions);
459     _nnpkg.reset();
460     _compiler_artifact = compiler->compile();
461     _execution = std::make_unique<onert::exec::Execution>(_compiler_artifact->_executors);
462   }
463   catch (const std::exception &e)
464   {
465     std::cerr << "Error during model prepare : " << e.what() << std::endl;
466     return NNFW_STATUS_ERROR;
467   }
468
469   _state = State::PREPARED;
470   return NNFW_STATUS_NO_ERROR;
471 }
472
473 NNFW_STATUS nnfw_session::prepare_pipeline(const char *)
474 {
475   std::cerr << "Pipeline prepare_pipeline: deprecated feature " << std::endl;
476   return NNFW_STATUS_ERROR;
477 }
478
479 NNFW_STATUS nnfw_session::run()
480 {
481   if (!isStatePreparedOrFinishedRun())
482   {
483     std::cerr << "Error during nnfw_session::run : "
484               << "run should be run after prepare" << std::endl;
485     return NNFW_STATUS_INVALID_STATE;
486   }
487
488   try
489   {
490     _execution->execute();
491   }
492   catch (const onert::InsufficientBufferSizeException &e)
493   {
494     // Currently insufficient buffer always means output buffer.
495     std::cerr << "Error during nnfw_session::run : " << e.what() << std::endl;
496     return NNFW_STATUS_INSUFFICIENT_OUTPUT_SIZE;
497   }
498   catch (const std::exception &e)
499   {
500     std::cerr << "Error during nnfw_session::run : " << e.what() << std::endl;
501     return NNFW_STATUS_ERROR;
502   }
503
504   _state = State::FINISHED_RUN;
505   return NNFW_STATUS_NO_ERROR;
506 }
507
508 NNFW_STATUS nnfw_session::run_async()
509 {
510   if (!isStatePreparedOrFinishedRun())
511   {
512     std::cerr << "Error during nnfw_session::run_async : "
513               << "run_async should be run after prepare" << std::endl;
514     return NNFW_STATUS_INVALID_STATE;
515   }
516
517   _execution->startExecute();
518
519   _state = State::RUNNING;
520   return NNFW_STATUS_NO_ERROR;
521 }
522
523 NNFW_STATUS nnfw_session::await()
524 {
525   if (!isStateRunning())
526   {
527     std::cerr << "Error during nnfw_session::run_await : "
528               << "run_await should be run after run_async" << std::endl;
529     return NNFW_STATUS_ERROR;
530   }
531
532   _execution->waitFinish();
533
534   _state = State::FINISHED_RUN;
535   return NNFW_STATUS_NO_ERROR;
536 }
537
538 NNFW_STATUS nnfw_session::set_input(uint32_t index, NNFW_TYPE /*type*/, const void *buffer,
539                                     size_t length)
540 {
541   if (!isStatePreparedOrFinishedRun())
542   {
543     std::cerr << "Error during nnfw_session::set_input : invalid state" << std::endl;
544     return NNFW_STATUS_INVALID_STATE;
545   }
546
547   if (!buffer && length != 0)
548   {
549     std::cerr
550       << "Error during nnfw_session::set_input : given buffer is NULL but the length is not 0"
551       << std::endl;
552     return NNFW_STATUS_ERROR;
553   }
554
555   try
556   {
557     _execution->setInput(onert::ir::IOIndex(index), buffer, length);
558   }
559   catch (const std::exception &e)
560   {
561     std::cerr << "Error during nnfw_session::set_input : " << e.what() << std::endl;
562     return NNFW_STATUS_ERROR;
563   }
564   return NNFW_STATUS_NO_ERROR;
565 }
566
567 NNFW_STATUS nnfw_session::set_output(uint32_t index, NNFW_TYPE /*type*/, void *buffer,
568                                      size_t length)
569 {
570   if (!isStatePreparedOrFinishedRun())
571   {
572     std::cerr << "Error during nnfw_session::set_output : invalid state" << std::endl;
573     return NNFW_STATUS_INVALID_STATE;
574   }
575
576   if (!buffer && length != 0)
577   {
578     std::cerr
579       << "Error during nnfw_session::set_output : given buffer is NULL but the length is not 0"
580       << std::endl;
581     return NNFW_STATUS_ERROR;
582   }
583
584   try
585   {
586     _execution->setOutput(onert::ir::IOIndex(index), buffer, length);
587   }
588   catch (const std::exception &e)
589   {
590     std::cerr << "Error during nnfw_session::set_output : " << e.what() << std::endl;
591     return NNFW_STATUS_ERROR;
592   }
593   return NNFW_STATUS_NO_ERROR;
594 }
595
596 NNFW_STATUS nnfw_session::input_size(uint32_t *number)
597 {
598   if (isStateInitialized()) // Model is not loaded
599     return NNFW_STATUS_INVALID_STATE;
600
601   try
602   {
603     if (number == nullptr)
604     {
605       std::cerr << "Error during nnfw_session::input_size, number is null pointer." << std::endl;
606       return NNFW_STATUS_UNEXPECTED_NULL;
607     }
608     *number = getInputSize();
609   }
610   catch (const std::exception &e)
611   {
612     std::cerr << "Error during nnfw_session::input_size : " << e.what() << std::endl;
613     return NNFW_STATUS_ERROR;
614   }
615   return NNFW_STATUS_NO_ERROR;
616 }
617
618 NNFW_STATUS nnfw_session::output_size(uint32_t *number)
619 {
620   if (isStateInitialized()) // Model is not loaded
621     return NNFW_STATUS_INVALID_STATE;
622
623   try
624   {
625     if (number == nullptr)
626     {
627       std::cerr << "Error during nnfw_session::output_size, number is null pointer." << std::endl;
628       return NNFW_STATUS_UNEXPECTED_NULL;
629     }
630     *number = getOutputSize();
631   }
632   catch (const std::exception &e)
633   {
634     std::cerr << "Error during nnfw_session::output_size" << e.what() << std::endl;
635     return NNFW_STATUS_ERROR;
636   }
637   return NNFW_STATUS_NO_ERROR;
638 }
639
640 NNFW_STATUS nnfw_session::set_input_layout(uint32_t index, NNFW_LAYOUT layout)
641 {
642   if (!isStatePreparedOrFinishedRun())
643   {
644     std::cerr << "Error during nnfw_session::set_input_layout : "
645               << "run should be run after prepare" << std::endl;
646     return NNFW_STATUS_INVALID_STATE;
647   }
648
649   try
650   {
651     if (layout != NNFW_LAYOUT_NONE && layout != NNFW_LAYOUT_CHANNELS_FIRST &&
652         layout != NNFW_LAYOUT_CHANNELS_LAST)
653     {
654       std::cerr << "Error during nnfw_session::set_input_layout, not supported layout" << std::endl;
655       return NNFW_STATUS_ERROR;
656     }
657
658     _execution->setInputLayout(onert::ir::IOIndex(index), convertLayout(layout));
659   }
660   catch (const std::exception &e)
661   {
662     std::cerr << "Error during nnfw_session::set_input_layout : " << e.what() << std::endl;
663     return NNFW_STATUS_ERROR;
664   }
665   return NNFW_STATUS_NO_ERROR;
666 }
667
668 NNFW_STATUS nnfw_session::set_output_layout(uint32_t index, NNFW_LAYOUT layout)
669 {
670   if (!isStatePreparedOrFinishedRun())
671   {
672     std::cerr << "Error during nnfw_session::set_output_layout : "
673               << "run should be run after prepare" << std::endl;
674     return NNFW_STATUS_INVALID_STATE;
675   }
676
677   try
678   {
679     if (layout != NNFW_LAYOUT_NONE && layout != NNFW_LAYOUT_CHANNELS_FIRST &&
680         layout != NNFW_LAYOUT_CHANNELS_LAST)
681     {
682       std::cerr << "Error during nnfw_session::set_output_layout, not supported layout"
683                 << std::endl;
684       return NNFW_STATUS_ERROR;
685     }
686
687     _execution->setOutputLayout(onert::ir::IOIndex(index), convertLayout(layout));
688   }
689   catch (const std::exception &e)
690   {
691     std::cerr << "Error during nnfw_session::set_output_layout : " << e.what() << std::endl;
692     return NNFW_STATUS_ERROR;
693   }
694   return NNFW_STATUS_NO_ERROR;
695 }
696
697 NNFW_STATUS nnfw_session::apply_tensorinfo(uint32_t index, nnfw_tensorinfo ti)
698 {
699   // sanity check
700   {
701     if (isStateInitialized())
702     {
703       std::cerr << "Error during set_input_tensorinfo : should be run after load_model"
704                 << std::endl;
705       return NNFW_STATUS_INVALID_STATE;
706     }
707
708     if (ti.rank <= 0 || ti.rank > NNFW_MAX_RANK)
709     {
710       std::cerr << "unsupported rank: " << ti.rank << std::endl;
711       return NNFW_STATUS_ERROR;
712     }
713
714     for (int32_t i = 0; i < ti.rank; ++i)
715     {
716       if (ti.dims[i] <= 0)
717       {
718         std::cerr << "dim must be positive integer but was " << ti.dims[i] << std::endl;
719         return NNFW_STATUS_ERROR;
720       }
721     }
722   }
723
724   onert::ir::Shape new_shape(ti.rank);
725   for (int32_t i = 0; i < ti.rank; i++)
726     new_shape.dim(i) = ti.dims[i];
727
728   if (!isStatePreparedOrFinishedRun())
729   {
730
731     // In this case, if we apply input shape, it will propagate after compilation and excution
732     _nnpkg->changeInputShape(index, new_shape);
733   }
734   else // when called after nnfw_session::prepare()
735     _execution->changeInputShape(onert::ir::IOIndex(index), new_shape);
736
737   return NNFW_STATUS_NO_ERROR;
738 }
739
740 NNFW_STATUS nnfw_session::set_input_tensorinfo(uint32_t index, const nnfw_tensorinfo *ti)
741 {
742   nnfw_tensorinfo ti_copy = *ti;
743   return apply_tensorinfo(index, ti_copy);
744 }
745
746 NNFW_STATUS nnfw_session::input_tensorinfo(uint32_t index, nnfw_tensorinfo *ti)
747 {
748   if (isStateInitialized())
749     return NNFW_STATUS_INVALID_STATE;
750
751   try
752   {
753     if (ti == nullptr)
754     {
755       std::cerr << "Error during nnfw_session::input_tensorinfo, tensorinfo is null pointer."
756                 << std::endl;
757       return NNFW_STATUS_UNEXPECTED_NULL;
758     }
759
760     if (index >= getInputSize())
761     {
762       std::cerr << "Error during nnfw_session::input_tensorinfo, index is out of range."
763                 << std::endl;
764       return NNFW_STATUS_ERROR;
765     }
766
767     if (isStateModelLoaded())
768     {
769       auto info = _nnpkg->inputInfo(index);
770       fillTensorInfo(ti, info.shape(), info.typeInfo().type());
771     }
772     else
773     {
774       auto io_index = onert::ir::IOIndex{index};
775       auto shape = _execution->getInputShape(io_index);
776       auto dtype = _compiler_artifact->_executors->inputInfo(io_index).typeInfo().type();
777       fillTensorInfo(ti, shape, dtype);
778     }
779   }
780   catch (const std::exception &e)
781   {
782     std::cerr << "Error during nnfw_session::input_tensorinfo : " << e.what() << std::endl;
783     return NNFW_STATUS_ERROR;
784   }
785   return NNFW_STATUS_NO_ERROR;
786 }
787
788 NNFW_STATUS nnfw_session::output_tensorinfo(uint32_t index, nnfw_tensorinfo *ti)
789 {
790   if (isStateInitialized())
791     return NNFW_STATUS_INVALID_STATE;
792
793   if (ti == nullptr)
794   {
795     std::cerr << "Error during nnfw_session::output_tensorinfo, tensorinfo is null pointer."
796               << std::endl;
797     return NNFW_STATUS_UNEXPECTED_NULL;
798   }
799
800   try
801   {
802     if (index >= getOutputSize())
803     {
804       std::cerr << "Error during nnfw_session::output_tensorinfo, index is out of range."
805                 << std::endl;
806       return NNFW_STATUS_ERROR;
807     }
808
809     if (isStateModelLoaded())
810     {
811       auto info = _nnpkg->outputInfo(index);
812       fillTensorInfo(ti, info.shape(), info.typeInfo().type());
813     }
814     else
815     {
816       auto io_index = onert::ir::IOIndex{index};
817       auto shape = _execution->getOutputShape(io_index);
818       auto dtype = _compiler_artifact->_executors->outputInfo(io_index).typeInfo().type();
819       fillTensorInfo(ti, shape, dtype);
820     }
821   }
822   catch (const std::exception &e)
823   {
824     std::cerr << "Error during nnfw_session::output_tensorinfo : " << e.what() << std::endl;
825     return NNFW_STATUS_ERROR;
826   }
827
828   return NNFW_STATUS_NO_ERROR;
829 }
830
831 NNFW_STATUS nnfw_session::push_pipeline_input(std::vector<void *> *, std::vector<uint32_t> *)
832 {
833   std::cerr << "Pipeline push_pipeline_input: deprecated feature " << std::endl;
834   return NNFW_STATUS_ERROR;
835 }
836
837 NNFW_STATUS nnfw_session::pop_pipeline_output(std::vector<void *> *)
838 {
839   std::cerr << "Pipeline pop_pipeline_output: deprecated feature " << std::endl;
840   return NNFW_STATUS_ERROR;
841 }
842
843 NNFW_STATUS nnfw_session::register_custom_operation(const std::string &id,
844                                                     nnfw_custom_eval eval_func)
845 {
846   _kernel_registry->registerKernel(id, eval_func);
847   return NNFW_STATUS_NO_ERROR;
848 }
849
850 static std::string get_op_backend_string(std::string op)
851 {
852 #define MAP_MACRO(CircleName, OneRTName) {#CircleName, #OneRTName},
853
854   static std::unordered_map<std::string, std::string> operation_map = {
855 #include "OpMap.lst"
856   };
857
858 #undef MAP_MACRO
859
860   auto n = operation_map.find(op);
861
862   if (n == operation_map.end())
863   {
864     // this return value is handled by a caller to return error code
865     return std::string("");
866   }
867   else
868   {
869     return n->second;
870   }
871 }
872
873 NNFW_STATUS nnfw_session::set_available_backends(const char *backends)
874 {
875   if (!isStateModelLoaded())
876     return NNFW_STATUS_INVALID_STATE;
877
878   try
879   {
880     if (!backends)
881       return NNFW_STATUS_UNEXPECTED_NULL;
882     if (null_terminating(backends, MAX_BACKEND_NAME_LENGTH) == false)
883       return NNFW_STATUS_ERROR;
884
885     auto &options = *_coptions[0];
886
887     using namespace onert::util;
888
889     options.backend_list = nnfw::misc::split(std::string{backends}, ';');
890   }
891   catch (const std::exception &e)
892   {
893     std::cerr << "Error during nnfw_session::set_available_backends : " << e.what() << std::endl;
894     return NNFW_STATUS_ERROR;
895   }
896   return NNFW_STATUS_NO_ERROR;
897 }
898
899 NNFW_STATUS nnfw_session::set_op_backend(const char *op, const char *backend)
900 {
901   if (!isStateModelLoaded())
902     return NNFW_STATUS_INVALID_STATE;
903
904   try
905   {
906     if (!op || !backend)
907       return NNFW_STATUS_UNEXPECTED_NULL;
908     if (!null_terminating(op, MAX_OP_NAME_LENGTH) ||
909         !null_terminating(backend, MAX_BACKEND_NAME_LENGTH))
910       return NNFW_STATUS_ERROR;
911
912     auto key = get_op_backend_string(op);
913
914     if (key.empty())
915     {
916       return NNFW_STATUS_ERROR;
917     }
918
919     auto &opcode_to_backend = _coptions[0]->manual_scheduler_options.opcode_to_backend;
920     opcode_to_backend.emplace(onert::ir::toOpCode(key), backend);
921   }
922   catch (const std::exception &e)
923   {
924     std::cerr << "Error during nnfw_session::set_op_backend : " << e.what() << std::endl;
925     return NNFW_STATUS_ERROR;
926   }
927   return NNFW_STATUS_NO_ERROR;
928 }
929
930 NNFW_STATUS nnfw_session::set_config(const char *key, const char *value)
931 {
932   if (!isStateModelLoaded())
933     return NNFW_STATUS_INVALID_STATE;
934
935   if (!key || !value)
936     return NNFW_STATUS_UNEXPECTED_NULL;
937
938   auto &options = *_coptions[0];
939
940   using namespace onert::util;
941
942   const std::string skey = key;
943
944   if (skey == config::TRACE_FILEPATH)
945   {
946     options.trace_filepath = value;
947   }
948   else if (skey == config::GRAPH_DOT_DUMP)
949   {
950     options.graph_dump_level = toInt(value);
951   }
952   else if (skey == config::EXECUTOR)
953   {
954     options.executor = value;
955   }
956   else if (skey == config::OP_BACKEND_ALLOPS)
957   {
958     options.manual_scheduler_options.backend_for_all = value;
959   }
960   else if (skey == config::USE_SCHEDULER)
961   {
962     options.he_scheduler = toBool(value);
963   }
964   else if (skey == config::PROFILING_MODE)
965   {
966     options.he_profiling_mode = toBool(value);
967   }
968   else
969   {
970     return NNFW_STATUS_ERROR;
971   }
972   return NNFW_STATUS_NO_ERROR;
973 }
974
975 const onert::ir::IGraph *nnfw_session::primary_subgraph()
976 {
977   if (_nnpkg != nullptr)
978   {
979     assert(_execution == nullptr);
980     return _nnpkg->primary_model()->primary_subgraph().get();
981   }
982   else
983   {
984     assert(_execution != nullptr);
985     // We assumed the graph will not change after compilation, but shape could change
986     return &_execution->primary_subgraph();
987   }
988 }
989
990 uint32_t nnfw_session::getInputSize()
991 {
992   if (isStateInitialized())
993     throw std::runtime_error{"Model is not loaded yet"};
994
995   if (isStateModelLoaded())
996     return _nnpkg->inputSize();
997
998   // Session is prepared (general inference)
999   return _compiler_artifact->_executors->inputSize();
1000 }
1001
1002 uint32_t nnfw_session::getOutputSize()
1003 {
1004   if (isStateInitialized())
1005     throw std::runtime_error{"Model is not loaded yet"};
1006
1007   if (isStateModelLoaded())
1008     return _nnpkg->outputSize();
1009
1010   // Session is prepared (general inference)
1011   return _compiler_artifact->_executors->outputSize();
1012 }
1013
1014 NNFW_STATUS nnfw_session::get_config(const char *key, char *value, size_t value_size)
1015 {
1016   if (!isStateModelLoaded())
1017     return NNFW_STATUS_INVALID_STATE;
1018
1019   if (!key || !value)
1020     return NNFW_STATUS_UNEXPECTED_NULL;
1021
1022   auto &options = *_coptions[0];
1023
1024   auto check_boundary = [](size_t dest_size, std::string &src) {
1025     if (dest_size < src.length() + 1 /* for '\0' */)
1026     {
1027       std::cerr << "buffer is small to copy config value." << std::endl;
1028       return false;
1029     }
1030     return true;
1031   };
1032
1033   const std::string skey = key;
1034
1035   if (skey == onert::util::config::BACKENDS)
1036   {
1037     if (options.backend_list.size() == 0)
1038       return NNFW_STATUS_NO_ERROR; // no setting backend is not an error of get_config_str()
1039
1040     auto str = nnfw::misc::join(options.backend_list.begin(), options.backend_list.end(), ";");
1041
1042     if (!check_boundary(value_size, str))
1043       return NNFW_STATUS_ERROR;
1044
1045     strncpy(value, str.c_str(), value_size);
1046   }
1047   else if (skey == onert::util::config::EXECUTOR)
1048   {
1049     if (!check_boundary(value_size, options.executor))
1050       return NNFW_STATUS_ERROR;
1051
1052     strncpy(value, options.executor.c_str(), options.executor.length());
1053   }
1054   else
1055   {
1056     return NNFW_STATUS_ERROR;
1057   }
1058
1059   return NNFW_STATUS_NO_ERROR;
1060 }
1061
1062 bool nnfw_session::isStateInitialized()
1063 {
1064   if (_state == State::INITIALIZED)
1065   {
1066     assert(_nnpkg == nullptr);
1067     assert(_coptions.empty());
1068     assert(_execution == nullptr);
1069     return true;
1070   }
1071   else
1072   {
1073     return false;
1074   }
1075 }
1076
1077 bool nnfw_session::isStateModelLoaded()
1078 {
1079   if (_state == State::MODEL_LOADED)
1080   {
1081     assert(_nnpkg != nullptr);
1082     assert(!_coptions.empty());
1083     assert(_execution == nullptr);
1084     return true;
1085   }
1086   else
1087   {
1088     return false;
1089   }
1090 }
1091
1092 bool nnfw_session::isStatePrepared()
1093 {
1094   if (_state == State::PREPARED)
1095   {
1096     assert(_nnpkg == nullptr);
1097     assert(!_coptions.empty());
1098     assert(_execution != nullptr);
1099     return true;
1100   }
1101   else
1102   {
1103     return false;
1104   }
1105 }
1106
1107 bool nnfw_session::isStateRunning()
1108 {
1109   if (_state == State::RUNNING)
1110   {
1111     assert(_nnpkg == nullptr);
1112     assert(!_coptions.empty());
1113     assert(_execution != nullptr);
1114     return true;
1115   }
1116   return false;
1117 }
1118
1119 bool nnfw_session::isStateFinishedRun()
1120 {
1121   if (_state == State::FINISHED_RUN)
1122   {
1123     assert(_nnpkg == nullptr);
1124     assert(!_coptions.empty());
1125     assert(_execution != nullptr);
1126     return true;
1127   }
1128   else
1129   {
1130     return false;
1131   }
1132 }
1133
1134 bool nnfw_session::isStatePreparedOrFinishedRun()
1135 {
1136   return isStatePrepared() || isStateFinishedRun();
1137 }
1138
1139 NNFW_STATUS nnfw_session::input_tensorindex(const char *tensorname, uint32_t *index)
1140 {
1141   return getTensorIndexImpl(*primary_subgraph(), tensorname, index, true);
1142 }
1143
1144 NNFW_STATUS nnfw_session::output_tensorindex(const char *tensorname, uint32_t *index)
1145 {
1146   return getTensorIndexImpl(*primary_subgraph(), tensorname, index, false);
1147 }
1148
1149 NNFW_STATUS nnfw_session::set_backends_per_operation(const char *backend_settings)
1150 {
1151   if (backend_settings == NULL)
1152     return NNFW_STATUS_ERROR;
1153
1154   if (!isStateModelLoaded())
1155     return NNFW_STATUS_INVALID_STATE;
1156
1157   // Backend for all
1158   auto &ms_options = _coptions[0]->manual_scheduler_options;
1159   ms_options.setBackendMap(std::string{backend_settings});
1160
1161   return NNFW_STATUS_NO_ERROR;
1162 }
1163
1164 #ifdef ONERT_TRAIN
1165 NNFW_STATUS nnfw_session::train_prepare(const nnfw_train_info *info)
1166 {
1167   // We may need different state to represent training model is loaded
1168   if (!isStateModelLoaded())
1169   {
1170     std::cerr << "Error during model prepare training: ";
1171     if (_state == State::PREPARED_TRAINING)
1172       std::cerr << "prepare should be run once";
1173     else
1174       std::cerr << "invalid state";
1175     std::cerr << std::endl;
1176     return NNFW_STATUS_INVALID_STATE;
1177   }
1178
1179   try
1180   {
1181     nnfw_train_info tinfo;
1182     if (info != nullptr)
1183     {
1184       tinfo = *info;
1185     }
1186
1187     auto convertLossType = [](const int &type) {
1188       if (type == NNFW_TRAIN_LOSS_MEAN_SQUARED_ERROR)
1189         return onert::ir::operation::Loss::Type::MEAN_SQUARED_ERROR;
1190       if (type == NNFW_TRAIN_LOSS_CATEGORICAL_CROSSENTROPY)
1191         return onert::ir::operation::Loss::Type::CATEGORICAL_CROSSENTROPY;
1192       else
1193         throw std::runtime_error("not supported loss type");
1194     };
1195     onert::compiler::train::LossInfo loss_info;
1196     loss_info.type = convertLossType(tinfo.loss);
1197
1198     auto convertOptType = [](const int &type) {
1199       if (type == NNFW_TRAIN_OPTIMIZER_SGD)
1200         return onert::exec::train::optimizer::OptimizerCode::SGD;
1201       else if (type == NNFW_TRAIN_OPTIMIZER_ADAM)
1202         return onert::exec::train::optimizer::OptimizerCode::Adam;
1203       else
1204         throw std::runtime_error("not supported optimizer type");
1205     };
1206     onert::compiler::train::OptimizerInfo opt_info;
1207     opt_info.learning_rate = tinfo.learning_rate;
1208     opt_info.optim_code = convertOptType(tinfo.opt);
1209
1210     onert::compiler::train::TrainingInfo training_info;
1211     training_info.setBatchSize(tinfo.batch_size);
1212     training_info.setLossInfo(loss_info);
1213     training_info.setOptimizerInfo(opt_info);
1214
1215     auto compiler =
1216       onert::compiler::CompilerFactory::get().create(_nnpkg, _coptions, &training_info);
1217     _nnpkg.reset();
1218     _compiler_artifact = compiler->compile();
1219     _execution = std::make_unique<onert::exec::Execution>(_compiler_artifact->_executors);
1220   }
1221   catch (const std::exception &e)
1222   {
1223     std::cerr << "Error during nnfw_session::train_prepare : " << e.what() << std::endl;
1224     return NNFW_STATUS_ERROR;
1225   }
1226
1227   _state = State::PREPARED_TRAINING;
1228   return NNFW_STATUS_NO_ERROR;
1229 }
1230
1231 NNFW_STATUS nnfw_session::train_input_tensorinfo(uint32_t index, nnfw_tensorinfo *ti)
1232 {
1233   if (!isStatePreparedOrFinishedTraining())
1234   {
1235     std::cerr << "Error during nnfw_session::train_input_tensorinfo : invalid state" << std::endl;
1236     return NNFW_STATUS_INVALID_STATE;
1237   }
1238
1239   // Check index is valid: [0, getInputSize())
1240
1241   // NYI
1242   (void)index;
1243   (void)ti;
1244   return NNFW_STATUS_ERROR;
1245 }
1246
1247 NNFW_STATUS nnfw_session::train_expected_tensorinfo(uint32_t index, nnfw_tensorinfo *ti)
1248 {
1249   if (!isStatePreparedOrFinishedTraining())
1250   {
1251     std::cerr << "Error during nnfw_session::train_expected_tensorinfo : invalid state"
1252               << std::endl;
1253     return NNFW_STATUS_INVALID_STATE;
1254   }
1255
1256   // Check index is valid: [0, getExpectedSize())
1257
1258   // NYI
1259   (void)index;
1260   (void)ti;
1261   return NNFW_STATUS_ERROR;
1262 }
1263
1264 NNFW_STATUS nnfw_session::train_set_input(uint32_t index, const void *input,
1265                                           const nnfw_tensorinfo *input_tensorinfo)
1266 {
1267   if (input == nullptr)
1268   {
1269     std::cerr << "Error during nnfw_session::train_set_input : input buffer is null" << std::endl;
1270     return NNFW_STATUS_UNEXPECTED_NULL;
1271   }
1272
1273   if (!isStatePreparedOrFinishedTraining())
1274   {
1275     std::cerr << "Error during nnfw_session::train_set_input : invalid state" << std::endl;
1276     return NNFW_STATUS_INVALID_STATE;
1277   }
1278
1279   if (index >= getInputSize())
1280   {
1281     std::cerr << "Error during nnfw_session::train_set_input : index is out of range" << std::endl;
1282     return NNFW_STATUS_ERROR;
1283   }
1284
1285   try
1286   {
1287     auto ind = onert::ir::IOIndex(index);
1288     auto size = _execution->getInputTotalSize(ind);
1289     if (input_tensorinfo && getBufSize(input_tensorinfo) != size)
1290     {
1291       std::cerr
1292         << "Error during nnfw_session::train_set_input : not supporeted to change tensorinfo"
1293         << std::endl;
1294       return NNFW_STATUS_ERROR;
1295     }
1296
1297     _execution->setInput(ind, input, size);
1298   }
1299   catch (const std::exception &e)
1300   {
1301     std::cerr << "Error during nnfw_session::train_set_input : " << e.what() << std::endl;
1302     return NNFW_STATUS_ERROR;
1303   }
1304
1305   return NNFW_STATUS_NO_ERROR;
1306 }
1307
1308 NNFW_STATUS nnfw_session::train_set_expected(uint32_t index, const void *expected,
1309                                              const nnfw_tensorinfo *expected_tensorinfo)
1310 {
1311   if (expected == nullptr)
1312   {
1313     std::cerr << "Error during nnfw_session::train_set_expected : expected buffer is null"
1314               << std::endl;
1315     return NNFW_STATUS_UNEXPECTED_NULL;
1316   }
1317
1318   if (!isStatePreparedOrFinishedTraining())
1319   {
1320     std::cerr << "Error during nnfw_session::train_set_expected : invalid state" << std::endl;
1321     return NNFW_STATUS_INVALID_STATE;
1322   }
1323
1324   if (index >= getOutputSize())
1325   {
1326     std::cerr << "Error during nnfw_session::train_set_expected : index is out of range"
1327               << std::endl;
1328     return NNFW_STATUS_ERROR;
1329   }
1330
1331   try
1332   {
1333     auto output_ind = onert::ir::IOIndex(index);
1334     auto size = _execution->getOutputTotalSize(output_ind);
1335     if (expected_tensorinfo && getBufSize(expected_tensorinfo) != size)
1336     {
1337       std::cerr << "Error during nnfw_session::train_set_expected : invalid tensorinfo"
1338                 << std::endl;
1339       return NNFW_STATUS_ERROR;
1340     }
1341
1342     // NOTE Find the loss input index
1343     // Input is added as many as the number of outputs.
1344     // The loss index is calculated from the value obtained by subtracting the
1345     // total output(added loss input) from the total input size.
1346     auto input_index = getInputSize() - getOutputSize() + index;
1347     auto input_ind = onert::ir::IOIndex(input_index);
1348     _execution->setInput(input_ind, expected, size);
1349   }
1350   catch (const std::exception &e)
1351   {
1352     std::cerr << "Error during nnfw_session::train_set_expected : " << e.what() << std::endl;
1353     return NNFW_STATUS_ERROR;
1354   }
1355
1356   return NNFW_STATUS_NO_ERROR;
1357 }
1358
1359 NNFW_STATUS nnfw_session::train_run(bool update_weights)
1360 {
1361   if (!isStatePreparedOrFinishedTraining())
1362   {
1363     std::cerr << "Error during nnfw_session::train_run : invalid state" << std::endl;
1364     return NNFW_STATUS_INVALID_STATE;
1365   }
1366
1367   try
1368   {
1369     if (update_weights)
1370     {
1371       _execution->train(_training_step++);
1372     }
1373     else
1374       _execution->execute();
1375   }
1376   catch (const onert::InsufficientBufferSizeException &e)
1377   {
1378     // Currently insufficient buffer always means output buffer.
1379     std::cerr << "Error during nnfw_session::train_run : " << e.what() << std::endl;
1380     return NNFW_STATUS_INSUFFICIENT_OUTPUT_SIZE;
1381   }
1382   catch (const std::exception &e)
1383   {
1384     std::cerr << "Error during nnfw_session::train_run : " << e.what() << std::endl;
1385     return NNFW_STATUS_ERROR;
1386   }
1387
1388   _state = State::FINISHED_TRAINING;
1389   return NNFW_STATUS_NO_ERROR;
1390 }
1391
1392 NNFW_STATUS nnfw_session::train_get_loss(uint32_t index, float *loss)
1393 {
1394   if (loss == nullptr)
1395   {
1396     std::cerr << "Error during nnfw_session::train_get_loss : loss is null" << std::endl;
1397     return NNFW_STATUS_UNEXPECTED_NULL;
1398   }
1399
1400   if (!isStateFinishedTraining())
1401   {
1402     std::cerr << "Error during nnfw_session::train_get_loss : invalid state" << std::endl;
1403     return NNFW_STATUS_INVALID_STATE;
1404   }
1405
1406   if (index >= getOutputSize())
1407   {
1408     std::cerr << "Error during nnfw_session::train_get_loss : index is out of range" << std::endl;
1409     return NNFW_STATUS_ERROR;
1410   }
1411
1412   try
1413   {
1414     auto ind = onert::ir::IOIndex(index);
1415     *loss = _execution->getLoss(ind);
1416   }
1417   catch (const std::exception &e)
1418   {
1419     std::cerr << "Error during nnfw_session::train_get_loss : " << e.what() << std::endl;
1420     return NNFW_STATUS_ERROR;
1421   }
1422
1423   return NNFW_STATUS_NO_ERROR;
1424 }
1425
1426 NNFW_STATUS nnfw_session::train_export_circle(const char *path)
1427 {
1428   if (path == nullptr)
1429   {
1430     std::cerr << "Error during nnfw_session::train_export_circle : path is null" << std::endl;
1431     return NNFW_STATUS_UNEXPECTED_NULL;
1432   }
1433
1434   // Check training mode is enabled
1435   if (!isStateFinishedTraining())
1436   {
1437     std::cerr << "Error during nnfw_session::train_export_circle : invalid state" << std::endl;
1438     return NNFW_STATUS_INVALID_STATE;
1439   }
1440
1441   // NYI
1442   return NNFW_STATUS_ERROR;
1443 }
1444
1445 bool nnfw_session::isStatePreparedTraining()
1446 {
1447   if (_state == State::PREPARED_TRAINING)
1448   {
1449     assert(_nnpkg == nullptr);
1450     assert(!_coptions.empty());
1451     assert(_execution != nullptr);
1452     return true;
1453   }
1454   else
1455     return false;
1456 }
1457
1458 bool nnfw_session::isStateFinishedTraining()
1459 {
1460   if (_state == State::FINISHED_TRAINING)
1461   {
1462     assert(_nnpkg == nullptr);
1463     assert(!_coptions.empty());
1464     assert(_execution != nullptr);
1465     return true;
1466   }
1467   else
1468     return false;
1469 }
1470
1471 bool nnfw_session::isStatePreparedOrFinishedTraining()
1472 {
1473   return isStatePreparedTraining() || isStateFinishedTraining();
1474 }
1475
1476 #endif // ONERT_TRAIN
1477
1478 NNFW_STATUS nnfw_session::set_quantization_type(NNFW_QUANTIZE_TYPE qtype)
1479 {
1480   try
1481   {
1482     if (!isStateModelLoaded())
1483     {
1484       std::cerr << "invalid state" << std::endl;
1485       return NNFW_STATUS_INVALID_STATE;
1486     }
1487
1488     bool is_q16 = false;
1489     switch (qtype)
1490     {
1491       case NNFW_QUANTIZE_TYPE_U8_ASYM:
1492         break;
1493       case NNFW_QUANTIZE_TYPE_I16_SYM:
1494         is_q16 = true;
1495         break;
1496       default:
1497         return NNFW_STATUS_INVALID_STATE;
1498     }
1499     _quant_manager->quantizeType(is_q16);
1500   }
1501   catch (const std::exception &e)
1502   {
1503     std::cerr << "Error during nnfw_session::set_quantization_type : " << e.what() << std::endl;
1504     return NNFW_STATUS_ERROR;
1505   }
1506
1507   return NNFW_STATUS_NO_ERROR;
1508 }
1509
1510 NNFW_STATUS nnfw_session::set_quantized_model_path(const char *path)
1511 {
1512   try
1513   {
1514     if (!isStateModelLoaded())
1515     {
1516       std::cerr << "invalid state" << std::endl;
1517       return NNFW_STATUS_INVALID_STATE;
1518     }
1519
1520     _quant_manager->exportModelPath(std::string(path));
1521   }
1522   catch (const std::exception &e)
1523   {
1524     std::cerr << "Error during nnfw_session::set_quantized_model_path : " << e.what() << std::endl;
1525     return NNFW_STATUS_ERROR;
1526   }
1527
1528   return NNFW_STATUS_NO_ERROR;
1529 }
1530
1531 NNFW_STATUS nnfw_session::quantize()
1532 {
1533   try
1534   {
1535     if (!isStateModelLoaded())
1536     {
1537       std::cerr << "invalid state" << std::endl;
1538       return NNFW_STATUS_INVALID_STATE;
1539     }
1540
1541     auto result = _quant_manager->quantize();
1542     if (!result)
1543       return NNFW_STATUS_INVALID_STATE;
1544
1545     // Replace model
1546     // TODO Support buffer replace, not file reload
1547     auto model = loadModel(_quant_manager->exportModelPath(), "circle");
1548     if (model == nullptr)
1549       return NNFW_STATUS_ERROR;
1550     _nnpkg->replaceModel(std::move(model));
1551   }
1552   catch (const std::exception &e)
1553   {
1554     std::cerr << "Error during nnfw_session::quantize : " << e.what() << std::endl;
1555     return NNFW_STATUS_ERROR;
1556   }
1557
1558   return NNFW_STATUS_NO_ERROR;
1559 }