2 * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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"
29 #include "ir/OpCode.h"
30 #include "util/TracingCtx.h"
31 #include "odc/QuantizeManager.h"
38 #include <misc/string_helpers.h>
41 * API does not accept string argument longer than max length below
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
51 // Is null-terminating in length ?
52 bool null_terminating(const char *str, uint32_t length)
54 for (uint32_t i = 0; i < length; i++)
56 if (*(str + i) == '\0')
64 onert::ir::Layout convertLayout(NNFW_LAYOUT layout)
66 if (layout == NNFW_LAYOUT_CHANNELS_LAST)
68 return onert::ir::Layout::NHWC;
70 else if (layout == NNFW_LAYOUT_CHANNELS_FIRST)
72 return onert::ir::Layout::NCHW;
74 return onert::ir::Layout::UNKNOWN;
77 NNFW_STATUS getTensorIndexImpl(const onert::ir::IGraph &graph, const char *tensorname,
78 uint32_t *index, bool is_input)
80 if (!tensorname || !index)
81 return NNFW_STATUS_UNEXPECTED_NULL;
83 if (!null_terminating(tensorname, MAX_TENSOR_NAME_LENGTH))
85 std::cerr << "nnpackage path is too long" << std::endl;
86 return NNFW_STATUS_ERROR;
89 auto ind_found = is_input ? graph.getInputIndex(tensorname) : graph.getOutputIndex(tensorname);
91 if (ind_found.undefined())
94 return NNFW_STATUS_ERROR;
98 *index = ind_found.value();
99 return NNFW_STATUS_NO_ERROR;
103 std::string trim(const std::string &value)
105 std::string whitespace = " \t";
106 auto begin = value.find_first_not_of(whitespace);
107 if (begin == std::string::npos)
108 return ""; // no content
110 auto end = value.find_last_not_of(whitespace);
111 auto range = end - begin + 1;
112 return value.substr(begin, range);
115 bool loadConfigure(const std::string cfgfile, onert::util::CfgKeyValues &keyValues)
117 std::ifstream ifs(cfgfile);
121 while (std::getline(ifs, line))
123 auto cmtpos = line.find('#');
124 if (cmtpos != std::string::npos)
126 line = line.substr(0, cmtpos);
128 std::istringstream isline(line);
130 if (std::getline(isline, key, '='))
133 if (std::getline(isline, value))
136 keyValues[key] = trim(value);
146 NNFW_TYPE datatype_to_nnfw_dtype(onert::ir::DataType dt)
148 using onert::ir::DataType;
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:
170 throw std::runtime_error("Error: Model has type that runtime API does not support.");
174 void fillTensorInfo(nnfw_tensorinfo *ti, const onert::ir::Shape &shape,
175 const onert::ir::DataType &dtype)
177 ti->rank = shape.rank();
178 for (int j = 0; j < ti->rank; ++j)
180 ti->dims[j] = shape.dim(j);
182 ti->dtype = datatype_to_nnfw_dtype(dtype);
185 std::unique_ptr<onert::ir::Model> loadModel(const std::string filename,
186 const std::string model_type)
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());
195 std::cerr << "Unsupported model type" << std::endl;
196 return std::unique_ptr<onert::ir::Model>(nullptr);
200 uint64_t getBufSize(const nnfw_tensorinfo *info)
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 */
214 for (int32_t i = 0; i < info->rank; ++i)
216 assert(info->dims[i] >= 0);
219 return elmsize[info->dtype] * n;
221 #endif // ONERT_TRAIN
224 nnfw_session::nnfw_session()
225 : _nnpkg{nullptr}, _coptions{}, _compiler_artifact{nullptr}, _execution{nullptr},
226 _kernel_registry{nullptr}, _quant_manager{nullptr}
231 NNFW_STATUS nnfw_session::create(nnfw_session **session)
233 if (session == nullptr)
234 return NNFW_STATUS_UNEXPECTED_NULL;
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();
241 catch (const std::bad_alloc &e)
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;
247 catch (const std::exception &e)
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;
253 return NNFW_STATUS_NO_ERROR;
256 nnfw_session::~nnfw_session() = default;
258 NNFW_STATUS nnfw_session::load_circle_from_buffer(uint8_t *buffer, size_t size)
260 if (!isStateInitialized())
261 return NNFW_STATUS_INVALID_STATE;
264 return NNFW_STATUS_UNEXPECTED_NULL;
267 return NNFW_STATUS_ERROR;
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;
276 catch (const std::exception &e)
278 std::cerr << "Error during model loading : " << e.what() << std::endl;
279 return NNFW_STATUS_ERROR;
281 return NNFW_STATUS_NO_ERROR;
284 NNFW_STATUS nnfw_session::load_model_from_modelfile(const char *model_file_path)
286 if (!isStateInitialized())
287 return NNFW_STATUS_INVALID_STATE;
289 if (!model_file_path)
291 std::cerr << "Model file path is null." << std::endl;
292 return NNFW_STATUS_UNEXPECTED_NULL;
295 // Create quantize manager
296 _quant_manager = std::make_unique<onert::odc::QuantizeManager>(std::string(model_file_path));
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)
303 std::cerr << "Invalid model file path. Please use file with extension." << std::endl;
304 return NNFW_STATUS_ERROR;
306 std::string model_type = filename.substr(dotidx + 1); // + 1 to exclude dot
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;
316 catch (const std::exception &e)
318 std::cerr << "Error during model loading : " << e.what() << std::endl;
319 return NNFW_STATUS_ERROR;
321 return NNFW_STATUS_NO_ERROR;
324 NNFW_STATUS nnfw_session::load_model_from_nnpackage(const char *package_dir)
326 if (!isStateInitialized())
327 return NNFW_STATUS_INVALID_STATE;
331 std::cerr << "package_dir is null." << std::endl;
332 return NNFW_STATUS_UNEXPECTED_NULL;
335 if (!null_terminating(package_dir, MAX_PATH_LENGTH))
337 std::cerr << "nnpackage path is too long" << std::endl;
338 return NNFW_STATUS_ERROR;
341 // TODO : add support for zipped package file load
343 if (!(dir = opendir(package_dir)))
345 std::cerr << "invalid nnpackge directory: " << package_dir << std::endl;
346 return NNFW_STATUS_ERROR;
352 std::string package_path(package_dir);
353 std::string manifest_file_name = package_path + "/metadata/MANIFEST";
354 std::ifstream mfs(manifest_file_name);
356 // extract the filename of the first(index 0) model
357 // e.g. In MANIFEST file, { "models" : [ "firstmodel.tflite", "2nd.tflite" ] }
360 const Json::Value &models = root["models"];
361 const Json::Value &model_types = root["model-types"];
362 const Json::Value &configs = root["configs"];
364 if (!configs.empty() && !configs[0].empty())
366 auto filepath = package_path + std::string("/metadata/") + configs[0].asString();
368 onert::util::CfgKeyValues keyValues;
369 if (loadConfigure(filepath, keyValues))
371 onert::util::setConfigKeyValues(keyValues);
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())
378 std::cerr << "Invalid model size - " << std::to_string(num_models) << std::endl;
379 return NNFW_STATUS_ERROR;
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);
387 for (uint16_t i = 0; i < num_models; ++i)
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());
399 auto toIODesc = [](std::string str) {
400 auto indices = nnfw::misc::split(str, ':');
401 if (indices.size() != 3)
403 std::cerr << "IODesc should be 3-tuple." << std::endl;
404 return onert::ir::IODesc{};
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};
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)
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()));
428 _state = State::MODEL_LOADED;
430 catch (const std::exception &e)
432 std::cerr << "Error during model loading : " << e.what() << std::endl;
433 return NNFW_STATUS_ERROR;
435 return NNFW_STATUS_NO_ERROR;
438 NNFW_STATUS nnfw_session::prepare()
440 // NOTE. If users want to run prepare() more than one time, this could be removed.
441 if (!isStateModelLoaded())
443 std::cerr << "Error during model prepare : ";
444 if (isStateInitialized())
446 std::cerr << "prepare should be run once";
450 std::cerr << "invalid state";
452 std::cerr << std::endl;
453 return NNFW_STATUS_INVALID_STATE;
458 auto compiler = onert::compiler::CompilerFactory::get().create(_nnpkg, _coptions);
460 _compiler_artifact = compiler->compile();
461 _execution = std::make_unique<onert::exec::Execution>(_compiler_artifact->_executors);
463 catch (const std::exception &e)
465 std::cerr << "Error during model prepare : " << e.what() << std::endl;
466 return NNFW_STATUS_ERROR;
469 _state = State::PREPARED;
470 return NNFW_STATUS_NO_ERROR;
473 NNFW_STATUS nnfw_session::prepare_pipeline(const char *)
475 std::cerr << "Pipeline prepare_pipeline: deprecated feature " << std::endl;
476 return NNFW_STATUS_ERROR;
479 NNFW_STATUS nnfw_session::run()
481 if (!isStatePreparedOrFinishedRun())
483 std::cerr << "Error during nnfw_session::run : "
484 << "run should be run after prepare" << std::endl;
485 return NNFW_STATUS_INVALID_STATE;
490 _execution->execute();
492 catch (const onert::InsufficientBufferSizeException &e)
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;
498 catch (const std::exception &e)
500 std::cerr << "Error during nnfw_session::run : " << e.what() << std::endl;
501 return NNFW_STATUS_ERROR;
504 _state = State::FINISHED_RUN;
505 return NNFW_STATUS_NO_ERROR;
508 NNFW_STATUS nnfw_session::run_async()
510 if (!isStatePreparedOrFinishedRun())
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;
517 _execution->startExecute();
519 _state = State::RUNNING;
520 return NNFW_STATUS_NO_ERROR;
523 NNFW_STATUS nnfw_session::await()
525 if (!isStateRunning())
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;
532 _execution->waitFinish();
534 _state = State::FINISHED_RUN;
535 return NNFW_STATUS_NO_ERROR;
538 NNFW_STATUS nnfw_session::set_input(uint32_t index, NNFW_TYPE /*type*/, const void *buffer,
541 if (!isStatePreparedOrFinishedRun())
543 std::cerr << "Error during nnfw_session::set_input : invalid state" << std::endl;
544 return NNFW_STATUS_INVALID_STATE;
547 if (!buffer && length != 0)
550 << "Error during nnfw_session::set_input : given buffer is NULL but the length is not 0"
552 return NNFW_STATUS_ERROR;
557 _execution->setInput(onert::ir::IOIndex(index), buffer, length);
559 catch (const std::exception &e)
561 std::cerr << "Error during nnfw_session::set_input : " << e.what() << std::endl;
562 return NNFW_STATUS_ERROR;
564 return NNFW_STATUS_NO_ERROR;
567 NNFW_STATUS nnfw_session::set_output(uint32_t index, NNFW_TYPE /*type*/, void *buffer,
570 if (!isStatePreparedOrFinishedRun())
572 std::cerr << "Error during nnfw_session::set_output : invalid state" << std::endl;
573 return NNFW_STATUS_INVALID_STATE;
576 if (!buffer && length != 0)
579 << "Error during nnfw_session::set_output : given buffer is NULL but the length is not 0"
581 return NNFW_STATUS_ERROR;
586 _execution->setOutput(onert::ir::IOIndex(index), buffer, length);
588 catch (const std::exception &e)
590 std::cerr << "Error during nnfw_session::set_output : " << e.what() << std::endl;
591 return NNFW_STATUS_ERROR;
593 return NNFW_STATUS_NO_ERROR;
596 NNFW_STATUS nnfw_session::input_size(uint32_t *number)
598 if (isStateInitialized()) // Model is not loaded
599 return NNFW_STATUS_INVALID_STATE;
603 if (number == nullptr)
605 std::cerr << "Error during nnfw_session::input_size, number is null pointer." << std::endl;
606 return NNFW_STATUS_UNEXPECTED_NULL;
608 *number = getInputSize();
610 catch (const std::exception &e)
612 std::cerr << "Error during nnfw_session::input_size : " << e.what() << std::endl;
613 return NNFW_STATUS_ERROR;
615 return NNFW_STATUS_NO_ERROR;
618 NNFW_STATUS nnfw_session::output_size(uint32_t *number)
620 if (isStateInitialized()) // Model is not loaded
621 return NNFW_STATUS_INVALID_STATE;
625 if (number == nullptr)
627 std::cerr << "Error during nnfw_session::output_size, number is null pointer." << std::endl;
628 return NNFW_STATUS_UNEXPECTED_NULL;
630 *number = getOutputSize();
632 catch (const std::exception &e)
634 std::cerr << "Error during nnfw_session::output_size" << e.what() << std::endl;
635 return NNFW_STATUS_ERROR;
637 return NNFW_STATUS_NO_ERROR;
640 NNFW_STATUS nnfw_session::set_input_layout(uint32_t index, NNFW_LAYOUT layout)
642 if (!isStatePreparedOrFinishedRun())
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;
651 if (layout != NNFW_LAYOUT_NONE && layout != NNFW_LAYOUT_CHANNELS_FIRST &&
652 layout != NNFW_LAYOUT_CHANNELS_LAST)
654 std::cerr << "Error during nnfw_session::set_input_layout, not supported layout" << std::endl;
655 return NNFW_STATUS_ERROR;
658 _execution->setInputLayout(onert::ir::IOIndex(index), convertLayout(layout));
660 catch (const std::exception &e)
662 std::cerr << "Error during nnfw_session::set_input_layout : " << e.what() << std::endl;
663 return NNFW_STATUS_ERROR;
665 return NNFW_STATUS_NO_ERROR;
668 NNFW_STATUS nnfw_session::set_output_layout(uint32_t index, NNFW_LAYOUT layout)
670 if (!isStatePreparedOrFinishedRun())
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;
679 if (layout != NNFW_LAYOUT_NONE && layout != NNFW_LAYOUT_CHANNELS_FIRST &&
680 layout != NNFW_LAYOUT_CHANNELS_LAST)
682 std::cerr << "Error during nnfw_session::set_output_layout, not supported layout"
684 return NNFW_STATUS_ERROR;
687 _execution->setOutputLayout(onert::ir::IOIndex(index), convertLayout(layout));
689 catch (const std::exception &e)
691 std::cerr << "Error during nnfw_session::set_output_layout : " << e.what() << std::endl;
692 return NNFW_STATUS_ERROR;
694 return NNFW_STATUS_NO_ERROR;
697 NNFW_STATUS nnfw_session::apply_tensorinfo(uint32_t index, nnfw_tensorinfo ti)
701 if (isStateInitialized())
703 std::cerr << "Error during set_input_tensorinfo : should be run after load_model"
705 return NNFW_STATUS_INVALID_STATE;
708 if (ti.rank <= 0 || ti.rank > NNFW_MAX_RANK)
710 std::cerr << "unsupported rank: " << ti.rank << std::endl;
711 return NNFW_STATUS_ERROR;
714 for (int32_t i = 0; i < ti.rank; ++i)
718 std::cerr << "dim must be positive integer but was " << ti.dims[i] << std::endl;
719 return NNFW_STATUS_ERROR;
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];
728 if (!isStatePreparedOrFinishedRun())
731 // In this case, if we apply input shape, it will propagate after compilation and excution
732 _nnpkg->changeInputShape(index, new_shape);
734 else // when called after nnfw_session::prepare()
735 _execution->changeInputShape(onert::ir::IOIndex(index), new_shape);
737 return NNFW_STATUS_NO_ERROR;
740 NNFW_STATUS nnfw_session::set_input_tensorinfo(uint32_t index, const nnfw_tensorinfo *ti)
742 nnfw_tensorinfo ti_copy = *ti;
743 return apply_tensorinfo(index, ti_copy);
746 NNFW_STATUS nnfw_session::input_tensorinfo(uint32_t index, nnfw_tensorinfo *ti)
748 if (isStateInitialized())
749 return NNFW_STATUS_INVALID_STATE;
755 std::cerr << "Error during nnfw_session::input_tensorinfo, tensorinfo is null pointer."
757 return NNFW_STATUS_UNEXPECTED_NULL;
760 if (index >= getInputSize())
762 std::cerr << "Error during nnfw_session::input_tensorinfo, index is out of range."
764 return NNFW_STATUS_ERROR;
767 if (isStateModelLoaded())
769 auto info = _nnpkg->inputInfo(index);
770 fillTensorInfo(ti, info.shape(), info.typeInfo().type());
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);
780 catch (const std::exception &e)
782 std::cerr << "Error during nnfw_session::input_tensorinfo : " << e.what() << std::endl;
783 return NNFW_STATUS_ERROR;
785 return NNFW_STATUS_NO_ERROR;
788 NNFW_STATUS nnfw_session::output_tensorinfo(uint32_t index, nnfw_tensorinfo *ti)
790 if (isStateInitialized())
791 return NNFW_STATUS_INVALID_STATE;
795 std::cerr << "Error during nnfw_session::output_tensorinfo, tensorinfo is null pointer."
797 return NNFW_STATUS_UNEXPECTED_NULL;
802 if (index >= getOutputSize())
804 std::cerr << "Error during nnfw_session::output_tensorinfo, index is out of range."
806 return NNFW_STATUS_ERROR;
809 if (isStateModelLoaded())
811 auto info = _nnpkg->outputInfo(index);
812 fillTensorInfo(ti, info.shape(), info.typeInfo().type());
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);
822 catch (const std::exception &e)
824 std::cerr << "Error during nnfw_session::output_tensorinfo : " << e.what() << std::endl;
825 return NNFW_STATUS_ERROR;
828 return NNFW_STATUS_NO_ERROR;
831 NNFW_STATUS nnfw_session::push_pipeline_input(std::vector<void *> *, std::vector<uint32_t> *)
833 std::cerr << "Pipeline push_pipeline_input: deprecated feature " << std::endl;
834 return NNFW_STATUS_ERROR;
837 NNFW_STATUS nnfw_session::pop_pipeline_output(std::vector<void *> *)
839 std::cerr << "Pipeline pop_pipeline_output: deprecated feature " << std::endl;
840 return NNFW_STATUS_ERROR;
843 NNFW_STATUS nnfw_session::register_custom_operation(const std::string &id,
844 nnfw_custom_eval eval_func)
846 _kernel_registry->registerKernel(id, eval_func);
847 return NNFW_STATUS_NO_ERROR;
850 static std::string get_op_backend_string(std::string op)
852 #define MAP_MACRO(CircleName, OneRTName) {#CircleName, #OneRTName},
854 static std::unordered_map<std::string, std::string> operation_map = {
860 auto n = operation_map.find(op);
862 if (n == operation_map.end())
864 // this return value is handled by a caller to return error code
865 return std::string("");
873 NNFW_STATUS nnfw_session::set_available_backends(const char *backends)
875 if (!isStateModelLoaded())
876 return NNFW_STATUS_INVALID_STATE;
881 return NNFW_STATUS_UNEXPECTED_NULL;
882 if (null_terminating(backends, MAX_BACKEND_NAME_LENGTH) == false)
883 return NNFW_STATUS_ERROR;
885 auto &options = *_coptions[0];
887 using namespace onert::util;
889 options.backend_list = nnfw::misc::split(std::string{backends}, ';');
891 catch (const std::exception &e)
893 std::cerr << "Error during nnfw_session::set_available_backends : " << e.what() << std::endl;
894 return NNFW_STATUS_ERROR;
896 return NNFW_STATUS_NO_ERROR;
899 NNFW_STATUS nnfw_session::set_op_backend(const char *op, const char *backend)
901 if (!isStateModelLoaded())
902 return NNFW_STATUS_INVALID_STATE;
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;
912 auto key = get_op_backend_string(op);
916 return NNFW_STATUS_ERROR;
919 auto &opcode_to_backend = _coptions[0]->manual_scheduler_options.opcode_to_backend;
920 opcode_to_backend.emplace(onert::ir::toOpCode(key), backend);
922 catch (const std::exception &e)
924 std::cerr << "Error during nnfw_session::set_op_backend : " << e.what() << std::endl;
925 return NNFW_STATUS_ERROR;
927 return NNFW_STATUS_NO_ERROR;
930 NNFW_STATUS nnfw_session::set_config(const char *key, const char *value)
932 if (!isStateModelLoaded())
933 return NNFW_STATUS_INVALID_STATE;
936 return NNFW_STATUS_UNEXPECTED_NULL;
938 auto &options = *_coptions[0];
940 using namespace onert::util;
942 const std::string skey = key;
944 if (skey == config::TRACE_FILEPATH)
946 options.trace_filepath = value;
948 else if (skey == config::GRAPH_DOT_DUMP)
950 options.graph_dump_level = toInt(value);
952 else if (skey == config::EXECUTOR)
954 options.executor = value;
956 else if (skey == config::OP_BACKEND_ALLOPS)
958 options.manual_scheduler_options.backend_for_all = value;
960 else if (skey == config::USE_SCHEDULER)
962 options.he_scheduler = toBool(value);
964 else if (skey == config::PROFILING_MODE)
966 options.he_profiling_mode = toBool(value);
970 return NNFW_STATUS_ERROR;
972 return NNFW_STATUS_NO_ERROR;
975 const onert::ir::IGraph *nnfw_session::primary_subgraph()
977 if (_nnpkg != nullptr)
979 assert(_execution == nullptr);
980 return _nnpkg->primary_model()->primary_subgraph().get();
984 assert(_execution != nullptr);
985 // We assumed the graph will not change after compilation, but shape could change
986 return &_execution->primary_subgraph();
990 uint32_t nnfw_session::getInputSize()
992 if (isStateInitialized())
993 throw std::runtime_error{"Model is not loaded yet"};
995 if (isStateModelLoaded())
996 return _nnpkg->inputSize();
998 // Session is prepared (general inference)
999 return _compiler_artifact->_executors->inputSize();
1002 uint32_t nnfw_session::getOutputSize()
1004 if (isStateInitialized())
1005 throw std::runtime_error{"Model is not loaded yet"};
1007 if (isStateModelLoaded())
1008 return _nnpkg->outputSize();
1010 // Session is prepared (general inference)
1011 return _compiler_artifact->_executors->outputSize();
1014 NNFW_STATUS nnfw_session::get_config(const char *key, char *value, size_t value_size)
1016 if (!isStateModelLoaded())
1017 return NNFW_STATUS_INVALID_STATE;
1020 return NNFW_STATUS_UNEXPECTED_NULL;
1022 auto &options = *_coptions[0];
1024 auto check_boundary = [](size_t dest_size, std::string &src) {
1025 if (dest_size < src.length() + 1 /* for '\0' */)
1027 std::cerr << "buffer is small to copy config value." << std::endl;
1033 const std::string skey = key;
1035 if (skey == onert::util::config::BACKENDS)
1037 if (options.backend_list.size() == 0)
1038 return NNFW_STATUS_NO_ERROR; // no setting backend is not an error of get_config_str()
1040 auto str = nnfw::misc::join(options.backend_list.begin(), options.backend_list.end(), ";");
1042 if (!check_boundary(value_size, str))
1043 return NNFW_STATUS_ERROR;
1045 strncpy(value, str.c_str(), value_size);
1047 else if (skey == onert::util::config::EXECUTOR)
1049 if (!check_boundary(value_size, options.executor))
1050 return NNFW_STATUS_ERROR;
1052 strncpy(value, options.executor.c_str(), options.executor.length());
1056 return NNFW_STATUS_ERROR;
1059 return NNFW_STATUS_NO_ERROR;
1062 bool nnfw_session::isStateInitialized()
1064 if (_state == State::INITIALIZED)
1066 assert(_nnpkg == nullptr);
1067 assert(_coptions.empty());
1068 assert(_execution == nullptr);
1077 bool nnfw_session::isStateModelLoaded()
1079 if (_state == State::MODEL_LOADED)
1081 assert(_nnpkg != nullptr);
1082 assert(!_coptions.empty());
1083 assert(_execution == nullptr);
1092 bool nnfw_session::isStatePrepared()
1094 if (_state == State::PREPARED)
1096 assert(_nnpkg == nullptr);
1097 assert(!_coptions.empty());
1098 assert(_execution != nullptr);
1107 bool nnfw_session::isStateRunning()
1109 if (_state == State::RUNNING)
1111 assert(_nnpkg == nullptr);
1112 assert(!_coptions.empty());
1113 assert(_execution != nullptr);
1119 bool nnfw_session::isStateFinishedRun()
1121 if (_state == State::FINISHED_RUN)
1123 assert(_nnpkg == nullptr);
1124 assert(!_coptions.empty());
1125 assert(_execution != nullptr);
1134 bool nnfw_session::isStatePreparedOrFinishedRun()
1136 return isStatePrepared() || isStateFinishedRun();
1139 NNFW_STATUS nnfw_session::input_tensorindex(const char *tensorname, uint32_t *index)
1141 return getTensorIndexImpl(*primary_subgraph(), tensorname, index, true);
1144 NNFW_STATUS nnfw_session::output_tensorindex(const char *tensorname, uint32_t *index)
1146 return getTensorIndexImpl(*primary_subgraph(), tensorname, index, false);
1149 NNFW_STATUS nnfw_session::set_backends_per_operation(const char *backend_settings)
1151 if (backend_settings == NULL)
1152 return NNFW_STATUS_ERROR;
1154 if (!isStateModelLoaded())
1155 return NNFW_STATUS_INVALID_STATE;
1158 auto &ms_options = _coptions[0]->manual_scheduler_options;
1159 ms_options.setBackendMap(std::string{backend_settings});
1161 return NNFW_STATUS_NO_ERROR;
1165 NNFW_STATUS nnfw_session::train_prepare(const nnfw_train_info *info)
1167 // We may need different state to represent training model is loaded
1168 if (!isStateModelLoaded())
1170 std::cerr << "Error during model prepare training: ";
1171 if (_state == State::PREPARED_TRAINING)
1172 std::cerr << "prepare should be run once";
1174 std::cerr << "invalid state";
1175 std::cerr << std::endl;
1176 return NNFW_STATUS_INVALID_STATE;
1181 nnfw_train_info tinfo;
1182 if (info != nullptr)
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;
1193 throw std::runtime_error("not supported loss type");
1195 onert::compiler::train::LossInfo loss_info;
1196 loss_info.type = convertLossType(tinfo.loss);
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;
1204 throw std::runtime_error("not supported optimizer type");
1206 onert::compiler::train::OptimizerInfo opt_info;
1207 opt_info.learning_rate = tinfo.learning_rate;
1208 opt_info.optim_code = convertOptType(tinfo.opt);
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);
1216 onert::compiler::CompilerFactory::get().create(_nnpkg, _coptions, &training_info);
1218 _compiler_artifact = compiler->compile();
1219 _execution = std::make_unique<onert::exec::Execution>(_compiler_artifact->_executors);
1221 catch (const std::exception &e)
1223 std::cerr << "Error during nnfw_session::train_prepare : " << e.what() << std::endl;
1224 return NNFW_STATUS_ERROR;
1227 _state = State::PREPARED_TRAINING;
1228 return NNFW_STATUS_NO_ERROR;
1231 NNFW_STATUS nnfw_session::train_input_tensorinfo(uint32_t index, nnfw_tensorinfo *ti)
1233 if (!isStatePreparedOrFinishedTraining())
1235 std::cerr << "Error during nnfw_session::train_input_tensorinfo : invalid state" << std::endl;
1236 return NNFW_STATUS_INVALID_STATE;
1239 // Check index is valid: [0, getInputSize())
1244 return NNFW_STATUS_ERROR;
1247 NNFW_STATUS nnfw_session::train_expected_tensorinfo(uint32_t index, nnfw_tensorinfo *ti)
1249 if (!isStatePreparedOrFinishedTraining())
1251 std::cerr << "Error during nnfw_session::train_expected_tensorinfo : invalid state"
1253 return NNFW_STATUS_INVALID_STATE;
1256 // Check index is valid: [0, getExpectedSize())
1261 return NNFW_STATUS_ERROR;
1264 NNFW_STATUS nnfw_session::train_set_input(uint32_t index, const void *input,
1265 const nnfw_tensorinfo *input_tensorinfo)
1267 if (input == nullptr)
1269 std::cerr << "Error during nnfw_session::train_set_input : input buffer is null" << std::endl;
1270 return NNFW_STATUS_UNEXPECTED_NULL;
1273 if (!isStatePreparedOrFinishedTraining())
1275 std::cerr << "Error during nnfw_session::train_set_input : invalid state" << std::endl;
1276 return NNFW_STATUS_INVALID_STATE;
1279 if (index >= getInputSize())
1281 std::cerr << "Error during nnfw_session::train_set_input : index is out of range" << std::endl;
1282 return NNFW_STATUS_ERROR;
1287 auto ind = onert::ir::IOIndex(index);
1288 auto size = _execution->getInputTotalSize(ind);
1289 if (input_tensorinfo && getBufSize(input_tensorinfo) != size)
1292 << "Error during nnfw_session::train_set_input : not supporeted to change tensorinfo"
1294 return NNFW_STATUS_ERROR;
1297 _execution->setInput(ind, input, size);
1299 catch (const std::exception &e)
1301 std::cerr << "Error during nnfw_session::train_set_input : " << e.what() << std::endl;
1302 return NNFW_STATUS_ERROR;
1305 return NNFW_STATUS_NO_ERROR;
1308 NNFW_STATUS nnfw_session::train_set_expected(uint32_t index, const void *expected,
1309 const nnfw_tensorinfo *expected_tensorinfo)
1311 if (expected == nullptr)
1313 std::cerr << "Error during nnfw_session::train_set_expected : expected buffer is null"
1315 return NNFW_STATUS_UNEXPECTED_NULL;
1318 if (!isStatePreparedOrFinishedTraining())
1320 std::cerr << "Error during nnfw_session::train_set_expected : invalid state" << std::endl;
1321 return NNFW_STATUS_INVALID_STATE;
1324 if (index >= getOutputSize())
1326 std::cerr << "Error during nnfw_session::train_set_expected : index is out of range"
1328 return NNFW_STATUS_ERROR;
1333 auto output_ind = onert::ir::IOIndex(index);
1334 auto size = _execution->getOutputTotalSize(output_ind);
1335 if (expected_tensorinfo && getBufSize(expected_tensorinfo) != size)
1337 std::cerr << "Error during nnfw_session::train_set_expected : invalid tensorinfo"
1339 return NNFW_STATUS_ERROR;
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);
1350 catch (const std::exception &e)
1352 std::cerr << "Error during nnfw_session::train_set_expected : " << e.what() << std::endl;
1353 return NNFW_STATUS_ERROR;
1356 return NNFW_STATUS_NO_ERROR;
1359 NNFW_STATUS nnfw_session::train_run(bool update_weights)
1361 if (!isStatePreparedOrFinishedTraining())
1363 std::cerr << "Error during nnfw_session::train_run : invalid state" << std::endl;
1364 return NNFW_STATUS_INVALID_STATE;
1371 _execution->train(_training_step++);
1374 _execution->execute();
1376 catch (const onert::InsufficientBufferSizeException &e)
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;
1382 catch (const std::exception &e)
1384 std::cerr << "Error during nnfw_session::train_run : " << e.what() << std::endl;
1385 return NNFW_STATUS_ERROR;
1388 _state = State::FINISHED_TRAINING;
1389 return NNFW_STATUS_NO_ERROR;
1392 NNFW_STATUS nnfw_session::train_get_loss(uint32_t index, float *loss)
1394 if (loss == nullptr)
1396 std::cerr << "Error during nnfw_session::train_get_loss : loss is null" << std::endl;
1397 return NNFW_STATUS_UNEXPECTED_NULL;
1400 if (!isStateFinishedTraining())
1402 std::cerr << "Error during nnfw_session::train_get_loss : invalid state" << std::endl;
1403 return NNFW_STATUS_INVALID_STATE;
1406 if (index >= getOutputSize())
1408 std::cerr << "Error during nnfw_session::train_get_loss : index is out of range" << std::endl;
1409 return NNFW_STATUS_ERROR;
1414 auto ind = onert::ir::IOIndex(index);
1415 *loss = _execution->getLoss(ind);
1417 catch (const std::exception &e)
1419 std::cerr << "Error during nnfw_session::train_get_loss : " << e.what() << std::endl;
1420 return NNFW_STATUS_ERROR;
1423 return NNFW_STATUS_NO_ERROR;
1426 NNFW_STATUS nnfw_session::train_export_circle(const char *path)
1428 if (path == nullptr)
1430 std::cerr << "Error during nnfw_session::train_export_circle : path is null" << std::endl;
1431 return NNFW_STATUS_UNEXPECTED_NULL;
1434 // Check training mode is enabled
1435 if (!isStateFinishedTraining())
1437 std::cerr << "Error during nnfw_session::train_export_circle : invalid state" << std::endl;
1438 return NNFW_STATUS_INVALID_STATE;
1442 return NNFW_STATUS_ERROR;
1445 bool nnfw_session::isStatePreparedTraining()
1447 if (_state == State::PREPARED_TRAINING)
1449 assert(_nnpkg == nullptr);
1450 assert(!_coptions.empty());
1451 assert(_execution != nullptr);
1458 bool nnfw_session::isStateFinishedTraining()
1460 if (_state == State::FINISHED_TRAINING)
1462 assert(_nnpkg == nullptr);
1463 assert(!_coptions.empty());
1464 assert(_execution != nullptr);
1471 bool nnfw_session::isStatePreparedOrFinishedTraining()
1473 return isStatePreparedTraining() || isStateFinishedTraining();
1476 #endif // ONERT_TRAIN
1478 NNFW_STATUS nnfw_session::set_quantization_type(NNFW_QUANTIZE_TYPE qtype)
1482 if (!isStateModelLoaded())
1484 std::cerr << "invalid state" << std::endl;
1485 return NNFW_STATUS_INVALID_STATE;
1488 bool is_q16 = false;
1491 case NNFW_QUANTIZE_TYPE_U8_ASYM:
1493 case NNFW_QUANTIZE_TYPE_I16_SYM:
1497 return NNFW_STATUS_INVALID_STATE;
1499 _quant_manager->quantizeType(is_q16);
1501 catch (const std::exception &e)
1503 std::cerr << "Error during nnfw_session::set_quantization_type : " << e.what() << std::endl;
1504 return NNFW_STATUS_ERROR;
1507 return NNFW_STATUS_NO_ERROR;
1510 NNFW_STATUS nnfw_session::set_quantized_model_path(const char *path)
1514 if (!isStateModelLoaded())
1516 std::cerr << "invalid state" << std::endl;
1517 return NNFW_STATUS_INVALID_STATE;
1520 _quant_manager->exportModelPath(std::string(path));
1522 catch (const std::exception &e)
1524 std::cerr << "Error during nnfw_session::set_quantized_model_path : " << e.what() << std::endl;
1525 return NNFW_STATUS_ERROR;
1528 return NNFW_STATUS_NO_ERROR;
1531 NNFW_STATUS nnfw_session::quantize()
1535 if (!isStateModelLoaded())
1537 std::cerr << "invalid state" << std::endl;
1538 return NNFW_STATUS_INVALID_STATE;
1541 auto result = _quant_manager->quantize();
1543 return NNFW_STATUS_INVALID_STATE;
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));
1552 catch (const std::exception &e)
1554 std::cerr << "Error during nnfw_session::quantize : " << e.what() << std::endl;
1555 return NNFW_STATUS_ERROR;
1558 return NNFW_STATUS_NO_ERROR;