[ Mixed Tensor ] Enable FP32 unittest cases
[platform/core/ml/nntrainer.git] / nntrainer / utils / base_properties.h
1 // SPDX-License-Identifier: Apache-2.0
2 /**
3  * Copyright (C) 2021 Jihoon Lee <jhoon.it.lee@samsung.com>
4  *
5  * @file base_properties.h
6  * @date 08 April 2021
7  * @brief Convenient property type definition for automated serialization
8  * @see https://github.com/nnstreamer/nntrainer
9  * @author Jihoon Lee <jhoon.it.lee@samsung.com>
10  * @bug No known bugs except for NYI items
11  */
12 #ifndef __BASE_PROPERTIES_H__
13 #define __BASE_PROPERTIES_H__
14
15 #include <array>
16 #include <memory>
17 #include <regex>
18 #include <sstream>
19 #include <string>
20 #include <vector>
21
22 #include <nntrainer_error.h>
23 #include <tensor_dim.h>
24 #include <util_func.h>
25
26 /** base and predefined structures */
27
28 namespace nntrainer {
29
30 using TensorDim = ml::train::TensorDim;
31
32 /**
33  * @brief property info to specialize functions based on this
34  * @tparam T property type
35  */
36 template <typename T> struct prop_info {
37   using prop_type = std::decay_t<T>;             /** property type of T */
38   using tag_type = typename prop_type::prop_tag; /** Property tag of T */
39   using data_type =
40     std::decay_t<decltype(std::declval<prop_type>().get())>; /** Underlying
41                                                                 datatype of T */
42 };
43
44 /**
45  * @brief property info when it is wrapped inside a vector
46  *
47  * @tparam T property type
48  */
49 template <typename T> struct prop_info<std::vector<T>> {
50   using prop_type = typename prop_info<T>::prop_type;
51   using tag_type = typename prop_info<T>::tag_type;
52   using data_type = typename prop_info<T>::data_type;
53 };
54
55 /**
56  * @brief property info when it is wrapped inside an array
57  *
58  * @tparam T property type
59  */
60 template <typename T, size_t size> struct prop_info<std::array<T, size>> {
61   using prop_type = typename prop_info<T>::prop_type;
62   using tag_type = typename prop_info<T>::tag_type;
63   using data_type = typename prop_info<T>::data_type;
64 };
65
66 /**
67  * @brief Get the Prop Key object
68  *
69  * @tparam T property to get type
70  * @param prop property
71  * @return constexpr const char* key
72  */
73 template <typename T> constexpr const char *getPropKey(T &&prop) {
74   return prop_info<std::decay_t<T>>::prop_type::key;
75 }
76
77 /**
78  * @brief property is treated as integer
79  *
80  */
81 struct int_prop_tag {};
82
83 /**
84  * @brief property is treated as unsigned integer
85  *
86  */
87 struct uint_prop_tag {};
88
89 /**
90  * @brief property is treated as unsigned integer
91  *
92  */
93 struct size_t_prop_tag {};
94
95 /**
96  * @brief property is treated as dimension, eg 1:2:3
97  *
98  */
99 struct dimension_prop_tag {};
100
101 /**
102  * @brief property is treated as float
103  *
104  */
105 struct float_prop_tag {};
106
107 /**
108  * @brief property is treated as double
109  *
110  */
111 struct double_prop_tag {};
112
113 /**
114  * @brief property is treated as string
115  *
116  */
117 struct str_prop_tag {};
118
119 /**
120  * @brief property is treated as boolean
121  *
122  */
123 struct bool_prop_tag {};
124
125 /**
126  * @brief property is treated as enum class
127  *
128  */
129 struct enum_class_prop_tag {};
130
131 /**
132  * @brief property treated as a raw pointer
133  *
134  */
135 struct ptr_prop_tag {};
136
137 /**
138  * @brief base property class, inherit this to make a convenient property
139  *
140  * @tparam T
141  */
142 template <typename T> class Property {
143
144 public:
145   /**
146    * @brief Construct a new Property object
147    *
148    */
149   Property() : value(nullptr){};
150
151   /**
152    * @brief Construct a new Property object
153    *
154    * @param value default value
155    */
156   Property(const T &value_) { set(value_); }
157
158   /**
159    * @brief Copy Construct a new Property object
160    *
161    * @param rhs right side to copy from
162    */
163   Property(const Property &rhs) {
164     if (this != &rhs && rhs.value) {
165       value = std::make_unique<T>(*rhs.value);
166     }
167   }
168
169   /**
170    * @brief Copy assignment operator of a new property
171    *
172    * @param rhs right side to copy from
173    * @return Property& this
174    */
175   Property &operator=(const Property &rhs) {
176     if (this != &rhs && rhs.value) {
177       value = std::make_unique<T>(*rhs.value);
178     }
179     return *this;
180   };
181
182   Property(Property &&rhs) noexcept = default;
183   Property &operator=(Property &&rhs) noexcept = default;
184
185   /**
186    * @brief Destroy the Property object
187    *
188    */
189   virtual ~Property() = default;
190
191   /**
192    * @brief cast operator for property
193    *
194    * @return T value
195    */
196   operator T &() { return get(); }
197
198   /**
199    * @brief cast operator for property
200    *
201    * @return T value
202    */
203   operator const T &() const { return get(); }
204
205   /**
206    * @brief get the underlying data
207    *
208    * @return const T& data
209    */
210   const T &get() const {
211     NNTR_THROW_IF(value == nullptr, std::invalid_argument)
212       << "Cannot get property, property is empty";
213     return *value;
214   }
215
216   /**
217    * @brief get the underlying data
218    *
219    * @return T& data
220    */
221   T &get() {
222     NNTR_THROW_IF(value == nullptr, std::invalid_argument)
223       << "Cannot get property, property is empty";
224     return *value;
225   }
226
227   /**
228    * @brief check if property is empty
229    *
230    * @retval true empty
231    * @retval false not empty
232    */
233   bool empty() const { return value == nullptr; }
234
235   /**
236    * @brief set the underlying data
237    *
238    * @param v value to set
239    * @throw std::invalid_argument if argument is not valid
240    */
241   virtual void set(const T &v) {
242     NNTR_THROW_IF(isValid(v) == false, std::invalid_argument)
243       << "argument is not valid";
244     value = std::make_unique<T>(v);
245   }
246
247   /**
248    * @brief check if given value is valid
249    *
250    * @param v value to check
251    * @retval true if valid
252    * @retval false if not valid
253    */
254   virtual bool isValid(const T &v) const { return true; }
255
256   /**
257    * @brief operator==
258    *
259    * @param rhs right side to compare
260    * @retval true if equal
261    * @retval false if not equal
262    */
263   bool operator==(const Property<T> &rhs) const { return *value == *rhs.value; }
264
265 private:
266   std::unique_ptr<T> value; /**< underlying data */
267 };
268
269 /**
270  * @brief enum property
271  *
272  * @tparam T underlying type info to query enum_info
273  */
274 template <typename EnumInfo>
275 class EnumProperty : public Property<typename EnumInfo::Enum> {
276 public:
277   static EnumInfo enum_info_;
278 };
279
280 /**
281  * @brief abstract class for positive integer
282  *
283  */
284 class PositiveIntegerProperty : public Property<unsigned int> {
285 public:
286   /**
287    * @brief Destroy the Positive Integer Property object
288    *
289    */
290   virtual ~PositiveIntegerProperty() = default;
291
292   /**
293    * @brief isValid override, check if value > 0
294    *
295    * @param value value to check
296    * @retval true if value > 0
297    */
298   virtual bool isValid(const unsigned int &value) const override;
299 };
300 /**
301  * @brief meta function to cast tag to it's base
302  * @code below is the test spec for the cast
303  *
304  * struct custom_tag: int_prop_tag {};
305  *
306  * using tag_type = tag_cast<custom_tag, float_prop_tag>::type
307  * static_assert(<std::is_same_v<tag_type, custom_tag> == true);
308  *
309  * using tag_type = tag_cast<custom_tag, int_prop_tag>::type
310  * static_assert(<std::is_same_v<tag_type, int_prop_tag> == true);
311  *
312  * using tag_type = tag_cast<custom_tag, float_prop_tag, int_prop_tag>::type
313  * static_assert(std::is_same_v<tag_type, int_prop_tag> == true);
314  *
315  * @tparam Tags First tag: tag to be casted, rest tags: candidates
316  */
317 template <typename... Tags> struct tag_cast;
318
319 /**
320  * @brief base case of tag_cast, if nothing matches return @a Tag
321  *
322  * @tparam Tag Tag to be casted
323  * @tparam Others empty parameter pack
324  */
325 template <typename Tag, typename... Others> struct tag_cast<Tag, Others...> {
326   using type = Tag;
327 };
328
329 /**
330  * @brief normal case of the tag cast
331  *
332  * @tparam Tag tag to be casted
333  * @tparam BaseTag candidates to cast the tag
334  * @tparam Others pending candidates to be compared
335  */
336 template <typename Tag, typename BaseTag, typename... Others>
337 struct tag_cast<Tag, BaseTag, Others...> {
338   using type = std::conditional_t<std::is_base_of<BaseTag, Tag>::value, BaseTag,
339                                   typename tag_cast<Tag, Others...>::type>;
340 };
341
342 /**
343  * @brief property to string converter.
344  * This structure defines how to convert to convert from/to string
345  *
346  * @tparam Tag tag type for the converter
347  * @tparam DataType underlying datatype
348  */
349 template <typename Tag, typename DataType> struct str_converter {
350
351   /**
352    * @brief convert underlying value to string
353    *
354    * @param value value to convert to string
355    * @return std::string string
356    */
357   static std::string to_string(const DataType &value);
358
359   /**
360    * @brief convert string to underlying value
361    *
362    * @param value value to convert to string
363    * @return DataType converted type
364    */
365   static DataType from_string(const std::string &value);
366 };
367
368 /**
369  * @brief str converter specialization for enum classes
370  *
371  * @tparam EnumInfo enum informations
372  */
373 template <typename EnumInfo>
374 struct str_converter<enum_class_prop_tag, EnumInfo> {
375
376   /**
377    * @copydoc template <typename Tag, typename DataType> struct str_converter
378    */
379   static std::string to_string(const typename EnumInfo::Enum &value) {
380     constexpr auto size = EnumInfo::EnumList.size();
381     constexpr const auto data = std::data(EnumInfo::EnumList);
382     for (unsigned i = 0; i < size; ++i) {
383       if (data[i] == value) {
384         return EnumInfo::EnumStr[i];
385       }
386     }
387     throw std::invalid_argument("Cannot find value in the enum list");
388   }
389
390   /**
391    * @copydoc template <typename Tag, typename DataType> struct str_converter
392    */
393   static typename EnumInfo::Enum from_string(const std::string &value) {
394     constexpr auto size = EnumInfo::EnumList.size();
395     constexpr const auto data = std::data(EnumInfo::EnumList);
396     for (unsigned i = 0; i < size; ++i) {
397       if (istrequal(EnumInfo::EnumStr[i], value.c_str())) {
398         return data[i];
399       }
400     }
401     throw std::invalid_argument("No matching enum for value: " + value);
402   }
403 };
404
405 /**
406  * @brief str converter which serializes a pointer and returns back to a ptr
407  *
408  * @tparam DataType pointer type
409  */
410 template <typename DataType> struct str_converter<ptr_prop_tag, DataType> {
411
412   /**
413    * @brief convert underlying value to string
414    *
415    * @param value value to convert to string
416    * @return std::string string
417    */
418   static std::string to_string(const DataType &value) {
419     std::ostringstream ss;
420     ss << value;
421     return ss.str();
422   }
423
424   /**
425    * @brief convert string to underlying value
426    *
427    * @param value value to convert to string
428    * @return DataType converted type
429    */
430   static DataType from_string(const std::string &value) {
431     std::stringstream ss(value);
432     uintptr_t addr = static_cast<uintptr_t>(std::stoull(value, 0, 16));
433     return reinterpret_cast<DataType>(addr);
434   }
435 };
436
437 /**
438  * @copydoc template <typename Tag, typename DataType> struct str_converter
439  */
440 template <>
441 std::string
442 str_converter<str_prop_tag, std::string>::to_string(const std::string &value);
443
444 /**
445  * @copydoc template <typename Tag, typename DataType> struct str_converter
446  */
447 template <>
448 std::string
449 str_converter<str_prop_tag, std::string>::from_string(const std::string &value);
450
451 /**
452  * @copydoc template <typename Tag, typename DataType> struct str_converter
453  */
454 template <>
455 std::string str_converter<uint_prop_tag, unsigned int>::to_string(
456   const unsigned int &value);
457
458 /**
459  * @copydoc template <typename Tag, typename DataType> struct str_converter
460  */
461 template <>
462 unsigned int str_converter<uint_prop_tag, unsigned int>::from_string(
463   const std::string &value);
464
465 /**
466  * @copydoc template <typename Tag, typename DataType> struct str_converter
467  */
468 template <>
469 std::string
470 str_converter<size_t_prop_tag, size_t>::to_string(const size_t &value);
471
472 /**
473  * @copydoc template <typename Tag, typename DataType> struct str_converter
474  */
475 template <>
476 size_t
477 str_converter<size_t_prop_tag, size_t>::from_string(const std::string &value);
478
479 /**
480  * @copydoc template <typename Tag, typename DataType> struct str_converter
481  */
482 template <>
483 std::string str_converter<bool_prop_tag, bool>::to_string(const bool &value);
484
485 /**
486  * @copydoc template <typename Tag, typename DataType> struct str_converter
487  */
488 template <>
489 bool str_converter<bool_prop_tag, bool>::from_string(const std::string &value);
490
491 /**
492  * @copydoc template <typename Tag, typename DataType> struct str_converter
493  */
494 template <>
495 std::string str_converter<float_prop_tag, float>::to_string(const float &value);
496
497 /**
498  * @copydoc template <typename Tag, typename DataType> struct str_converter
499  */
500 template <>
501 float str_converter<float_prop_tag, float>::from_string(
502   const std::string &value);
503
504 /**
505  * @copydoc template <typename Tag, typename DataType> struct str_converter
506  */
507 template <>
508 std::string
509 str_converter<double_prop_tag, double>::to_string(const double &value);
510
511 /**
512  * @copydoc template <typename Tag, typename DataType> struct str_converter
513  */
514 template <>
515 double
516 str_converter<double_prop_tag, double>::from_string(const std::string &value);
517
518 /**
519  * @brief convert dispatcher (to string)
520  *
521  * @tparam T type to convert
522  * @param property property to convert
523  * @return std::string converted string
524  */
525 template <typename T> std::string to_string(const T &property) {
526   using info = prop_info<T>;
527   using tag_type =
528     typename tag_cast<typename info::tag_type, int_prop_tag, uint_prop_tag,
529                       dimension_prop_tag, float_prop_tag, str_prop_tag,
530                       enum_class_prop_tag>::type;
531   using data_type = typename info::data_type;
532
533   if constexpr (std::is_same_v<tag_type, enum_class_prop_tag>) {
534     return str_converter<tag_type, decltype(T::enum_info_)>::to_string(
535       property.get());
536   } else {
537     return str_converter<tag_type, data_type>::to_string(property.get());
538   }
539 }
540
541 /**
542  * @brief to_string vector specialization
543  * @copydoc template <typename T> std::string to_string(const T &property)
544  */
545 template <typename T> std::string to_string(const std::vector<T> &property) {
546   std::stringstream ss;
547   auto last_iter = property.end() - 1;
548   for (auto iter = property.begin(); iter != last_iter; ++iter) {
549     ss << to_string(*iter) << ',';
550   }
551   ss << to_string(*last_iter);
552
553   return ss.str();
554 }
555
556 /**
557  * @brief to_string array specialization
558  * @copydoc template <typename T> std::string to_string(const T &property)
559  */
560 template <typename T, size_t sz>
561 static std::string to_string(const std::array<T, sz> &value) {
562   std::stringstream ss;
563   auto last_iter = value.end() - 1;
564   for (auto iter = value.begin(); iter != last_iter; ++iter) {
565     ss << to_string(*iter) << ',';
566   }
567   ss << to_string(*last_iter);
568
569   return ss.str();
570 }
571
572 /**
573  *
574  * @brief convert dispatcher (from string)
575  *
576  *
577  * @tparam T type to convert
578  * @param str string to convert
579  * @param[out] property property, converted type
580  */
581 template <typename T> void from_string(const std::string &str, T &property) {
582   using info = prop_info<T>;
583   using tag_type =
584     typename tag_cast<typename info::tag_type, int_prop_tag, uint_prop_tag,
585                       dimension_prop_tag, float_prop_tag, str_prop_tag,
586                       enum_class_prop_tag>::type;
587   using data_type = typename info::data_type;
588
589   if constexpr (std::is_same_v<tag_type, enum_class_prop_tag>) {
590     property.set(
591       str_converter<tag_type, decltype(T::enum_info_)>::from_string(str));
592   } else {
593     property.set(str_converter<tag_type, data_type>::from_string(str));
594   }
595 }
596
597 /**
598  * @brief transform iternal data, this is to use with std::transform
599  *
600  * @param item item to transform
601  * @return DataType transformed result
602  */
603 template <typename T> static T from_string_helper_(const std::string &item) {
604   T t;
605   from_string(item, t);
606   return t;
607 }
608
609 static const std::regex reg_("\\s*\\,\\s*");
610
611 /**
612  * @brief from_string array specialization
613  * @copydoc template <typename T> void from_string(const std::string &str, T
614  * &property)
615  * @note array implies that the size is @b fixed so there will be a validation
616  * check on size
617  */
618 template <typename T, size_t sz>
619 void from_string(const std::string &value, std::array<T, sz> &property) {
620   auto v = split(value, reg_);
621   NNTR_THROW_IF(v.size() != sz, std::invalid_argument)
622     << "size must match with array size, array size: " << sz
623     << " string: " << value;
624
625   std::transform(v.begin(), v.end(), property.begin(), from_string_helper_<T>);
626 }
627
628 /**
629  * @brief from_string vector specialization
630  * @copydoc str_converter<Tag, DataType>::to_string(const DataType &value)
631  * @note vector implies that the size is @b not fixed so there shouldn't be any
632  * validation on size
633  *
634  */
635 template <typename T>
636 void from_string(const std::string &value, std::vector<T> &property) {
637   auto v = split(value, reg_);
638
639   property.clear();
640   property.reserve(v.size());
641   std::transform(v.begin(), v.end(), std::back_inserter(property),
642                  from_string_helper_<T>);
643 }
644 /******** below section is for enumerations ***************/
645 /**
646  * @brief     Enumeration of Data Type for model & layer
647  */
648 struct TensorDataTypeInfo {
649   using Enum = nntrainer::TensorDim::DataType;
650   static constexpr std::initializer_list<Enum> EnumList = {Enum::FP16,
651                                                            Enum::FP32};
652
653   static constexpr const char *EnumStr[] = {"FP16", "FP32"};
654 };
655
656 namespace props {
657
658 /**
659  * @brief Activation Enumeration Information
660  *
661  */
662 class TensorDataType final : public EnumProperty<TensorDataTypeInfo> {
663 public:
664   using prop_tag = enum_class_prop_tag;
665   static constexpr const char *key = "tensor_type";
666   TensorDataType(
667     TensorDataTypeInfo::Enum value = TensorDataTypeInfo::Enum::FP32) {
668     set(value);
669   };
670 };
671
672 /**
673  * @brief model tensor type : NCHW or NHWC
674  *
675  */
676 class TensorFormat : public nntrainer::Property<std::string> {
677 public:
678   static constexpr const char *key =
679     "tensor_format";             /**< unique key to access */
680   using prop_tag = str_prop_tag; /**< property type */
681
682   /**
683    * @brief Constructor
684    *
685    * @param value value to set, defaults to false
686    */
687   TensorFormat(const std::string &value = "NCHW") { set(value); };
688 };
689 } // namespace props
690
691 } // namespace nntrainer
692
693 #endif // __BASE_PROPERTIES_H__