Imported Upstream version 1.25.0
[platform/core/ml/nnfw.git] / runtime / onert / api / src / nnfw_api_internal.cc
index 8eedb53..fc02a92 100644 (file)
@@ -28,6 +28,7 @@
 #include "ir/NNPkg.h"
 #include "ir/OpCode.h"
 #include "util/TracingCtx.h"
+#include "odc/QuantizeManager.h"
 
 #include <fstream>
 #include <iostream>
@@ -73,7 +74,7 @@ onert::ir::Layout convertLayout(NNFW_LAYOUT layout)
   return onert::ir::Layout::UNKNOWN;
 }
 
-NNFW_STATUS getTensorIndexImpl(const onert::ir::Graph &graph, const char *tensorname,
+NNFW_STATUS getTensorIndexImpl(const onert::ir::IGraph &graph, const char *tensorname,
                                uint32_t *index, bool is_input)
 {
   if (!tensorname || !index)
@@ -195,11 +196,34 @@ std::unique_ptr<onert::ir::Model> loadModel(const std::string filename,
   return std::unique_ptr<onert::ir::Model>(nullptr);
 }
 
+#ifdef ONERT_TRAIN
+uint64_t getBufSize(const nnfw_tensorinfo *info)
+{
+  static int elmsize[] = {
+    sizeof(float),   /* NNFW_TYPE_TENSOR_FLOAT32 = 0 */
+    sizeof(int),     /* NNFW_TYPE_TENSOR_INT32 = 1 */
+    sizeof(uint8_t), /* NNFW_TYPE_TENSOR_QUANT8_ASYMM = 2 */
+    sizeof(bool),    /* NNFW_TYPE_TENSOR_BOOL = 3 */
+    sizeof(uint8_t), /* NNFW_TYPE_TENSOR_UINT8 = 4 */
+    sizeof(int64_t), /* NNFW_TYPE_TENSOR_INT64 = 5 */
+    sizeof(int8_t),  /* NNFW_TYPE_TENSOR_QUANT8_ASYMM_SIGNED = 6 */
+    sizeof(int16_t), /* NNFW_TYPE_TENSOR_QUANT16_SYMM_SIGNED = 7 */
+  };
+
+  uint64_t n = 1;
+  for (int32_t i = 0; i < info->rank; ++i)
+  {
+    assert(info->dims[i] >= 0);
+    n *= info->dims[i];
+  }
+  return elmsize[info->dtype] * n;
+}
+#endif // ONERT_TRAIN
 } // namespace
 
 nnfw_session::nnfw_session()
   : _nnpkg{nullptr}, _coptions{}, _compiler_artifact{nullptr}, _execution{nullptr},
-    _kernel_registry{nullptr}
+    _kernel_registry{nullptr}, _quant_manager{nullptr}
 {
   // DO NOTHING
 }
@@ -268,6 +292,9 @@ NNFW_STATUS nnfw_session::load_model_from_modelfile(const char *model_file_path)
     return NNFW_STATUS_UNEXPECTED_NULL;
   }
 
+  // Create quantize manager
+  _quant_manager = std::make_unique<onert::odc::QuantizeManager>(std::string(model_file_path));
+
   std::string filename{model_file_path};
   // TODO: Use std::filesystem::path when we can use c++17.
   auto dotidx = filename.find_last_of('.');
@@ -352,6 +379,11 @@ NNFW_STATUS nnfw_session::load_model_from_nnpackage(const char *package_dir)
       return NNFW_STATUS_ERROR;
     }
 
+    // Create quantize manager
+    // TODO Support multiple models
+    auto const model_filename = package_path + std::string("/") + models[0].asString();
+    _quant_manager = std::make_unique<onert::odc::QuantizeManager>(model_filename);
+
     for (uint16_t i = 0; i < num_models; ++i)
     {
       auto model_file_path = package_path + std::string("/") + models[i].asString();
@@ -359,7 +391,7 @@ NNFW_STATUS nnfw_session::load_model_from_nnpackage(const char *package_dir)
       auto model = loadModel(model_file_path, model_type);
       if (model == nullptr)
         return NNFW_STATUS_ERROR;
-      model->primary_subgraph()->bindKernelBuilder(_kernel_registry->getBuilder());
+      model->bindKernelBuilder(_kernel_registry->getBuilder());
       _nnpkg->push(onert::ir::ModelIndex{i}, std::move(model));
       _coptions.push_back(onert::compiler::CompilerOptions::fromGlobalConfig());
     }
@@ -697,8 +729,7 @@ NNFW_STATUS nnfw_session::apply_tensorinfo(uint32_t index, nnfw_tensorinfo ti)
   {
 
     // In this case, if we apply input shape, it will propagate after compilation and excution
-    auto &info = _nnpkg->inputInfo(index);
-    info.shape(new_shape);
+    _nnpkg->changeInputShape(index, new_shape);
   }
   else // when called after nnfw_session::prepare()
     _execution->changeInputShape(onert::ir::IOIndex(index), new_shape);
@@ -941,7 +972,7 @@ NNFW_STATUS nnfw_session::set_config(const char *key, const char *value)
   return NNFW_STATUS_NO_ERROR;
 }
 
-const onert::ir::Graph *nnfw_session::primary_subgraph()
+const onert::ir::IGraph *nnfw_session::primary_subgraph()
 {
   if (_nnpkg != nullptr)
   {
@@ -1129,3 +1160,400 @@ NNFW_STATUS nnfw_session::set_backends_per_operation(const char *backend_setting
 
   return NNFW_STATUS_NO_ERROR;
 }
+
+#ifdef ONERT_TRAIN
+NNFW_STATUS nnfw_session::train_prepare(const nnfw_train_info *info)
+{
+  // We may need different state to represent training model is loaded
+  if (!isStateModelLoaded())
+  {
+    std::cerr << "Error during model prepare training: ";
+    if (_state == State::PREPARED_TRAINING)
+      std::cerr << "prepare should be run once";
+    else
+      std::cerr << "invalid state";
+    std::cerr << std::endl;
+    return NNFW_STATUS_INVALID_STATE;
+  }
+
+  try
+  {
+    nnfw_train_info tinfo;
+    if (info != nullptr)
+    {
+      tinfo = *info;
+    }
+
+    auto convertLossType = [](const int &type) {
+      if (type == NNFW_TRAIN_LOSS_MEAN_SQUARED_ERROR)
+        return onert::ir::operation::Loss::Type::MEAN_SQUARED_ERROR;
+      if (type == NNFW_TRAIN_LOSS_CATEGORICAL_CROSSENTROPY)
+        return onert::ir::operation::Loss::Type::CATEGORICAL_CROSSENTROPY;
+      else
+        throw std::runtime_error("not supported loss type");
+    };
+    onert::compiler::train::LossInfo loss_info;
+    loss_info.type = convertLossType(tinfo.loss);
+
+    auto convertOptType = [](const int &type) {
+      if (type == NNFW_TRAIN_OPTIMIZER_SGD)
+        return onert::exec::train::optimizer::OptimizerCode::SGD;
+      else if (type == NNFW_TRAIN_OPTIMIZER_ADAM)
+        return onert::exec::train::optimizer::OptimizerCode::Adam;
+      else
+        throw std::runtime_error("not supported optimizer type");
+    };
+    onert::compiler::train::OptimizerInfo opt_info;
+    opt_info.learning_rate = tinfo.learning_rate;
+    opt_info.optim_code = convertOptType(tinfo.opt);
+
+    onert::compiler::train::TrainingInfo training_info;
+    training_info.setBatchSize(tinfo.batch_size);
+    training_info.setLossInfo(loss_info);
+    training_info.setOptimizerInfo(opt_info);
+
+    auto compiler =
+      onert::compiler::CompilerFactory::get().create(_nnpkg, _coptions, &training_info);
+    _nnpkg.reset();
+    _compiler_artifact = compiler->compile();
+    _execution = std::make_unique<onert::exec::Execution>(_compiler_artifact->_executors);
+  }
+  catch (const std::exception &e)
+  {
+    std::cerr << "Error during nnfw_session::train_prepare : " << e.what() << std::endl;
+    return NNFW_STATUS_ERROR;
+  }
+
+  _state = State::PREPARED_TRAINING;
+  return NNFW_STATUS_NO_ERROR;
+}
+
+NNFW_STATUS nnfw_session::train_input_tensorinfo(uint32_t index, nnfw_tensorinfo *ti)
+{
+  if (!isStatePreparedOrFinishedTraining())
+  {
+    std::cerr << "Error during nnfw_session::train_input_tensorinfo : invalid state" << std::endl;
+    return NNFW_STATUS_INVALID_STATE;
+  }
+
+  // Check index is valid: [0, getInputSize())
+
+  // NYI
+  (void)index;
+  (void)ti;
+  return NNFW_STATUS_ERROR;
+}
+
+NNFW_STATUS nnfw_session::train_expected_tensorinfo(uint32_t index, nnfw_tensorinfo *ti)
+{
+  if (!isStatePreparedOrFinishedTraining())
+  {
+    std::cerr << "Error during nnfw_session::train_expected_tensorinfo : invalid state"
+              << std::endl;
+    return NNFW_STATUS_INVALID_STATE;
+  }
+
+  // Check index is valid: [0, getExpectedSize())
+
+  // NYI
+  (void)index;
+  (void)ti;
+  return NNFW_STATUS_ERROR;
+}
+
+NNFW_STATUS nnfw_session::train_set_input(uint32_t index, const void *input,
+                                          const nnfw_tensorinfo *input_tensorinfo)
+{
+  if (input == nullptr)
+  {
+    std::cerr << "Error during nnfw_session::train_set_input : input buffer is null" << std::endl;
+    return NNFW_STATUS_UNEXPECTED_NULL;
+  }
+
+  if (!isStatePreparedOrFinishedTraining())
+  {
+    std::cerr << "Error during nnfw_session::train_set_input : invalid state" << std::endl;
+    return NNFW_STATUS_INVALID_STATE;
+  }
+
+  if (index >= getInputSize())
+  {
+    std::cerr << "Error during nnfw_session::train_set_input : index is out of range" << std::endl;
+    return NNFW_STATUS_ERROR;
+  }
+
+  try
+  {
+    auto ind = onert::ir::IOIndex(index);
+    auto size = _execution->getInputTotalSize(ind);
+    if (input_tensorinfo && getBufSize(input_tensorinfo) != size)
+    {
+      std::cerr
+        << "Error during nnfw_session::train_set_input : not supporeted to change tensorinfo"
+        << std::endl;
+      return NNFW_STATUS_ERROR;
+    }
+
+    _execution->setInput(ind, input, size);
+  }
+  catch (const std::exception &e)
+  {
+    std::cerr << "Error during nnfw_session::train_set_input : " << e.what() << std::endl;
+    return NNFW_STATUS_ERROR;
+  }
+
+  return NNFW_STATUS_NO_ERROR;
+}
+
+NNFW_STATUS nnfw_session::train_set_expected(uint32_t index, const void *expected,
+                                             const nnfw_tensorinfo *expected_tensorinfo)
+{
+  if (expected == nullptr)
+  {
+    std::cerr << "Error during nnfw_session::train_set_expected : expected buffer is null"
+              << std::endl;
+    return NNFW_STATUS_UNEXPECTED_NULL;
+  }
+
+  if (!isStatePreparedOrFinishedTraining())
+  {
+    std::cerr << "Error during nnfw_session::train_set_expected : invalid state" << std::endl;
+    return NNFW_STATUS_INVALID_STATE;
+  }
+
+  if (index >= getOutputSize())
+  {
+    std::cerr << "Error during nnfw_session::train_set_expected : index is out of range"
+              << std::endl;
+    return NNFW_STATUS_ERROR;
+  }
+
+  try
+  {
+    auto output_ind = onert::ir::IOIndex(index);
+    auto size = _execution->getOutputTotalSize(output_ind);
+    if (expected_tensorinfo && getBufSize(expected_tensorinfo) != size)
+    {
+      std::cerr << "Error during nnfw_session::train_set_expected : invalid tensorinfo"
+                << std::endl;
+      return NNFW_STATUS_ERROR;
+    }
+
+    // NOTE Find the loss input index
+    // Input is added as many as the number of outputs.
+    // The loss index is calculated from the value obtained by subtracting the
+    // total output(added loss input) from the total input size.
+    auto input_index = getInputSize() - getOutputSize() + index;
+    auto input_ind = onert::ir::IOIndex(input_index);
+    _execution->setInput(input_ind, expected, size);
+  }
+  catch (const std::exception &e)
+  {
+    std::cerr << "Error during nnfw_session::train_set_expected : " << e.what() << std::endl;
+    return NNFW_STATUS_ERROR;
+  }
+
+  return NNFW_STATUS_NO_ERROR;
+}
+
+NNFW_STATUS nnfw_session::train_run(bool update_weights)
+{
+  if (!isStatePreparedOrFinishedTraining())
+  {
+    std::cerr << "Error during nnfw_session::train_run : invalid state" << std::endl;
+    return NNFW_STATUS_INVALID_STATE;
+  }
+
+  try
+  {
+    if (update_weights)
+    {
+      _execution->train(_training_step++);
+    }
+    else
+      _execution->execute();
+  }
+  catch (const onert::InsufficientBufferSizeException &e)
+  {
+    // Currently insufficient buffer always means output buffer.
+    std::cerr << "Error during nnfw_session::train_run : " << e.what() << std::endl;
+    return NNFW_STATUS_INSUFFICIENT_OUTPUT_SIZE;
+  }
+  catch (const std::exception &e)
+  {
+    std::cerr << "Error during nnfw_session::train_run : " << e.what() << std::endl;
+    return NNFW_STATUS_ERROR;
+  }
+
+  _state = State::FINISHED_TRAINING;
+  return NNFW_STATUS_NO_ERROR;
+}
+
+NNFW_STATUS nnfw_session::train_get_loss(uint32_t index, float *loss)
+{
+  if (loss == nullptr)
+  {
+    std::cerr << "Error during nnfw_session::train_get_loss : loss is null" << std::endl;
+    return NNFW_STATUS_UNEXPECTED_NULL;
+  }
+
+  if (!isStateFinishedTraining())
+  {
+    std::cerr << "Error during nnfw_session::train_get_loss : invalid state" << std::endl;
+    return NNFW_STATUS_INVALID_STATE;
+  }
+
+  if (index >= getOutputSize())
+  {
+    std::cerr << "Error during nnfw_session::train_get_loss : index is out of range" << std::endl;
+    return NNFW_STATUS_ERROR;
+  }
+
+  try
+  {
+    auto ind = onert::ir::IOIndex(index);
+    *loss = _execution->getLoss(ind);
+  }
+  catch (const std::exception &e)
+  {
+    std::cerr << "Error during nnfw_session::train_get_loss : " << e.what() << std::endl;
+    return NNFW_STATUS_ERROR;
+  }
+
+  return NNFW_STATUS_NO_ERROR;
+}
+
+NNFW_STATUS nnfw_session::train_export_circle(const char *path)
+{
+  if (path == nullptr)
+  {
+    std::cerr << "Error during nnfw_session::train_export_circle : path is null" << std::endl;
+    return NNFW_STATUS_UNEXPECTED_NULL;
+  }
+
+  // Check training mode is enabled
+  if (!isStateFinishedTraining())
+  {
+    std::cerr << "Error during nnfw_session::train_export_circle : invalid state" << std::endl;
+    return NNFW_STATUS_INVALID_STATE;
+  }
+
+  // NYI
+  return NNFW_STATUS_ERROR;
+}
+
+bool nnfw_session::isStatePreparedTraining()
+{
+  if (_state == State::PREPARED_TRAINING)
+  {
+    assert(_nnpkg == nullptr);
+    assert(!_coptions.empty());
+    assert(_execution != nullptr);
+    return true;
+  }
+  else
+    return false;
+}
+
+bool nnfw_session::isStateFinishedTraining()
+{
+  if (_state == State::FINISHED_TRAINING)
+  {
+    assert(_nnpkg == nullptr);
+    assert(!_coptions.empty());
+    assert(_execution != nullptr);
+    return true;
+  }
+  else
+    return false;
+}
+
+bool nnfw_session::isStatePreparedOrFinishedTraining()
+{
+  return isStatePreparedTraining() || isStateFinishedTraining();
+}
+
+#endif // ONERT_TRAIN
+
+NNFW_STATUS nnfw_session::set_quantization_type(NNFW_QUANTIZE_TYPE qtype)
+{
+  try
+  {
+    if (!isStateModelLoaded())
+    {
+      std::cerr << "invalid state" << std::endl;
+      return NNFW_STATUS_INVALID_STATE;
+    }
+
+    bool is_q16 = false;
+    switch (qtype)
+    {
+      case NNFW_QUANTIZE_TYPE_U8_ASYM:
+        break;
+      case NNFW_QUANTIZE_TYPE_I16_SYM:
+        is_q16 = true;
+        break;
+      default:
+        return NNFW_STATUS_INVALID_STATE;
+    }
+    _quant_manager->quantizeType(is_q16);
+  }
+  catch (const std::exception &e)
+  {
+    std::cerr << "Error during nnfw_session::set_quantization_type : " << e.what() << std::endl;
+    return NNFW_STATUS_ERROR;
+  }
+
+  return NNFW_STATUS_NO_ERROR;
+}
+
+NNFW_STATUS nnfw_session::set_quantized_model_path(const char *path)
+{
+  try
+  {
+    if (!isStateModelLoaded())
+    {
+      std::cerr << "invalid state" << std::endl;
+      return NNFW_STATUS_INVALID_STATE;
+    }
+
+    _quant_manager->exportModelPath(std::string(path));
+  }
+  catch (const std::exception &e)
+  {
+    std::cerr << "Error during nnfw_session::set_quantized_model_path : " << e.what() << std::endl;
+    return NNFW_STATUS_ERROR;
+  }
+
+  return NNFW_STATUS_NO_ERROR;
+}
+
+NNFW_STATUS nnfw_session::quantize()
+{
+  try
+  {
+    if (!isStateModelLoaded())
+    {
+      std::cerr << "invalid state" << std::endl;
+      return NNFW_STATUS_INVALID_STATE;
+    }
+
+    auto result = _quant_manager->quantize();
+    if (!result)
+      return NNFW_STATUS_INVALID_STATE;
+
+    // Replace model
+    // TODO Support buffer replace, not file reload
+    auto model = loadModel(_quant_manager->exportModelPath(), "circle");
+    if (model == nullptr)
+      return NNFW_STATUS_ERROR;
+    _nnpkg->replaceModel(std::move(model));
+  }
+  catch (const std::exception &e)
+  {
+    std::cerr << "Error during nnfw_session::quantize : " << e.what() << std::endl;
+    return NNFW_STATUS_ERROR;
+  }
+
+  return NNFW_STATUS_NO_ERROR;
+}