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