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