Publishing 2019 R1.1 content and Myriad plugin sources (#162)
[platform/upstream/dldt.git] / inference-engine / include / ie_precision.hpp
1 // Copyright (C) 2018-2019 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 //
4
5 /**
6  * @brief A header file that provides class for describing precision of data
7  * @file ie_precision.hpp
8  */
9 #pragma once
10 #include <unordered_map>
11 #include <string>
12 #include "details/ie_exception.hpp"
13
14 namespace InferenceEngine {
15
16 /**
17  * @brief This class holds precision value and provides precision related operations
18  */
19 class Precision {
20 public:
21     /** Enum to specify of different  */
22     enum ePrecision : uint8_t {
23         UNSPECIFIED = 255, /**< Unspecified value. Used by default */
24         MIXED = 0,  /**< Mixed value. Can be received from network. No applicable for tensors */
25         FP32 = 10,  /**< 32bit floating point value */
26         FP16 = 11,  /**< 16bit floating point value */
27         Q78 = 20,   /**< 16bit specific signed fixed point precision */
28         I16 = 30,   /**< 16bit signed integer value */
29         U8 = 40,    /**< 8bit unsigned integer value */
30         I8 = 50,    /**< 8bit signed integer value */
31         U16 = 60,   /**< 16bit unsigned integer value */
32         I32 = 70,   /**< 32bit signed integer value */
33         BIN = 71,   /**< 1bit integer value */
34         CUSTOM = 80 /**< custom precision has it's own name and size of elements */
35     };
36
37 private:
38     struct PrecisionInfo {
39         /** @brief Size of underlined element */
40         size_t bitsSize = 0;
41
42         /** @brief Null terminated string with precision name */
43         const char *name = "UNSPECIFIED";
44
45         bool isFloat     = false;
46         ePrecision value = Precision::UNSPECIFIED;
47     };
48     PrecisionInfo precisionInfo;
49
50 public:
51     /** @brief Default constructor */
52     Precision()  = default;
53
54     /** @brief Constructor with specified precision */
55     Precision(const Precision::ePrecision  value) {  // NOLINT
56         precisionInfo = getPrecisionInfo(value);
57     }
58
59     /**
60      * @brief Custom precision constructor
61      * @param byteSize size of elements
62      * @param name optional name string, used in serialisation
63      */
64     explicit Precision(size_t bitsSize, const char * name = nullptr) {
65         if (bitsSize == 0) {
66             THROW_IE_EXCEPTION << "Precision with 0 elements size not supported";
67         }
68         precisionInfo.bitsSize = bitsSize;
69         if (name == nullptr) {
70             precisionInfo.name = "CUSTOM";
71         } else {
72             precisionInfo.name = name;
73         }
74         precisionInfo.value = CUSTOM;
75     }
76
77     /** @brief Creates custom precision with specific underlined type */
78     template <class T>
79     static Precision fromType(const char * typeName = nullptr) {
80         return Precision(8 * sizeof(T), typeName == nullptr ? typeid(T).name() : typeName);
81     }
82
83     /** @brief checks whether given storage class T can be used to store objects of current precision */
84     template <class T>
85     bool hasStorageType(const char * typeName = nullptr) const noexcept {
86         try {
87             if (precisionInfo.value != BIN) {
88                 if (sizeof(T) != size()) {
89                     return false;
90                 }
91             }
92 #define CASE(x, y) case x: return std::is_same<T, y>()
93 #define CASE2(x, y1, y2) case x: return std::is_same<T, y1>() || std::is_same<T, y2>()
94
95             switch (precisionInfo.value) {
96                 CASE(FP32, float);
97                 CASE2(FP16, int16_t, uint16_t);
98                 CASE(I16, int16_t);
99                 CASE(I32, int32_t);
100                 CASE(U16, uint16_t);
101                 CASE(U8, uint8_t);
102                 CASE(I8, int8_t);
103                 CASE2(Q78, int16_t, uint16_t);
104                 CASE2(BIN, int8_t, uint8_t);
105                 default :
106                     return areSameStrings(name(), typeName == nullptr ? typeid(T).name() : typeName);
107 #undef CASE
108 #undef CASE2
109             }
110         } catch (...) {
111             return false;
112         }
113     }
114
115     /** @brief Equality operator with Precision object */
116     bool operator == (const Precision  & p) const noexcept {
117         return precisionInfo.value == p &&
118             precisionInfo.bitsSize == p.precisionInfo.bitsSize &&
119             areSameStrings(precisionInfo.name, p.precisionInfo.name);
120     }
121
122     /** @brief Equality operator with ePrecision enum value */
123     bool operator == (const ePrecision  p) const noexcept {
124         return precisionInfo.value == p;
125     }
126
127     /** @brief Inequality operator with ePrecision enum value */
128     bool operator != (const ePrecision   p) const noexcept {
129         return precisionInfo.value != p;
130     }
131
132     /** @brief Assignment operator with ePrecision enum value */
133     Precision & operator = (const ePrecision p) noexcept {
134         precisionInfo = getPrecisionInfo(p);
135         return *this;
136     }
137
138     /** @brief Cast operator to a bool */
139     explicit operator bool() const noexcept {
140         return precisionInfo.value != UNSPECIFIED;
141     }
142
143     /** @brief Logical negation operator */
144     bool operator !() const noexcept {
145         return precisionInfo.value == UNSPECIFIED;
146     }
147
148     /** @brief Cast operator to a ePrecision */
149     operator Precision::ePrecision  () const noexcept {
150         return precisionInfo.value;
151     }
152
153     /** @brief Getter of precision name */
154     const char *name() const noexcept {
155         return precisionInfo.name;
156     }
157
158     /** @brief Creates from string with precision name */
159     static Precision FromStr(const std::string &str) {
160         static std::unordered_map<std::string, ePrecision > names = {
161 #define     PRECISION_NAME(s) {#s, s}
162             PRECISION_NAME(Q78),
163             PRECISION_NAME(U8),
164             PRECISION_NAME(I8),
165             PRECISION_NAME(I16),
166             PRECISION_NAME(I32),
167             PRECISION_NAME(U16),
168             PRECISION_NAME(FP32),
169             PRECISION_NAME(FP16),
170             PRECISION_NAME(MIXED),
171             PRECISION_NAME(BIN),
172 #undef      PRECISION_NAME
173         };
174         auto i = names.find(str);
175         return i == names.end() ? Precision() : Precision(i->second);
176     }
177
178     /**
179      * @brief Returns size in bytes of single element of that precision
180      * @deprecated : size of precision will be reported in bits in future releases
181      */
182     size_t size() const {
183         if (precisionInfo.bitsSize == 0) {
184             THROW_IE_EXCEPTION << " cannot estimate element if precision is " << precisionInfo.name;
185         }
186         return precisionInfo.bitsSize >> 3;
187     }
188
189     /** @brief Checks if it is a floating point */
190     bool is_float() const noexcept {
191         return precisionInfo.isFloat;
192     }
193
194  protected:
195     template<Precision::ePrecision precision>
196     static PrecisionInfo makePrecisionInfo(const char * name);
197
198     static bool areSameStrings(const char *l, const char *r) noexcept {
199         if (l == r)
200             return true;
201
202         if (l == nullptr || r == nullptr)
203             return false;
204
205         for (; *l && *r; l++, r++) {
206             if (*l != *r) return false;
207         }
208         return *l == *r;
209     }
210
211     static PrecisionInfo getPrecisionInfo(ePrecision v) {
212 #define CASE(x) case x: return makePrecisionInfo<x>(#x);
213         switch (v) {
214             CASE(FP32);
215             CASE(FP16);
216             CASE(I16);
217             CASE(I32);
218             CASE(U16);
219             CASE(U8);
220             CASE(I8);
221             CASE(Q78);
222             CASE(MIXED);
223             CASE(BIN);
224             default : return makePrecisionInfo<UNSPECIFIED>("UNSPECIFIED");
225 #undef CASE
226         }
227     }
228 };
229
230 /**
231  * @brief Particular precision traits
232  */
233 template<Precision::ePrecision p>
234 struct PrecisionTrait {
235 };
236
237 /** @cond INTERNAL */
238 template<>
239 struct PrecisionTrait<Precision::FP32> {
240     using value_type = float;
241 };
242
243 template<>
244 struct PrecisionTrait<Precision::FP16> {
245     using value_type = int16_t;
246 };
247 template<>
248 struct PrecisionTrait<Precision::Q78> {
249     using value_type = uint16_t;
250 };
251 template<>
252 struct PrecisionTrait<Precision::I16> {
253     using value_type = int16_t;
254 };
255 template<>
256 struct PrecisionTrait<Precision::U16> {
257     using value_type = uint16_t;
258 };
259 template<>
260 struct PrecisionTrait<Precision::U8> {
261     using value_type = uint8_t;
262 };
263 template<>
264 struct PrecisionTrait<Precision::I8> {
265     using value_type = int8_t;
266 };
267 template<>
268 struct PrecisionTrait<Precision::I32> {
269     using value_type = int32_t;
270 };
271 template<>
272 struct PrecisionTrait<Precision::BIN> {
273     using value_type = int8_t;
274 };
275
276 template<class T>
277 inline uint8_t type_size_or_zero() {
278     return sizeof(T);
279 }
280
281 template<>
282 struct PrecisionTrait<Precision::UNSPECIFIED> {
283     using value_type = void;
284 };
285
286 template<>
287 struct PrecisionTrait<Precision::MIXED> : PrecisionTrait<Precision::UNSPECIFIED>{
288 };
289
290 template<>
291 inline uint8_t type_size_or_zero<void>() {
292     return 0;
293 }
294
295 template<Precision::ePrecision T>
296 inline typename std::enable_if<std::is_same<
297     std::integral_constant<Precision::ePrecision, Precision::FP16>,
298     std::integral_constant<Precision::ePrecision, T>>::value, bool>::type is_floating() {
299     return true;
300 }
301
302 template<Precision::ePrecision T>
303 inline typename std::enable_if<!std::is_same<
304     std::integral_constant<Precision::ePrecision, Precision::FP16>,
305     std::integral_constant<Precision::ePrecision, T>>::value, bool>::type is_floating() {
306     return std::is_floating_point<typename PrecisionTrait<T>::value_type>::value;
307 }
308
309 template<Precision::ePrecision precision>
310 inline Precision::PrecisionInfo Precision::makePrecisionInfo(const char *name) {
311     Precision::PrecisionInfo info;
312     info.name = name;
313
314     size_t nBits = precision == BIN ? 1 : 8;
315     info.bitsSize = nBits * type_size_or_zero<typename PrecisionTrait<precision>::value_type>();
316     info.isFloat = is_floating<precision>();
317     info.value = precision;
318     return info;
319 }
320
321 inline std::ostream & operator << (std::ostream &out, const InferenceEngine::Precision & p) {
322     return out << p.name();
323 }
324
325 inline std::ostream & operator << (std::ostream &out, const InferenceEngine::Precision::ePrecision & p) {
326     return out << Precision(p).name();
327 }
328
329 /** @endcond */
330
331 }  // namespace InferenceEngine