2e902753786ca0224ed61e76d70f9ae5016eeab0
[platform/core/ml/nntrainer.git] / api / capi / src / nntrainer.cpp
1 /**
2  * Copyright (C) 2020 Samsung Electronics Co., Ltd. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *   http://www.apache.org/licenses/LICENSE-2.0
8  * Unless required by applicable law or agreed to in writing, software
9  * distributed under the License is distributed on an "AS IS" BASIS,
10  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  * See the License for the specific language governing permissions and
12  * limitations under the License.
13  */
14 /**
15  * @file nntrainer.cpp
16  * @date 02 April 2020
17  * @brief NNTrainer C-API Wrapper.
18  *        This allows to construct and control NNTrainer Model.
19  * @see https://github.com/nnstreamer/nntrainer
20  * @author Jijoong Moon <jijoong.moon@samsung.com>
21  * @author Parichay Kapoor <pk.kapoor@samsung.com>
22  * @bug No known bugs except for NYI items
23  */
24
25 #include <array>
26 #include <cstdarg>
27 #include <cstring>
28 #include <nntrainer.h>
29 #include <nntrainer_internal.h>
30 #include <sstream>
31 #include <string>
32
33 #include <nntrainer_error.h>
34 #include <nntrainer_log.h>
35
36 /**
37  * @brief   Global lock for nntrainer C-API
38  * @details This lock ensures that ml_train_model_destroy is thread safe. All
39  *          other API functions use the mutex from their object handle. However
40  *          for destroy, object mutex cannot be used as their handles are
41  *          destroyed at destroy.
42  */
43 std::mutex GLOCK;
44
45 /**
46  * @brief   Adopt the lock to the current scope for the object
47  */
48 #define ML_TRAIN_ADOPT_LOCK(obj, obj_lock) \
49   std::lock_guard<std::mutex> obj_lock(obj->m, std::adopt_lock)
50
51 /**
52  * @brief     function to wrap an exception to predefined error value
53  * @param[in] func must be wrapped inside lambda []() -> int otherwise compile
54  * error will be raised
55  * @retval    errorno
56  */
57 template <typename F> static int nntrainer_exception_boundary(F &&func) {
58   int status = ML_ERROR_NONE;
59
60   /**< Exception boundary for cpp exceptions */
61   /// @note aware that some exception are inheritance of others so should be
62   /// caught before than some
63   try {
64     status = func();
65   } catch (nntrainer::exception::not_supported &e) {
66     ml_loge("%s %s", typeid(e).name(), e.what());
67     return ML_ERROR_INVALID_PARAMETER;
68   } catch (nntrainer::exception::permission_denied &e) {
69     ml_loge("%s %s", typeid(e).name(), e.what());
70     return ML_ERROR_PERMISSION_DENIED;
71   } catch (std::invalid_argument &e) {
72     ml_loge("%s %s", typeid(e).name(), e.what());
73     return ML_ERROR_INVALID_PARAMETER;
74   } catch (std::range_error &e) {
75     ml_loge("%s %s", typeid(e).name(), e.what());
76     return ML_ERROR_INVALID_PARAMETER;
77   } catch (std::out_of_range &e) {
78     ml_loge("%s %s", typeid(e).name(), e.what());
79     return ML_ERROR_INVALID_PARAMETER;
80   } catch (std::logic_error &e) {
81     ml_loge("%s %s", typeid(e).name(), e.what());
82     return ML_ERROR_INVALID_PARAMETER;
83   } catch (std::bad_alloc &e) {
84     ml_loge("%s %s", typeid(e).name(), e.what());
85     return ML_ERROR_OUT_OF_MEMORY;
86   } catch (std::exception &e) {
87     ml_loge("%s %s", typeid(e).name(), e.what());
88     return ML_ERROR_UNKNOWN;
89   } catch (...) {
90     ml_loge("unknown error type thrown");
91     return ML_ERROR_UNKNOWN;
92   }
93
94   /**< Exception boundary for specialized error code */
95   /// @todo deprecate this with #233
96   switch (status) {
97   case ML_ERROR_BAD_ADDRESS:
98     return ML_ERROR_OUT_OF_MEMORY;
99   case ML_ERROR_RESULT_OUT_OF_RANGE:
100     return ML_ERROR_INVALID_PARAMETER;
101   default:
102     return status;
103   }
104 }
105
106 typedef std::function<int()> returnable;
107
108 /**
109  * @brief std::make_shared wrapped with exception boundary
110  *
111  * @tparam Tv value type.
112  * @tparam Tp pointer type.
113  * @tparam Types args used to construct
114  * @param target pointer
115  * @param args args
116  * @return int error value. ML_ERROR_OUT_OF_MEMORY if fail
117  */
118 template <typename Tv, typename Tp, typename... Types>
119 static int exception_bounded_make_shared(Tp &target, Types... args) {
120   returnable f = [&]() {
121     target = std::make_shared<Tv>(args...);
122     return ML_ERROR_NONE;
123   };
124
125   return nntrainer_exception_boundary(f);
126 }
127
128 /**
129  * @brief Create dataset with different types of train/test/valid data source
130  * @param[in] dataset dataset object to be created
131  * @param[in] type type of the dataset
132  * @param[in] train training data source
133  * @param[in] valid validation data source
134  * @param[in] test testing data source
135  */
136 template <typename T>
137 static int ml_train_dataset_create(ml_train_dataset_h *dataset,
138                                    ml::train::DatasetType type, T train,
139                                    T valid, T test) {
140   int status = ML_ERROR_NONE;
141
142   check_feature_state();
143   if (dataset == NULL) {
144     return ML_ERROR_INVALID_PARAMETER;
145   }
146
147   ml_train_dataset *nndataset = new ml_train_dataset;
148   nndataset->magic = ML_NNTRAINER_MAGIC;
149   nndataset->in_use = false;
150
151   returnable f = [&]() {
152     if (train != nullptr) {
153       nndataset->dataset[ML_TRAIN_DATASET_MODE_TRAIN] =
154         ml::train::createDataset(type, train);
155     }
156     if (valid != nullptr) {
157       nndataset->dataset[ML_TRAIN_DATASET_MODE_VALID] =
158         ml::train::createDataset(type, valid);
159     }
160     if (test != nullptr) {
161       nndataset->dataset[ML_TRAIN_DATASET_MODE_TEST] =
162         ml::train::createDataset(type, test);
163     }
164     return ML_ERROR_NONE;
165   };
166
167   status = nntrainer_exception_boundary(f);
168   if (status != ML_ERROR_NONE) {
169     delete nndataset;
170     ml_loge("Error: Create dataset failed");
171   } else {
172     *dataset = nndataset;
173   }
174
175   return status;
176 }
177
178 /**
179  * @brief add ml::train::Dataset to @a dataset
180  *
181  * @tparam Args args needed to create the dataset
182  * @param dataset dataset handle
183  * @param mode target mode
184  * @param type dataset type
185  * @param args args needed to create the dataset
186  * @retval #ML_ERROR_NONE Successful
187  * @retval #ML_ERROR_INVALID_PARAMETER if parameter is invalid
188  */
189 template <typename... Args>
190 static int ml_train_dataset_add_(ml_train_dataset_h dataset,
191                                  ml_train_dataset_mode_e mode,
192                                  ml::train::DatasetType type, Args &&... args) {
193   check_feature_state();
194   std::shared_ptr<ml::train::Dataset> underlying_dataset;
195
196   returnable f = [&]() {
197     underlying_dataset =
198       ml::train::createDataset(type, std::forward<Args>(args)...);
199     return ML_ERROR_NONE;
200   };
201
202   int status = nntrainer_exception_boundary(f);
203   if (status != ML_ERROR_NONE) {
204     ml_loge("Failed to create dataset");
205     return status;
206   }
207
208   if (underlying_dataset == nullptr) {
209     return ML_ERROR_INVALID_PARAMETER;
210   }
211
212   ml_train_dataset *nndataset;
213   ML_TRAIN_VERIFY_VALID_HANDLE(dataset);
214
215   {
216     ML_TRAIN_GET_VALID_DATASET_LOCKED(nndataset, dataset);
217     ML_TRAIN_ADOPT_LOCK(nndataset, dataset_lock);
218
219     nndataset->dataset[mode] = underlying_dataset;
220   }
221   return status;
222 }
223
224 #ifdef __cplusplus
225 extern "C" {
226 #endif
227
228 /**
229  * @brief Function to create ml::train::Model object.
230  */
231 static int nn_object(ml_train_model_h *model) {
232   int status = ML_ERROR_NONE;
233
234   if (model == NULL)
235     return ML_ERROR_INVALID_PARAMETER;
236
237   ml_train_model *nnmodel = new ml_train_model;
238   nnmodel->magic = ML_NNTRAINER_MAGIC;
239   nnmodel->optimizer = NULL;
240   nnmodel->dataset = NULL;
241
242   *model = nnmodel;
243
244   returnable f = [&]() {
245     nnmodel->model = ml::train::createModel(ml::train::ModelType::NEURAL_NET);
246     return ML_ERROR_NONE;
247   };
248
249   status = nntrainer_exception_boundary(f);
250   if (status != ML_ERROR_NONE) {
251     delete nnmodel;
252     ml_loge("Error: creating nn object failed");
253   }
254
255   return status;
256 }
257
258 int ml_train_model_construct(ml_train_model_h *model) {
259   int status = ML_ERROR_NONE;
260
261   check_feature_state();
262
263   returnable f = [&]() { return nn_object(model); };
264
265   status = nntrainer_exception_boundary(f);
266   return status;
267 }
268
269 int ml_train_model_construct_with_conf(const char *model_conf,
270                                        ml_train_model_h *model) {
271   int status = ML_ERROR_NONE;
272   ml_train_model *nnmodel;
273   std::shared_ptr<ml::train::Model> m;
274   returnable f;
275
276   status = ml_train_model_construct(model);
277   if (status != ML_ERROR_NONE)
278     return status;
279
280   nnmodel = (ml_train_model *)(*model);
281   m = nnmodel->model;
282
283   f = [&]() { return m->loadFromConfig(model_conf); };
284   status = nntrainer_exception_boundary(f);
285   if (status != ML_ERROR_NONE) {
286     ml_train_model_destroy(*model);
287   }
288
289   return status;
290 }
291
292 int ml_train_model_compile(ml_train_model_h model, ...) {
293   int status = ML_ERROR_NONE;
294   const char *data;
295   ml_train_model *nnmodel;
296   returnable f;
297   std::shared_ptr<ml::train::Model> m;
298
299   check_feature_state();
300
301   ML_TRAIN_VERIFY_VALID_HANDLE(model);
302
303   std::vector<std::string> arg_list;
304   va_list arguments;
305   va_start(arguments, model);
306
307   while ((data = va_arg(arguments, const char *))) {
308     arg_list.push_back(data);
309   }
310   va_end(arguments);
311
312   {
313     ML_TRAIN_GET_VALID_MODEL_LOCKED(nnmodel, model);
314     ML_TRAIN_ADOPT_LOCK(nnmodel, model_lock);
315     m = nnmodel->model;
316   }
317
318   f = [&]() {
319     m->setProperty(arg_list);
320     return ML_ERROR_NONE;
321   };
322   status = nntrainer_exception_boundary(f);
323   if (status != ML_ERROR_NONE)
324     return status;
325
326   f = [&]() { return m->compile(); };
327   status = nntrainer_exception_boundary(f);
328   if (status != ML_ERROR_NONE)
329     return status;
330
331   f = [&]() { return m->initialize(); };
332   status = nntrainer_exception_boundary(f);
333   if (status != ML_ERROR_NONE)
334     return status;
335
336   return status;
337 }
338
339 int ml_train_model_compile_with_single_param(ml_train_model_h model,
340                                              const char *single_param) {
341   ML_TRAIN_VERIFY_VALID_HANDLE(model);
342
343   return ml_train_model_compile(model, single_param, NULL);
344 }
345
346 int ml_train_model_run(ml_train_model_h model, ...) {
347   int status = ML_ERROR_NONE;
348   ml_train_model *nnmodel;
349   const char *data;
350   std::shared_ptr<ml::train::Model> m;
351
352   check_feature_state();
353
354   ML_TRAIN_VERIFY_VALID_HANDLE(model);
355
356   std::vector<std::string> arg_list;
357   va_list arguments;
358   va_start(arguments, model);
359
360   while ((data = va_arg(arguments, const char *))) {
361     arg_list.push_back(data);
362   }
363
364   va_end(arguments);
365
366   {
367     ML_TRAIN_GET_VALID_MODEL_LOCKED(nnmodel, model);
368     ML_TRAIN_ADOPT_LOCK(nnmodel, model_lock);
369     m = nnmodel->model;
370   }
371
372   returnable f = [&]() { return m->train(arg_list); };
373   status = nntrainer_exception_boundary(f);
374
375   return status;
376 }
377
378 int ml_train_model_run_with_single_param(ml_train_model_h model,
379                                          const char *single_param) {
380   ML_TRAIN_VERIFY_VALID_HANDLE(model);
381
382   return ml_train_model_run(model, single_param, NULL);
383 }
384
385 int ml_train_model_destroy(ml_train_model_h model) {
386   int status = ML_ERROR_NONE;
387   ml_train_model *nnmodel;
388
389   check_feature_state();
390
391   {
392     ML_TRAIN_GET_VALID_MODEL_LOCKED_RESET(nnmodel, model);
393     ML_TRAIN_ADOPT_LOCK(nnmodel, model_lock);
394   }
395
396   if (nnmodel->optimizer) {
397     if (nnmodel->optimizer->lr_sheduler) {
398       ML_TRAIN_RESET_VALIDATED_HANDLE(nnmodel->optimizer->lr_sheduler);
399       delete nnmodel->optimizer->lr_sheduler;
400     }
401
402     ML_TRAIN_RESET_VALIDATED_HANDLE(nnmodel->optimizer);
403     delete nnmodel->optimizer;
404   }
405
406   if (nnmodel->dataset) {
407     ML_TRAIN_RESET_VALIDATED_HANDLE(nnmodel->dataset);
408     delete nnmodel->dataset;
409   }
410
411   for (auto &x : nnmodel->layers_map) {
412     ML_TRAIN_RESET_VALIDATED_HANDLE(x.second);
413     delete (x.second);
414   }
415   nnmodel->layers_map.clear();
416
417   delete nnmodel;
418
419   return status;
420 }
421
422 static int ml_train_model_get_summary_util(ml_train_model_h model,
423                                            ml_train_summary_type_e verbosity,
424                                            std::stringstream &ss) {
425   int status = ML_ERROR_NONE;
426   ml_train_model *nnmodel;
427   std::shared_ptr<ml::train::Model> m;
428
429   {
430     ML_TRAIN_GET_VALID_MODEL_LOCKED(nnmodel, model);
431     ML_TRAIN_ADOPT_LOCK(nnmodel, model_lock);
432
433     m = nnmodel->model;
434   }
435
436   returnable f = [&]() {
437     m->summarize(ss, verbosity);
438     return ML_ERROR_NONE;
439   };
440
441   status = nntrainer_exception_boundary(f);
442   return status;
443 }
444
445 int ml_train_model_get_summary(ml_train_model_h model,
446                                ml_train_summary_type_e verbosity,
447                                char **summary) {
448   int status = ML_ERROR_NONE;
449   std::stringstream ss;
450
451   check_feature_state();
452
453   ML_TRAIN_VERIFY_VALID_HANDLE(model);
454
455   if (summary == nullptr) {
456     ml_loge("summary pointer is null");
457     return ML_ERROR_INVALID_PARAMETER;
458   }
459
460   status = ml_train_model_get_summary_util(model, verbosity, ss);
461   if (status != ML_ERROR_NONE) {
462     ml_loge("failed make a summary: %d", status);
463     return status;
464   }
465
466   std::string str = ss.str();
467   const std::string::size_type size = str.size();
468
469   if (size == 0) {
470     ml_logw("summary is empty for the model!");
471   }
472
473   *summary = (char *)malloc((size + 1) * sizeof(char));
474   if (*summary == nullptr) {
475     ml_loge("failed to malloc");
476     return ML_ERROR_OUT_OF_MEMORY;
477   }
478   std::memcpy(*summary, str.c_str(), size + 1);
479
480   return status;
481 }
482
483 int ml_train_model_add_layer(ml_train_model_h model, ml_train_layer_h layer) {
484   int status = ML_ERROR_NONE;
485   ml_train_model *nnmodel;
486   ml_train_layer *nnlayer;
487
488   check_feature_state();
489
490   ML_TRAIN_GET_VALID_MODEL_LOCKED(nnmodel, model);
491   ML_TRAIN_ADOPT_LOCK(nnmodel, model_lock);
492   ML_TRAIN_GET_VALID_LAYER_LOCKED(nnlayer, layer);
493   ML_TRAIN_ADOPT_LOCK(nnlayer, layer_lock);
494
495   if (nnlayer->in_use) {
496     ml_loge("Layer already in use.");
497     return ML_ERROR_INVALID_PARAMETER;
498   }
499
500   std::shared_ptr<ml::train::Model> m;
501   std::shared_ptr<ml::train::Layer> l;
502
503   m = nnmodel->model;
504   l = nnlayer->layer;
505
506   if (nnmodel->layers_map.count(l->getName())) {
507     ml_loge("It is not allowed to add layer with same name: %s",
508             l->getName().c_str());
509     return ML_ERROR_INVALID_PARAMETER;
510   }
511
512   returnable f = [&]() { return m->addLayer(l); };
513
514   status = nntrainer_exception_boundary(f);
515   if (status != ML_ERROR_NONE)
516     return status;
517
518   nnmodel->layers_map.insert({l->getName(), nnlayer});
519   nnlayer->in_use = true;
520   return status;
521 }
522
523 int ml_train_model_set_optimizer(ml_train_model_h model,
524                                  ml_train_optimizer_h optimizer) {
525   int status = ML_ERROR_NONE;
526   ml_train_model *nnmodel;
527   ml_train_optimizer *nnopt;
528
529   check_feature_state();
530
531   ML_TRAIN_GET_VALID_MODEL_LOCKED(nnmodel, model);
532   ML_TRAIN_ADOPT_LOCK(nnmodel, model_lock);
533   ML_TRAIN_GET_VALID_OPT_LOCKED(nnopt, optimizer);
534   ML_TRAIN_ADOPT_LOCK(nnopt, opt_lock);
535
536   if (nnopt->in_use) {
537     ml_loge("Optimizer already in use.");
538     return ML_ERROR_INVALID_PARAMETER;
539   }
540
541   std::shared_ptr<ml::train::Model> m;
542   std::shared_ptr<ml::train::Optimizer> opt;
543
544   m = nnmodel->model;
545   opt = nnopt->optimizer;
546
547   returnable f = [&]() { return m->setOptimizer(opt); };
548
549   status = nntrainer_exception_boundary(f);
550   if (status == ML_ERROR_NONE) {
551     nnopt->in_use = true;
552     if (nnmodel->optimizer) {
553       nnmodel->optimizer->in_use = false;
554     }
555     nnmodel->optimizer = nnopt;
556   }
557
558   return status;
559 }
560
561 int ml_train_model_set_dataset(ml_train_model_h model,
562                                ml_train_dataset_h dataset) {
563   int status = ML_ERROR_NONE;
564   ml_train_model *nnmodel;
565   ml_train_dataset *nndataset;
566
567   check_feature_state();
568
569   ML_TRAIN_GET_VALID_MODEL_LOCKED(nnmodel, model);
570   ML_TRAIN_ADOPT_LOCK(nnmodel, model_lock);
571   ML_TRAIN_GET_VALID_DATASET_LOCKED(nndataset, dataset);
572   ML_TRAIN_ADOPT_LOCK(nndataset, dataset_lock);
573
574   if (nndataset->in_use) {
575     ml_loge("Dataset already in use.");
576     return ML_ERROR_INVALID_PARAMETER;
577   }
578
579   std::shared_ptr<ml::train::Model> m;
580
581   m = nnmodel->model;
582
583   returnable f = [&]() {
584     auto &[train_set, valid_set, test_set] = nndataset->dataset;
585     int status = ML_ERROR_NONE;
586     status = m->setDataset(ml::train::DatasetModeType::MODE_TRAIN, train_set);
587     if (status != ML_ERROR_NONE) {
588       return status;
589     }
590
591     if (valid_set != nullptr) {
592       status = m->setDataset(ml::train::DatasetModeType::MODE_VALID, valid_set);
593       if (status != ML_ERROR_NONE) {
594         return status;
595       }
596     }
597
598     if (test_set != nullptr) {
599       status = m->setDataset(ml::train::DatasetModeType::MODE_TEST, test_set);
600       if (status != ML_ERROR_NONE) {
601         return status;
602       }
603     }
604     return status;
605   };
606
607   status = nntrainer_exception_boundary(f);
608   if (status == ML_ERROR_NONE) {
609     nndataset->in_use = true;
610     if (nnmodel->dataset)
611       nnmodel->dataset->in_use = false;
612     nnmodel->dataset = nndataset;
613   }
614
615   return status;
616 }
617
618 int ml_train_model_get_layer(ml_train_model_h model, const char *layer_name,
619                              ml_train_layer_h *layer) {
620   int status = ML_ERROR_NONE;
621   ml_train_model *nnmodel;
622
623   check_feature_state();
624
625   ML_TRAIN_GET_VALID_MODEL_LOCKED(nnmodel, model);
626   ML_TRAIN_ADOPT_LOCK(nnmodel, model_lock);
627
628   std::unordered_map<std::string, ml_train_layer *>::iterator layer_iter =
629     nnmodel->layers_map.find(std::string(layer_name));
630   /** if layer found in layers_map, return layer */
631   if (layer_iter != nnmodel->layers_map.end()) {
632     *layer = layer_iter->second;
633     return status;
634   }
635
636   /**
637    * if layer not found in layers_map, get layer from model,
638    * wrap it in struct nnlayer, add new entry in layer_map and then return
639    */
640   std::shared_ptr<ml::train::Model> m;
641   std::shared_ptr<ml::train::Layer> l;
642
643   m = nnmodel->model;
644   returnable f = [&]() { return m->getLayer(layer_name, &l); };
645   status = nntrainer_exception_boundary(f);
646
647   if (status != ML_ERROR_NONE)
648     return status;
649
650   ml_train_layer *nnlayer = new ml_train_layer;
651   nnlayer->magic = ML_NNTRAINER_MAGIC;
652   nnlayer->layer = l;
653   nnlayer->in_use = true;
654   nnmodel->layers_map.insert({l->getName(), nnlayer});
655
656   *layer = nnlayer;
657   return status;
658 }
659
660 int ml_train_layer_create(ml_train_layer_h *layer, ml_train_layer_type_e type) {
661   int status = ML_ERROR_NONE;
662   ml_train_layer *nnlayer;
663
664   check_feature_state();
665
666   nnlayer = new ml_train_layer;
667   nnlayer->magic = ML_NNTRAINER_MAGIC;
668   nnlayer->in_use = false;
669
670   returnable f = [&]() {
671     nnlayer->layer = ml::train::createLayer((ml::train::LayerType)type);
672     return ML_ERROR_NONE;
673   };
674
675   status = nntrainer_exception_boundary(f);
676   if (status != ML_ERROR_NONE) {
677     delete nnlayer;
678     ml_loge("Error: Create layer failed");
679   } else {
680     *layer = nnlayer;
681   }
682
683   return status;
684 }
685
686 int ml_train_layer_destroy(ml_train_layer_h layer) {
687   int status = ML_ERROR_NONE;
688   ml_train_layer *nnlayer;
689
690   check_feature_state();
691
692   {
693     ML_TRAIN_GET_VALID_LAYER_LOCKED_RESET(nnlayer, layer);
694     ML_TRAIN_ADOPT_LOCK(nnlayer, layer_lock);
695
696     if (nnlayer->in_use) {
697       ml_loge("Cannot delete layer already added in a model."
698               "Delete model will delete this layer.");
699       return ML_ERROR_INVALID_PARAMETER;
700     }
701   }
702
703   delete nnlayer;
704
705   return status;
706 }
707
708 int ml_train_layer_set_property(ml_train_layer_h layer, ...) {
709   int status = ML_ERROR_NONE;
710   ml_train_layer *nnlayer;
711   const char *data;
712   std::shared_ptr<ml::train::Layer> l;
713
714   check_feature_state();
715
716   ML_TRAIN_VERIFY_VALID_HANDLE(layer);
717
718   std::vector<std::string> arg_list;
719   va_list arguments;
720   va_start(arguments, layer);
721
722   while ((data = va_arg(arguments, const char *))) {
723     arg_list.push_back(data);
724   }
725
726   va_end(arguments);
727
728   {
729     ML_TRAIN_GET_VALID_LAYER_LOCKED(nnlayer, layer);
730     ML_TRAIN_ADOPT_LOCK(nnlayer, layer_lock);
731
732     l = nnlayer->layer;
733   }
734
735   returnable f = [&]() {
736     l->setProperty(arg_list);
737     return ML_ERROR_NONE;
738   };
739   status = nntrainer_exception_boundary(f);
740
741   return status;
742 }
743
744 int ml_train_layer_set_property_with_single_param(ml_train_layer_h layer,
745                                                   const char *single_param) {
746   ML_TRAIN_VERIFY_VALID_HANDLE(layer);
747
748   return ml_train_layer_set_property(layer, single_param, NULL);
749 }
750
751 int ml_train_optimizer_create(ml_train_optimizer_h *optimizer,
752                               ml_train_optimizer_type_e type) {
753   int status = ML_ERROR_NONE;
754
755   check_feature_state();
756
757   ml_train_optimizer *nnopt = new ml_train_optimizer;
758   nnopt->magic = ML_NNTRAINER_MAGIC;
759   nnopt->in_use = false;
760   nnopt->lr_sheduler = NULL;
761
762   returnable f = [&]() {
763     nnopt->optimizer =
764       ml::train::createOptimizer((ml::train::OptimizerType)type);
765     return ML_ERROR_NONE;
766   };
767
768   status = nntrainer_exception_boundary(f);
769   if (status != ML_ERROR_NONE) {
770     delete nnopt;
771     ml_loge("creating optimizer failed");
772   } else {
773     *optimizer = nnopt;
774   }
775
776   return status;
777 }
778
779 int ml_train_optimizer_destroy(ml_train_optimizer_h optimizer) {
780   int status = ML_ERROR_NONE;
781   ml_train_optimizer *nnopt;
782
783   check_feature_state();
784
785   {
786     ML_TRAIN_GET_VALID_OPT_LOCKED_RESET(nnopt, optimizer);
787     ML_TRAIN_ADOPT_LOCK(nnopt, optimizer_lock);
788
789     if (nnopt->in_use) {
790       ml_loge("Cannot delete optimizer already set to a model."
791               "Delete model will delete this optimizer.");
792       return ML_ERROR_INVALID_PARAMETER;
793     }
794
795     if (nnopt->lr_sheduler) {
796       ML_TRAIN_RESET_VALIDATED_HANDLE(nnopt->lr_sheduler);
797       delete nnopt->lr_sheduler;
798     }
799   }
800
801   delete nnopt;
802   return status;
803 }
804
805 int ml_train_optimizer_set_property(ml_train_optimizer_h optimizer, ...) {
806   int status = ML_ERROR_NONE;
807   ml_train_optimizer *nnopt;
808   const char *data;
809   std::shared_ptr<ml::train::Optimizer> opt;
810
811   check_feature_state();
812
813   ML_TRAIN_VERIFY_VALID_HANDLE(optimizer);
814
815   std::vector<std::string> arg_list;
816   va_list arguments;
817   va_start(arguments, optimizer);
818
819   while ((data = va_arg(arguments, const char *))) {
820     arg_list.push_back(data);
821   }
822
823   va_end(arguments);
824
825   {
826     ML_TRAIN_GET_VALID_OPT_LOCKED(nnopt, optimizer);
827     ML_TRAIN_ADOPT_LOCK(nnopt, optimizer_lock);
828
829     opt = nnopt->optimizer;
830   }
831
832   returnable f = [&]() {
833     opt->setProperty(arg_list);
834     return ML_ERROR_NONE;
835   };
836
837   status = nntrainer_exception_boundary(f);
838
839   return status;
840 }
841
842 int ml_train_optimizer_set_property_with_single_param(
843   ml_train_optimizer_h optimizer, const char *single_param) {
844   ML_TRAIN_VERIFY_VALID_HANDLE(optimizer);
845
846   return ml_train_optimizer_set_property(optimizer, single_param, NULL);
847 }
848
849 int ml_train_optimizer_set_lr_scheduler(ml_train_optimizer_h optimizer,
850                                         ml_train_lr_scheduler_h lr_scheduler) {
851   int status = ML_ERROR_NONE;
852   ml_train_optimizer *nnopt;
853   ml_train_lr_scheduler *nnlrscheduler;
854
855   check_feature_state();
856
857   ML_TRAIN_GET_VALID_OPT_LOCKED(nnopt, optimizer);
858   ML_TRAIN_ADOPT_LOCK(nnopt, opt_lock);
859   ML_TRAIN_GET_VALID_LR_SCHEDULER_LOCKED(nnlrscheduler, lr_scheduler);
860   ML_TRAIN_ADOPT_LOCK(nnlrscheduler, lr_scheduler_lock);
861
862   if (nnlrscheduler->in_use) {
863     ml_loge("learning rate scheduler already in use.");
864     return ML_ERROR_INVALID_PARAMETER;
865   }
866
867   std::shared_ptr<ml::train::Optimizer> opt;
868   std::shared_ptr<ml::train::LearningRateScheduler> lr_sched;
869
870   opt = nnopt->optimizer;
871   lr_sched = nnlrscheduler->lr_scheduler;
872
873   returnable f = [&]() { return opt->setLearningRateScheduler(lr_sched); };
874
875   status = nntrainer_exception_boundary(f);
876   if (status == ML_ERROR_NONE) {
877     nnlrscheduler->in_use = true;
878     if (nnopt->lr_sheduler) {
879       nnopt->lr_sheduler->in_use = false;
880     }
881     nnopt->lr_sheduler = nnlrscheduler;
882   }
883
884   return status;
885 }
886
887 int ml_train_lr_scheduler_create(ml_train_lr_scheduler_h *lr_scheduler,
888                                  ml_train_lr_scheduler_type_e type) {
889   int status = ML_ERROR_NONE;
890
891   check_feature_state();
892
893   ml_train_lr_scheduler *nnlrscheduler = new ml_train_lr_scheduler;
894   nnlrscheduler->magic = ML_NNTRAINER_MAGIC;
895   nnlrscheduler->in_use = false;
896
897   returnable f = [&]() {
898     nnlrscheduler->lr_scheduler = ml::train::createLearningRateScheduler(
899       (ml::train::LearningRateSchedulerType)type);
900     return ML_ERROR_NONE;
901   };
902
903   status = nntrainer_exception_boundary(f);
904   if (status != ML_ERROR_NONE) {
905     delete nnlrscheduler;
906     ml_loge("creating optimizer failed");
907   } else {
908     *lr_scheduler = nnlrscheduler;
909   }
910
911   return status;
912 }
913
914 int ml_train_lr_scheduler_destroy(ml_train_lr_scheduler_h lr_scheduler) {
915   int status = ML_ERROR_NONE;
916   ml_train_lr_scheduler *nnlrscheduler;
917
918   check_feature_state();
919
920   {
921     ML_TRAIN_GET_VALID_LR_SCHEDULER_LOCKED_RESET(nnlrscheduler, lr_scheduler);
922     ML_TRAIN_ADOPT_LOCK(nnlrscheduler, lr_scheduler_lock);
923
924     if (nnlrscheduler->in_use) {
925       ml_loge(
926         "Cannot delete learning rate scheduler already set to a optimizer."
927         "Delete optimizer will delete this learning rate scheduler.");
928       return ML_ERROR_INVALID_PARAMETER;
929     }
930   }
931
932   delete nnlrscheduler;
933   return status;
934 }
935
936 int ml_train_lr_scheduler_set_property(ml_train_lr_scheduler_h lr_scheduler,
937                                        ...) {
938   int status = ML_ERROR_NONE;
939   ml_train_lr_scheduler *nnlrscheduler;
940   const char *data;
941   std::shared_ptr<ml::train::LearningRateScheduler> lr_sched;
942
943   check_feature_state();
944
945   ML_TRAIN_VERIFY_VALID_HANDLE(lr_scheduler);
946
947   std::vector<std::string> arg_list;
948   va_list arguments;
949   va_start(arguments, lr_scheduler);
950
951   while ((data = va_arg(arguments, const char *))) {
952     arg_list.push_back(data);
953   }
954
955   va_end(arguments);
956
957   {
958     ML_TRAIN_GET_VALID_LR_SCHEDULER_LOCKED(nnlrscheduler, lr_scheduler);
959     ML_TRAIN_ADOPT_LOCK(nnlrscheduler, lr_scheduler_lock);
960
961     lr_sched = nnlrscheduler->lr_scheduler;
962   }
963
964   returnable f = [&]() {
965     lr_sched->setProperty(arg_list);
966     return ML_ERROR_NONE;
967   };
968
969   status = nntrainer_exception_boundary(f);
970
971   return status;
972 }
973
974 int ml_train_lr_scheduler_set_property_with_single_param(
975   ml_train_lr_scheduler_h lr_scheduler, const char *single_param) {
976   ML_TRAIN_VERIFY_VALID_HANDLE(lr_scheduler);
977
978   return ml_train_lr_scheduler_set_property(lr_scheduler, single_param, NULL);
979 }
980
981 int ml_train_dataset_create(ml_train_dataset_h *dataset) {
982   return ml_train_dataset_create(dataset, ml::train::DatasetType::UNKNOWN,
983                                  nullptr, nullptr, nullptr);
984 }
985
986 int ml_train_dataset_add_generator(ml_train_dataset_h dataset,
987                                    ml_train_dataset_mode_e mode,
988                                    ml_train_datagen_cb cb, void *user_data) {
989   check_feature_state();
990   if (cb == nullptr) {
991     return ML_ERROR_INVALID_PARAMETER;
992   }
993
994   return ml_train_dataset_add_(dataset, mode, ml::train::DatasetType::GENERATOR,
995                                cb, user_data);
996 }
997
998 int ml_train_dataset_add_file(ml_train_dataset_h dataset,
999                               ml_train_dataset_mode_e mode, const char *file) {
1000   check_feature_state();
1001   if (file == nullptr) {
1002     return ML_ERROR_INVALID_PARAMETER;
1003   }
1004
1005   return ml_train_dataset_add_(dataset, mode, ml::train::DatasetType::FILE,
1006                                file);
1007 }
1008
1009 int ml_train_dataset_create_with_generator(ml_train_dataset_h *dataset,
1010                                            ml_train_datagen_cb train_cb,
1011                                            ml_train_datagen_cb valid_cb,
1012                                            ml_train_datagen_cb test_cb) {
1013   if (train_cb == nullptr) {
1014     return ML_ERROR_INVALID_PARAMETER;
1015   }
1016   return ml_train_dataset_create(dataset, ml::train::DatasetType::GENERATOR,
1017                                  train_cb, valid_cb, test_cb);
1018 }
1019
1020 int ml_train_dataset_create_with_file(ml_train_dataset_h *dataset,
1021                                       const char *train_file,
1022                                       const char *valid_file,
1023                                       const char *test_file) {
1024   if (train_file == nullptr) {
1025     return ML_ERROR_INVALID_PARAMETER;
1026   }
1027   return ml_train_dataset_create(dataset, ml::train::DatasetType::FILE,
1028                                  train_file, valid_file, test_file);
1029 }
1030
1031 /**
1032  * @brief set property for the specific data mode, main difference from @a
1033  * ml_train_dataset_set_property_for_mode() is that this function returns @a
1034  * ML_ERROR_NOT_SUPPORTED if dataset does not exist.
1035  *
1036  * @param[in] dataset dataset
1037  * @param[in] mode mode
1038  * @param[in] args argument
1039  * @retval #ML_ERROR_NONE successful
1040  * @retval #ML_ERROR_INVALID_PARAMETER when arg is invalid
1041  * @retval #ML_ERROR_NOT_SUPPORTED when dataset did not exist
1042  */
1043 static int
1044 ml_train_dataset_set_property_for_mode_(ml_train_dataset_h dataset,
1045                                         ml_train_dataset_mode_e mode,
1046                                         const std::vector<void *> &args) {
1047   static constexpr char USER_DATA[] = "user_data";
1048   int status = ML_ERROR_NONE;
1049   ml_train_dataset *nndataset;
1050
1051   check_feature_state();
1052
1053   ML_TRAIN_VERIFY_VALID_HANDLE(dataset);
1054
1055   {
1056     ML_TRAIN_GET_VALID_DATASET_LOCKED(nndataset, dataset);
1057     ML_TRAIN_ADOPT_LOCK(nndataset, dataset_lock);
1058
1059     auto &db = nndataset->dataset[mode];
1060
1061     returnable f = [&db, &args]() {
1062       int status_ = ML_ERROR_NONE;
1063       if (db == nullptr) {
1064         status_ = ML_ERROR_NOT_SUPPORTED;
1065         return status_;
1066       }
1067
1068       std::vector<std::string> properties;
1069       for (unsigned int i = 0; i < args.size(); ++i) {
1070         char *key_ptr = (char *)args[i];
1071         std::string key = key_ptr;
1072         std::for_each(key.begin(), key.end(),
1073                       [](char &c) { c = ::tolower(c); });
1074         key.erase(std::remove_if(key.begin(), key.end(), ::isspace), key.end());
1075
1076         /** Handle the user_data as a special case, serialize the address and
1077          * pass it to the databuffer */
1078         if (key == USER_DATA) {
1079           /** This ensures that a valid user_data element is passed by the user
1080            */
1081           if (i + 1 >= args.size()) {
1082             ml_loge("key user_data expects, next value to be a pointer");
1083             status_ = ML_ERROR_INVALID_PARAMETER;
1084             return status_;
1085           }
1086           std::ostringstream ss;
1087           ss << key << '=' << args[i + 1];
1088           properties.push_back(ss.str());
1089
1090           /** As values of i+1 is consumed, increase i by 1 */
1091           i++;
1092         } else if (key.rfind("user_data=", 0) == 0) {
1093           /** case that user tries to pass something like user_data=5, this is
1094            * not allowed */
1095           status_ = ML_ERROR_INVALID_PARAMETER;
1096           return status_;
1097         } else {
1098           properties.push_back(key);
1099           continue;
1100         }
1101       }
1102
1103       db->setProperty(properties);
1104       return status_;
1105     };
1106
1107     status = nntrainer_exception_boundary(f);
1108   }
1109   return status;
1110 }
1111
1112 int ml_train_dataset_set_property(ml_train_dataset_h dataset, ...) {
1113   std::vector<void *> arg_list;
1114   va_list arguments;
1115   va_start(arguments, dataset);
1116
1117   void *data;
1118   while ((data = va_arg(arguments, void *))) {
1119     arg_list.push_back(data);
1120   }
1121   va_end(arguments);
1122
1123   /// having status of ML_ERROR_NOT_SUPPORTED is not an error in this call.
1124   int status = ml_train_dataset_set_property_for_mode_(
1125     dataset, ML_TRAIN_DATASET_MODE_TRAIN, arg_list);
1126   if (status != ML_ERROR_NONE && status != ML_ERROR_NOT_SUPPORTED) {
1127     return status;
1128   }
1129
1130   status = ml_train_dataset_set_property_for_mode_(
1131     dataset, ML_TRAIN_DATASET_MODE_VALID, arg_list);
1132   if (status != ML_ERROR_NONE && status != ML_ERROR_NOT_SUPPORTED) {
1133     return status;
1134   }
1135
1136   status = ml_train_dataset_set_property_for_mode_(
1137     dataset, ML_TRAIN_DATASET_MODE_TEST, arg_list);
1138   if (status != ML_ERROR_NONE && status != ML_ERROR_NOT_SUPPORTED) {
1139     return status;
1140   }
1141
1142   return ML_ERROR_NONE;
1143 }
1144
1145 int ml_train_dataset_set_property_for_mode(ml_train_dataset_h dataset,
1146                                            ml_train_dataset_mode_e mode, ...) {
1147   std::vector<void *> arg_list;
1148   va_list arguments;
1149   va_start(arguments, mode);
1150
1151   void *data;
1152   while ((data = va_arg(arguments, void *))) {
1153     arg_list.push_back(data);
1154   }
1155   va_end(arguments);
1156
1157   int status = ml_train_dataset_set_property_for_mode_(dataset, mode, arg_list);
1158
1159   return status != ML_ERROR_NONE ? ML_ERROR_INVALID_PARAMETER : ML_ERROR_NONE;
1160 }
1161
1162 int ml_train_dataset_set_property_for_mode_with_single_param(
1163   ml_train_dataset_h dataset, ml_train_dataset_mode_e mode,
1164   const char *single_param) {
1165   ML_TRAIN_VERIFY_VALID_HANDLE(dataset);
1166
1167   return ml_train_dataset_set_property_for_mode(dataset, mode, single_param,
1168                                                 NULL);
1169 }
1170
1171 int ml_train_dataset_destroy(ml_train_dataset_h dataset) {
1172   int status = ML_ERROR_NONE;
1173   ml_train_dataset *nndataset;
1174
1175   check_feature_state();
1176
1177   {
1178     ML_TRAIN_GET_VALID_DATASET_LOCKED_RESET(nndataset, dataset);
1179     ML_TRAIN_ADOPT_LOCK(nndataset, dataset_lock);
1180
1181     if (nndataset->in_use) {
1182       ml_loge("Cannot delete dataset already set to a model."
1183               "Delete model will delete this dataset.");
1184       return ML_ERROR_INVALID_PARAMETER;
1185     }
1186   }
1187
1188   delete nndataset;
1189   return status;
1190 }
1191
1192 int ml_train_model_get_input_tensors_info(ml_train_model_h model,
1193                                           ml_tensors_info_h *info) {
1194   int status = ML_ERROR_NONE;
1195   ml_train_model *nnmodel;
1196   std::shared_ptr<ml::train::Model> m;
1197   returnable f;
1198
1199   check_feature_state();
1200
1201   if (!info) {
1202     return ML_ERROR_INVALID_PARAMETER;
1203   }
1204
1205   ML_TRAIN_GET_VALID_MODEL_LOCKED(nnmodel, model);
1206   ML_TRAIN_ADOPT_LOCK(nnmodel, model_lock);
1207   m = nnmodel->model;
1208   if (m == NULL) {
1209     return ML_ERROR_INVALID_PARAMETER;
1210   }
1211
1212   std::vector<ml::train::TensorDim> dims;
1213   f = [&]() {
1214     dims = m->getInputDimension();
1215     return ML_ERROR_NONE;
1216   };
1217   status = nntrainer_exception_boundary(f);
1218   if (status != ML_ERROR_NONE) {
1219     return status;
1220   }
1221
1222   status = ml_tensors_info_create(info);
1223   if (status != ML_ERROR_NONE) {
1224     return status;
1225   }
1226
1227   status = ml_tensors_info_set_count(*info, dims.size());
1228   if (status != ML_ERROR_NONE) {
1229     ml_tensors_info_destroy(*info);
1230     return status;
1231   }
1232
1233   for (unsigned int i = 0; i < dims.size(); ++i) {
1234     status = ml_tensors_info_set_tensor_type(*info, i, ML_TENSOR_TYPE_FLOAT32);
1235     if (status != ML_ERROR_NONE) {
1236       ml_tensors_info_destroy(*info);
1237       return status;
1238     }
1239
1240     std::vector<unsigned int> u_dim;
1241
1242     for (unsigned int j = 0; j < dims[i].getNumDim(); j++)
1243       u_dim.push_back(dims[i].getDim()[j]);
1244
1245     status = ml_tensors_info_set_tensor_dimension(*info, i, u_dim.data());
1246     if (status != ML_ERROR_NONE) {
1247       ml_tensors_info_destroy(*info);
1248       return status;
1249     }
1250   }
1251
1252   return status;
1253 }
1254
1255 int ml_train_model_get_output_tensors_info(ml_train_model_h model,
1256                                            ml_tensors_info_h *info) {
1257   int status = ML_ERROR_NONE;
1258   ml_train_model *nnmodel;
1259   std::shared_ptr<ml::train::Model> m;
1260   returnable f;
1261
1262   check_feature_state();
1263
1264   if (!info) {
1265     return ML_ERROR_INVALID_PARAMETER;
1266   }
1267
1268   ML_TRAIN_GET_VALID_MODEL_LOCKED(nnmodel, model);
1269   ML_TRAIN_ADOPT_LOCK(nnmodel, model_lock);
1270   m = nnmodel->model;
1271   if (m == NULL) {
1272     return ML_ERROR_INVALID_PARAMETER;
1273   }
1274
1275   std::vector<ml::train::TensorDim> dims;
1276
1277   f = [&]() {
1278     dims = m->getOutputDimension();
1279     return ML_ERROR_NONE;
1280   };
1281   status = nntrainer_exception_boundary(f);
1282   if (status != ML_ERROR_NONE) {
1283     return status;
1284   }
1285
1286   status = ml_tensors_info_create(info);
1287   if (status != ML_ERROR_NONE) {
1288     return status;
1289   }
1290
1291   status = ml_tensors_info_set_count(*info, dims.size());
1292   if (status != ML_ERROR_NONE) {
1293     ml_tensors_info_destroy(*info);
1294     return status;
1295   }
1296
1297   for (unsigned int i = 0; i < dims.size(); ++i) {
1298     status = ml_tensors_info_set_tensor_type(*info, i, ML_TENSOR_TYPE_FLOAT32);
1299     if (status != ML_ERROR_NONE) {
1300       ml_tensors_info_destroy(*info);
1301       return status;
1302     }
1303
1304     std::vector<unsigned int> u_dim;
1305
1306     for (unsigned int j = 0; j < dims[i].getNumDim(); j++)
1307       u_dim.push_back(dims[i].getDim()[j]);
1308
1309     status = ml_tensors_info_set_tensor_dimension(*info, i, u_dim.data());
1310     if (status != ML_ERROR_NONE) {
1311       ml_tensors_info_destroy(*info);
1312       return status;
1313     }
1314   }
1315
1316   return status;
1317 }
1318
1319 int ml_train_model_save(ml_train_model_h model, const char *file_path,
1320                         ml_train_model_format_e format) {
1321   int status = ML_ERROR_NONE;
1322   ml_train_model *nnmodel;
1323   std::shared_ptr<ml::train::Model> m;
1324
1325   {
1326     ML_TRAIN_GET_VALID_MODEL_LOCKED(nnmodel, model);
1327     ML_TRAIN_ADOPT_LOCK(nnmodel, model_lock);
1328
1329     m = nnmodel->model;
1330   }
1331
1332   returnable f = [&]() {
1333     m->save(file_path, static_cast<ml::train::ModelFormat>(format));
1334     return ML_ERROR_NONE;
1335   };
1336
1337   status = nntrainer_exception_boundary(f);
1338   return status;
1339 }
1340
1341 int ml_train_model_load(ml_train_model_h model, const char *file_path,
1342                         ml_train_model_format_e format) {
1343   int status = ML_ERROR_NONE;
1344   ml_train_model *nnmodel;
1345   std::shared_ptr<ml::train::Model> m;
1346
1347   {
1348     ML_TRAIN_GET_VALID_MODEL_LOCKED(nnmodel, model);
1349     ML_TRAIN_ADOPT_LOCK(nnmodel, model_lock);
1350
1351     m = nnmodel->model;
1352   }
1353
1354   returnable f = [&]() {
1355     m->load(file_path, static_cast<ml::train::ModelFormat>(format));
1356     return ML_ERROR_NONE;
1357   };
1358
1359   status = nntrainer_exception_boundary(f);
1360   return status;
1361 }
1362
1363 int ml_train_model_get_weight(ml_train_model_h model, const char *layer_name,
1364                               ml_tensors_data_h *weight,
1365                               ml_tensors_info_h *info) {
1366   int status = ML_ERROR_NONE;
1367   ml_train_model *nnmodel;
1368
1369   check_feature_state();
1370
1371   ML_TRAIN_GET_VALID_MODEL_LOCKED(nnmodel, model);
1372   ML_TRAIN_ADOPT_LOCK(nnmodel, model_lock);
1373
1374   std::shared_ptr<ml::train::Model> m;
1375   std::shared_ptr<ml::train::Layer> l;
1376   std::vector<float *> w;
1377   std::vector<ml::train::TensorDim> dims;
1378   std::vector<std::string> weight_name;
1379
1380   m = nnmodel->model;
1381
1382   returnable f = [&]() { return m->getLayer(layer_name, &l); };
1383   status = nntrainer_exception_boundary(f);
1384   if (status != ML_ERROR_NONE)
1385     return status;
1386
1387   f = [&]() {
1388     l->getWeights(w, dims);
1389
1390     for (unsigned int i = 0; i < dims.size(); ++i)
1391       weight_name.emplace_back(l->getWeightName(i));
1392
1393     return ML_ERROR_NONE;
1394   };
1395
1396   status = nntrainer_exception_boundary(f);
1397   if (status != ML_ERROR_NONE) {
1398     return status;
1399   }
1400
1401   status = ml_tensors_info_create(info);
1402   if (status != ML_ERROR_NONE) {
1403     return status;
1404   }
1405
1406   status = ml_tensors_info_set_count(*info, dims.size());
1407   if (status != ML_ERROR_NONE) {
1408     ml_tensors_info_destroy(*info);
1409     return status;
1410   }
1411
1412   for (unsigned int i = 0; i < dims.size(); ++i) {
1413     status = ml_tensors_info_set_tensor_type(*info, i, ML_TENSOR_TYPE_FLOAT32);
1414     if (status != ML_ERROR_NONE) {
1415       ml_tensors_info_destroy(*info);
1416       return status;
1417     }
1418
1419     std::vector<unsigned int> u_dim;
1420
1421     for (unsigned int j = 0; j < dims[i].getNumDim(); j++)
1422       u_dim.push_back(dims[i].getDim()[j]);
1423
1424     status = ml_tensors_info_set_tensor_dimension(*info, i, u_dim.data());
1425     if (status != ML_ERROR_NONE) {
1426       ml_tensors_info_destroy(*info);
1427       return status;
1428     }
1429
1430     status = ml_tensors_info_set_tensor_name(*info, i, weight_name[i].c_str());
1431     if (status != ML_ERROR_NONE) {
1432       ml_tensors_info_destroy(*info);
1433       return status;
1434     }
1435   }
1436
1437   status = ml_tensors_data_create(*info, weight);
1438   if (status != ML_ERROR_NONE) {
1439     ml_tensors_data_destroy(*weight);
1440     return status;
1441   }
1442
1443   for (unsigned int i = 0; i < dims.size(); ++i) {
1444     status = ml_tensors_data_set_tensor_data(
1445       *weight, i, w[i], dims[i].getDataLen() * sizeof(float));
1446     if (status != ML_ERROR_NONE) {
1447       ml_tensors_data_destroy(*weight);
1448       return status;
1449     }
1450   }
1451
1452   return status;
1453 }
1454
1455 #ifdef __cplusplus
1456 }
1457 #endif