Publishing 2019 R1.1 content and Myriad plugin sources (#162)
[platform/upstream/dldt.git] / inference-engine / src / vpu / graph_transformer / src / model / data_desc.cpp
1 // Copyright (C) 2018-2019 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 //
4
5 #include <vpu/model/data_desc.hpp>
6
7 #include <array>
8 #include <algorithm>
9 #include <queue>
10 #include <memory>
11 #include <vector>
12 #include <unordered_map>
13 #include <unordered_set>
14 #include <limits>
15
16 #include <precision_utils.h>
17
18 #include <vpu/model/edges.hpp>
19 #include <vpu/utils/ie_helpers.hpp>
20 #include <vpu/utils/numeric.hpp>
21
22 namespace vpu {
23
24 //
25 // DimsOrder
26 //
27
28 namespace {
29
30 const StorageOrder64 ORDER_MASK = static_cast<StorageOrder64>(-1ull) >> (std::numeric_limits<StorageOrder64>::digits / 4 - MAX_DIMS_64);
31
32 }  // namespace
33
34 StorageOrder64 maskOrder(StorageOrder64 fullOrder, int size) {
35     StorageOrder64 mask = ~ORDER_MASK | ~(static_cast<StorageOrder64>(-1ull) << (size * 4));
36     return fullOrder & mask;
37 }
38
39 DimsOrder DimsOrder::C = DimsOrder::fromCode(0x3);
40 DimsOrder DimsOrder::NC = DimsOrder::fromCode(0x43);
41 DimsOrder DimsOrder::CHW = DimsOrder::fromCode(0x321);
42 DimsOrder DimsOrder::HWC = DimsOrder::fromCode(0x213);
43 DimsOrder DimsOrder::HCW = DimsOrder::fromCode(0x231);
44 DimsOrder DimsOrder::NCHW = DimsOrder::fromCode(0x4321);
45 DimsOrder DimsOrder::NHWC = DimsOrder::fromCode(0x4213);
46 DimsOrder DimsOrder::NHCW = DimsOrder::fromCode(0x4231);
47
48 namespace {
49
50 bool isOrderCodeValid(StorageOrder64 order) {
51     if (order == 0) {
52         return false;
53     }
54
55     std::unordered_set<int> usedDims;
56
57     auto orderCopy = order;
58
59     int length = 0;
60
61     for (int i = 0; i < MAX_DIMS_64; i++) {
62         int digit = orderCopy & 0xF;
63         if (digit == 0) {
64             break;
65         }
66
67         --digit;
68
69         // Dimension is used more than once
70         if (usedDims.count(digit) > 0) {
71             return false;
72         }
73         usedDims.insert(digit);
74
75         length = i + 1;
76
77         orderCopy >>= 4;
78     }
79
80     orderCopy = order >> (4 * length);
81
82     // All digits on positions upper or equal to the order length should be UNDEF
83     for (int i = length; i < MAX_DIMS_64; i++) {
84         int digit = orderCopy & 0xF;
85         if (digit != 0) {
86             break;
87         }
88
89         orderCopy >>= 4;
90     }
91
92     return true;
93 }
94
95 }  // namespace
96
97 DimsOrder DimsOrder::fromCode(StorageOrder64 code) {
98     IE_ASSERT(isOrderCodeValid(code));
99     DimsOrder out;
100     out._code = code;
101     return out;
102 }
103
104 DimsOrder DimsOrder::fromNumDims(int numDims) {
105     static const StorageOrder64 FULL_ORDER_DEFAULT =
106             maskOrder(static_cast<StorageOrder64>(0x0fedcba987654321ull), MAX_DIMS_64);
107
108     if (numDims == 1) {
109         return DimsOrder::C;
110     } else if (numDims == 2) {
111         return DimsOrder::NC;
112     } else {
113         return DimsOrder::fromCode(maskOrder(FULL_ORDER_DEFAULT, numDims));
114     }
115 }
116
117 DimsOrder DimsOrder::fromPermutation(const std::vector<Dim>& perm) {
118     StorageOrder64 code = 0;
119
120     for (int sh = 0, i = 0; i < perm.size(); i++, sh += 4) {
121         code += (((static_cast<StorageOrder64>(perm[i]) + 1ull) & 0xFull) << sh);
122     }
123
124     return DimsOrder::fromCode(code);
125 }
126
127 int DimsOrder::numDims() const {
128     int out = 0;
129
130     auto code = _code;
131
132     for (int i = 0; i < MAX_DIMS_64; i++) {
133         auto digit = code & 0xF;
134         if (digit == 0)
135             break;
136
137         ++out;
138
139         code >>= 4;
140     }
141
142     return out;
143 }
144
145 bool DimsOrder::hasDim(Dim d) const {
146     auto dimDigit = static_cast<int>(d) + 1;
147
148     auto code = _code;
149
150     for (int i = 0; i < MAX_DIMS_64; i++) {
151         auto digit = code & 0xF;
152         if (digit == 0)
153             break;
154
155         if (digit == dimDigit) {
156             return true;
157         }
158
159         code >>= 4;
160     }
161
162     return false;
163 }
164
165 int DimsOrder::dimInd(Dim d) const {
166     auto dimDigit = static_cast<int>(d) + 1;
167
168     auto code = _code;
169
170     for (int i = 0; i < MAX_DIMS_64; i++) {
171         auto digit = code & 0xF;
172         if (digit == 0)
173             break;
174
175         if (digit == dimDigit) {
176             return i;
177         }
178
179         code >>= 4;
180     }
181
182     VPU_THROW_EXCEPTION << "Dim " << d << " is not avaialble in layout " << toString(*this);
183 }
184
185 std::vector<Dim> DimsOrder::toPermutation() const {
186     std::vector<Dim> out;
187     out.reserve(MAX_DIMS_64);
188
189     auto code = _code;
190
191     for (int i = 0; i < MAX_DIMS_64; i++) {
192         auto digit = code & 0xF;
193         if (digit == 0)
194             break;
195
196         auto d = static_cast<Dim>(digit - 1);
197         out.emplace_back(d);
198
199         code >>= 4;
200     }
201
202     return out;
203 }
204
205 DimValues DimsOrder::toIndices() const {
206     DimValues out;
207
208     auto code = _code;
209
210     for (int i = 0; i < MAX_DIMS_64; i++) {
211         auto digit = code & 0xF;
212         if (digit == 0)
213             break;
214
215         auto d = static_cast<Dim>(digit - 1);
216         out.set(d, i);
217
218         code >>= 4;
219     }
220
221     return out;
222 }
223
224 void DimsOrder::moveDim(Dim dim, int newPos) {
225     IE_ASSERT(newPos >= 0 && newPos < numDims());
226
227     int oldPos = dimInd(dim);
228     if (oldPos == newPos)
229         return;
230
231     auto step = (oldPos > newPos) ? -1 : 1;
232
233     auto perm = toPermutation();
234     IE_ASSERT(newPos < perm.size());
235
236     for (int i = oldPos; i != newPos; i += step) {
237         perm[i] = perm[i + step];
238     }
239
240     perm[newPos] = dim;
241
242     _code = fromPermutation(perm).code();
243 }
244
245 DimsOrder DimsOrder::createMovedDim(Dim dim, int newPos) const {
246     auto copy = *this;
247     copy.moveDim(dim, newPos);
248     return copy;
249 }
250
251 bool isOrdersCompatible(DimsOrder order1, DimsOrder order2) {
252     auto vec1 = order1.toPermutation();
253     auto vec2 = order2.toPermutation();
254
255     std::sort(vec1.begin(), vec1.end());
256     std::sort(vec2.begin(), vec2.end());
257
258     return vec1 == vec2;
259 }
260
261 void printTo(std::ostream& os, DimsOrder order) {
262     static std::unordered_map<int, char> DIM_NAMES({
263         {1, 'W'},
264         {2, 'H'},
265         {3, 'C'},
266         {4, 'N'}
267     });
268
269     auto code = order.code();
270
271     int i = MAX_DIMS_64 - 1;
272
273     for (; i >= 0; i--) {
274         auto curDim = (code >> (i * 4)) & 0xF;
275
276         if (curDim != 0)
277             break;
278     }
279
280     for (; i >= 0; i--) {
281         auto curDim = (code >> (i * 4)) & 0xF;
282
283         auto it = DIM_NAMES.find(curDim);
284         if (it != DIM_NAMES.end()) {
285             os << it->second;
286         } else {
287             os << curDim;
288         }
289     }
290 }
291
292 //
293 // DataDesc
294 //
295
296 DataDesc::DataDesc(const ie::TensorDesc& ieDesc) {
297     //
298     // Parse precision
299     //
300
301     switch (ieDesc.getPrecision()) {
302     case ie::Precision::U8:
303         _type = DataType::U8;
304         break;
305     case ie::Precision::FP16:
306         _type = DataType::FP16;
307         break;
308     case ie::Precision::FP32:
309         _type = DataType::FP32;
310         break;
311     default:
312         VPU_THROW_EXCEPTION << "Unsupported precision " << ieDesc.getPrecision().name();
313     }
314
315     //
316     // Parse dimensions and layout
317     //
318
319     const auto& ieDims = ieDesc.getDims();
320     IE_ASSERT(!ieDims.empty());
321
322     _dimsOrder = DimsOrder::fromNumDims(ieDims.size());
323
324     auto perm = _dimsOrder.toPermutation();
325
326     for (int i = 0; i < perm.size(); ++i) {
327         _dims.set(perm[i], ieDims[ieDims.size() - 1 - i]);
328     }
329 }
330
331 DataDesc::DataDesc(DataType type, DimsOrder dimsOrder, const DimValues& dims) :
332         _type(type), _dimsOrder(dimsOrder), _dims(dims) {
333     IE_ASSERT(_dimsOrder.numDims() == _dims.size());
334     for (const auto& p : _dims) {
335         IE_ASSERT(_dimsOrder.hasDim(p.first));
336     }
337 }
338
339 int DataDesc::elemSize() const {
340     switch (_type) {
341     case DataType::U8:
342         return sizeof(uint8_t);
343     case DataType::FP16:
344         return sizeof(fp16_t);
345     case DataType::FP32:
346         return sizeof(float);
347     default:
348         VPU_THROW_EXCEPTION << "Unknown data type " << _type;
349     }
350 }
351
352 void DataDesc::setDim(Dim d, int val) {
353     IE_ASSERT(_dimsOrder.hasDim(d));
354     _dims.set(d, val);
355 }
356
357 int DataDesc::totalDimSize() const {
358     int total = 1;
359
360     auto perm = _dimsOrder.toPermutation();
361     for (auto d : perm) {
362         total *= _dims[d];
363     }
364
365     return total;
366 }
367
368 void DataDesc::reorder(DimsOrder dimsOrder) {
369     IE_ASSERT(isOrdersCompatible(_dimsOrder, dimsOrder));
370     _dimsOrder = dimsOrder;
371 }
372
373 void printTo(std::ostream& os, const DataDesc& desc) {
374     os << "[" << std::endl;
375
376     os << "type=";
377     printTo(os, desc.type());
378     os << std::endl;
379
380     os << "dimsOrder=";
381     printTo(os, desc.dimsOrder());
382     os << std::endl;
383
384     os << "dims=";
385     printTo(os, desc.dims());
386     os << std::endl;
387
388     os << "]";
389 }
390
391 void printTo(DotLabel& lbl, const DataDesc& desc) {
392     DotLabel subLbl(lbl);
393     subLbl.appendPair("type", desc.type());
394     subLbl.appendPair("dimsOrder", desc.dimsOrder());
395     subLbl.appendPair("dims", desc.dims());
396 }
397
398 //
399 // StridesRequirement
400 //
401
402 StridesRequirement StridesRequirement::compact() {
403     StridesRequirement reqs;
404     for (int i = 0; i < MAX_DIMS_64; ++i) {
405         reqs.add(i, DimStride::Compact);
406     }
407     return reqs;
408 }
409
410 void printTo(std::ostream& os, const StridesRequirement& reqs) {
411     os << "[" << std::endl;
412
413     for (int i = 0; i < MAX_DIMS_64; ++i) {
414         auto req = reqs.get(i);
415         if (req != DimStride::Any) {
416             printTo(os, i);
417             os << "=";
418             printTo(os, req);
419             os << std::endl;
420         }
421     }
422
423     os << "]";
424 }
425
426 void printTo(DotLabel& lbl, const StridesRequirement& reqs) {
427     DotLabel subLbl(lbl);
428     for (int i = 0; i < MAX_DIMS_64; ++i) {
429         auto req = reqs.get(i);
430         if (req != DimStride::Any) {
431             subLbl.appendPair(i, req);
432         }
433     }
434 }
435
436 namespace {
437
438 int applyStrideRequirement(int origStride, int index, const StridesRequirement& reqs) {
439     auto req = reqs.get(index);
440
441     if (req == DimStride::Any || req == DimStride::Compact) {
442         return origStride;
443     } else if (req == DimStride::Aligned) {
444         return alignVal(origStride, STRIDE_ALIGNMENT);
445     } else {
446         VPU_THROW_EXCEPTION << "Unknown stride requirement : " << req;
447     }
448 }
449
450 }  // namespace
451
452 DimValues calcStrides(const DataDesc& desc, const StridesRequirement& reqs) {
453     DimValues strides;
454
455     auto perm = desc.dimsOrder().toPermutation();
456     IE_ASSERT(!perm.empty());
457
458     strides.set(perm[0], desc.elemSize());
459     strides.set(perm[0], applyStrideRequirement(strides[perm[0]], 0, reqs));
460
461     for (int i = 1; i < perm.size(); i++) {
462         strides.set(perm[i], strides[perm[i - 1]] * desc.dim(perm[i - 1]));
463         strides.set(perm[i], applyStrideRequirement(strides[perm[i]], i, reqs));
464     }
465
466     return strides;
467 }
468
469 bool checkStride(
470         const DimValues& strides,
471         const DataDesc& desc,
472         int ind,
473         DimStride req) {
474     if (req == DimStride::Any) {
475         return true;
476     }
477
478     auto perm = desc.dimsOrder().toPermutation();
479     IE_ASSERT(!perm.empty());
480
481     auto strideVal = strides[perm[ind]];
482
483     if (req == DimStride::Compact) {
484         if (ind == 0) {
485             if (strideVal != desc.elemSize()) {
486                 return false;
487             }
488         } else {
489             if (strides[perm[ind]] != strides[perm[ind - 1]] * desc.dim(perm[ind - 1])) {
490                 return false;
491             }
492         }
493     } else if (req == DimStride::Aligned) {
494         if (strideVal % STRIDE_ALIGNMENT != 0) {
495             return false;
496         }
497     } else {
498         VPU_THROW_EXCEPTION << "Unsupported stride requirement : " << req;
499     }
500
501     return true;
502 }
503
504 bool checkStrides(
505         const DataDesc& desc,
506         const DimValues& strides,
507         const StridesRequirement& reqs) {
508     auto perm = desc.dimsOrder().toPermutation();
509     IE_ASSERT(!perm.empty());
510
511     for (int i = 0; i < perm.size(); i++) {
512         if (!checkStride(strides, desc, i, reqs.get(i))) {
513             return false;
514         }
515     }
516
517     return true;
518 }
519
520 int calcTotalByteSize(const DataDesc& desc, const DimValues& strides) {
521     auto perm = desc.dimsOrder().toPermutation();
522     return strides[perm.back()] * desc.dim(perm.back());
523 }
524
525 }  // namespace vpu