updated readme file due to moving CMake scripts to the root folder
[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 DimsOrder DimsOrder::NCDHW = DimsOrder::fromCode(0x43521);
48 DimsOrder DimsOrder::NDHWC = DimsOrder::fromCode(0x45213);
49
50 namespace {
51
52 bool isOrderCodeValid(StorageOrder64 order) {
53     if (order == 0) {
54         return false;
55     }
56
57     std::unordered_set<int> usedDims;
58
59     auto orderCopy = order;
60
61     int length = 0;
62
63     for (int i = 0; i < MAX_DIMS_64; i++) {
64         int digit = orderCopy & 0xF;
65         if (digit == 0) {
66             break;
67         }
68
69         --digit;
70
71         // Dimension is used more than once
72         if (usedDims.count(digit) > 0) {
73             return false;
74         }
75         usedDims.insert(digit);
76
77         length = i + 1;
78
79         orderCopy >>= 4;
80     }
81
82     orderCopy = order >> (4 * length);
83
84     // All digits on positions upper or equal to the order length should be UNDEF
85     for (int i = length; i < MAX_DIMS_64; i++) {
86         int digit = orderCopy & 0xF;
87         if (digit != 0) {
88             break;
89         }
90
91         orderCopy >>= 4;
92     }
93
94     return true;
95 }
96
97 }  // namespace
98
99 DimsOrder DimsOrder::fromCode(StorageOrder64 code) {
100     IE_ASSERT(isOrderCodeValid(code));
101     DimsOrder out;
102     out._code = code;
103     return out;
104 }
105
106 DimsOrder DimsOrder::fromNumDims(int numDims) {
107     static const StorageOrder64 FULL_ORDER_DEFAULT =
108             maskOrder(static_cast<StorageOrder64>(0x0fedcba987654321ull), MAX_DIMS_64);
109
110     if (numDims == 1) {
111         return DimsOrder::C;
112     } else if (numDims == 2) {
113         return DimsOrder::NC;
114     } else if (numDims == 3) {
115         return DimsOrder::CHW;
116     } else if (numDims == 4) {
117         return DimsOrder::NCHW;
118     } else if (numDims == 5) {
119         return DimsOrder::NCDHW;
120     } else {
121         return DimsOrder::fromCode(maskOrder(FULL_ORDER_DEFAULT, numDims));
122     }
123 }
124
125 DimsOrder DimsOrder::fromPermutation(const DimVector& perm) {
126     StorageOrder64 code = 0;
127
128     for (int sh = 0, i = 0; i < perm.size(); i++, sh += 4) {
129         code += (((static_cast<StorageOrder64>(perm[i]) + 1ull) & 0xFull) << sh);
130     }
131
132     return DimsOrder::fromCode(code);
133 }
134
135 DimsOrder DimsOrder::fromLayout(ie::Layout const& layout) {
136     switch (layout) {
137     case ie::Layout::C     : return DimsOrder::C;
138     case ie::Layout::NC    : return DimsOrder::NC;
139     case ie::Layout::CHW   : return DimsOrder::CHW;
140     case ie::Layout::NCHW  : return DimsOrder::NCHW;
141     case ie::Layout::NHWC  : return DimsOrder::NHWC;
142     case ie::Layout::NCDHW : return DimsOrder::NCDHW;
143     case ie::Layout::NDHWC : return DimsOrder::NDHWC;
144     default:
145         VPU_THROW_EXCEPTION << "Unsupported layout " << layout;
146     }
147 }
148
149 int DimsOrder::numDims() const {
150     int out = 0;
151
152     auto code = _code;
153
154     for (int i = 0; i < MAX_DIMS_64; i++) {
155         auto digit = code & 0xF;
156         if (digit == 0)
157             break;
158
159         ++out;
160
161         code >>= 4;
162     }
163
164     return out;
165 }
166
167 bool DimsOrder::hasDim(Dim d) const {
168     auto dimDigit = static_cast<int>(d) + 1;
169
170     auto code = _code;
171
172     for (int i = 0; i < MAX_DIMS_64; i++) {
173         auto digit = code & 0xF;
174         if (digit == 0)
175             break;
176
177         if (digit == dimDigit) {
178             return true;
179         }
180
181         code >>= 4;
182     }
183
184     return false;
185 }
186
187 int DimsOrder::dimInd(Dim d) const {
188     auto dimDigit = static_cast<int>(d) + 1;
189
190     auto code = _code;
191
192     for (int i = 0; i < MAX_DIMS_64; i++) {
193         auto digit = code & 0xF;
194         if (digit == 0)
195             break;
196
197         if (digit == dimDigit) {
198             return i;
199         }
200
201         code >>= 4;
202     }
203
204     VPU_THROW_EXCEPTION << "Dim " << d << " is not avaialble in layout " << toString(*this);
205 }
206
207 SmallVector<Dim, MAX_DIMS_64> DimsOrder::toPermutation() const {
208     SmallVector<Dim, MAX_DIMS_64> out;
209
210     auto code = _code;
211
212     for (int i = 0; i < MAX_DIMS_64; i++) {
213         auto digit = code & 0xF;
214         if (digit == 0)
215             break;
216
217         auto d = static_cast<Dim>(digit - 1);
218         out.emplace_back(d);
219
220         code >>= 4;
221     }
222
223     return out;
224 }
225
226 DimValues DimsOrder::toIndices() const {
227     DimValues out;
228
229     auto code = _code;
230
231     for (int i = 0; i < MAX_DIMS_64; i++) {
232         auto digit = code & 0xF;
233         if (digit == 0)
234             break;
235
236         auto d = static_cast<Dim>(digit - 1);
237         out.set(d, i);
238
239         code >>= 4;
240     }
241
242     return out;
243 }
244
245 void DimsOrder::moveDim(Dim dim, int newPos) {
246     IE_ASSERT(newPos >= 0 && newPos < numDims());
247
248     int oldPos = dimInd(dim);
249     if (oldPos == newPos)
250         return;
251
252     auto step = (oldPos > newPos) ? -1 : 1;
253
254     auto perm = toPermutation();
255     IE_ASSERT(newPos < perm.size());
256
257     for (int i = oldPos; i != newPos; i += step) {
258         perm[i] = perm[i + step];
259     }
260
261     perm[newPos] = dim;
262
263     _code = fromPermutation(perm).code();
264 }
265
266 DimsOrder DimsOrder::createMovedDim(Dim dim, int newPos) const {
267     auto copy = *this;
268     copy.moveDim(dim, newPos);
269     return copy;
270 }
271
272 bool isOrdersCompatible(DimsOrder order1, DimsOrder order2) {
273     auto vec1 = order1.toPermutation();
274     auto vec2 = order2.toPermutation();
275
276     std::sort(vec1.begin(), vec1.end());
277     std::sort(vec2.begin(), vec2.end());
278
279     return vec1 == vec2;
280 }
281
282 void printTo(std::ostream& os, DimsOrder order) {
283     static std::unordered_map<int, char> DIM_NAMES({
284         {1, 'W'},
285         {2, 'H'},
286         {3, 'C'},
287         {4, 'N'},
288         {5, 'D'}
289     });
290
291     auto code = order.code();
292
293     int i = MAX_DIMS_64 - 1;
294
295     for (; i >= 0; i--) {
296         auto curDim = (code >> (i * 4)) & 0xF;
297
298         if (curDim != 0)
299             break;
300     }
301
302     for (; i >= 0; i--) {
303         auto curDim = (code >> (i * 4)) & 0xF;
304
305         auto it = DIM_NAMES.find(curDim);
306         if (it != DIM_NAMES.end()) {
307             os << it->second;
308         } else {
309             os << curDim;
310         }
311     }
312 }
313
314 //
315 // Dim
316 //
317
318 int dimToIeInd(vpu::Dim const& dim, int numDims) {
319     IE_ASSERT(1 <= numDims && numDims <= 8);
320     auto dimsOrder =  DimsOrder::fromNumDims(numDims);
321     int dimInd = dimsOrder.dimInd(dim);
322     return (numDims - 1) - dimInd;
323 }
324
325 //
326 // DataDesc
327 //
328
329 DataDesc::DataDesc(const ie::TensorDesc& ieDesc) {
330     //
331     // Parse precision
332     //
333
334     _type = fromIEPrecision(ieDesc.getPrecision());
335
336     //
337     // Parse dimensions and layout
338     //
339
340     const auto& ieDims = ieDesc.getDims();
341     IE_ASSERT(!ieDims.empty());
342
343     _dimsOrder = DimsOrder::fromNumDims(ieDims.size());
344
345     auto perm = _dimsOrder.toPermutation();
346
347     for (int i = 0; i < perm.size(); ++i) {
348         _dims.set(perm[i], ieDims[ieDims.size() - 1 - i]);
349     }
350 }
351
352 DataDesc::DataDesc(DataType type, DimsOrder dimsOrder, const DimValues& dims) :
353         _type(type), _dimsOrder(dimsOrder), _dims(dims) {
354     IE_ASSERT(_dimsOrder.numDims() == _dims.size());
355     for (const auto& p : _dims) {
356         IE_ASSERT(_dimsOrder.hasDim(p.first));
357     }
358 }
359
360 int DataDesc::elemSize() const {
361     switch (_type) {
362     case DataType::U8:
363         return sizeof(uint8_t);
364     case DataType::I8:
365         return sizeof(int8_t);
366     case DataType::FP16:
367         return sizeof(fp16_t);
368     case DataType::FP32:
369         return sizeof(float);
370     case DataType::S32:
371         return sizeof(int32_t);
372     default:
373         VPU_THROW_EXCEPTION << "Unknown data type " << _type;
374     }
375 }
376
377 void DataDesc::setDim(Dim d, int val) {
378     IE_ASSERT(_dimsOrder.hasDim(d));
379     _dims.set(d, val);
380 }
381
382 int DataDesc::totalDimSize() const {
383     int total = 1;
384
385     auto perm = _dimsOrder.toPermutation();
386     for (auto d : perm) {
387         total *= _dims[d];
388     }
389
390     return total;
391 }
392
393 void DataDesc::reorder(DimsOrder dimsOrder) {
394     IE_ASSERT(isOrdersCompatible(_dimsOrder, dimsOrder));
395     _dimsOrder = dimsOrder;
396 }
397
398 ie::TensorDesc DataDesc::toTensorDesc() const {
399     ie::TensorDesc desc;
400
401     switch (this->type()) {
402         case DataType::FP16:
403             desc.setPrecision(ie::Precision::FP16);
404             break;
405         case DataType::FP32:
406             desc.setPrecision(ie::Precision::FP32);
407             break;
408         case DataType::I8:
409             desc.setPrecision(ie::Precision::I8);
410             break;
411         case DataType::U8:
412             desc.setPrecision(ie::Precision::U8);
413             break;
414         case DataType::S32:
415             desc.setPrecision(ie::Precision::I32);
416             break;
417         default:
418             desc.setPrecision(ie::Precision::UNSPECIFIED);
419     }
420
421     ie::SizeVector dims{};
422
423     DataDesc descCopy = *this;
424     descCopy.reorder(DimsOrder::fromNumDims(this->numDims()));
425     auto perm = descCopy.dimsOrder().toPermutation();
426     std::reverse(perm.begin(), perm.end());
427     for (auto &p : perm) {
428         dims.push_back(descCopy.dim(p));
429     }
430
431     desc.setDims(dims);
432
433     if (DimsOrder::C == this->dimsOrder()) {
434         desc.setLayout(ie::Layout::C);
435     } else if (DimsOrder::NC == this->dimsOrder()) {
436         desc.setLayout(ie::Layout::NC);
437     } else if (DimsOrder::CHW == this->dimsOrder()) {
438         desc.setLayout(ie::Layout::CHW);
439     } else if (DimsOrder::NCHW == this->dimsOrder()) {
440         desc.setLayout(ie::Layout::NCHW);
441     } else if (DimsOrder::NHWC == this->dimsOrder()) {
442         desc.setLayout(ie::Layout::NHWC);
443     } else if (DimsOrder::NCDHW == this->dimsOrder()) {
444         desc.setLayout(ie::Layout::NCDHW);
445     } else if (DimsOrder::NDHWC == this->dimsOrder()) {
446         desc.setLayout(ie::Layout::NDHWC);
447     } else {
448         desc.setLayout(ie::Layout::BLOCKED);
449     }
450
451     return desc;
452 }
453
454 void printTo(std::ostream& os, const DataDesc& desc) {
455     os << "[" << std::endl;
456
457     os << "type=";
458     printTo(os, desc.type());
459     os << std::endl;
460
461     os << "dimsOrder=";
462     printTo(os, desc.dimsOrder());
463     os << std::endl;
464
465     os << "dims=";
466     printTo(os, desc.dims());
467     os << std::endl;
468
469     os << "]";
470 }
471
472 void printTo(DotLabel& lbl, const DataDesc& desc) {
473     DotLabel subLbl(lbl);
474     subLbl.appendPair("type", desc.type());
475     subLbl.appendPair("dimsOrder", desc.dimsOrder());
476     subLbl.appendPair("dims", desc.dims());
477 }
478
479 //
480 // StridesRequirement
481 //
482
483 StridesRequirement StridesRequirement::compact() {
484     StridesRequirement reqs;
485     for (int i = 0; i < MAX_DIMS_64; ++i) {
486         reqs.add(i, DimStride::Compact);
487     }
488     return reqs;
489 }
490
491 StridesRequirement StridesRequirement::fixed(const std::vector<int>& strides, const DataDesc& desc) {
492     StridesRequirement reqs;
493
494     const auto dims = desc.dims();
495     const auto dimsOrder = desc.dimsOrder();
496     const auto dimOrderVec = dimsOrder.toPermutation();
497     auto setStride = [&] (Dim d, int val) {
498         IE_ASSERT(dimsOrder.hasDim(d));
499
500         auto perm = dimsOrder.toPermutation();
501         auto idx = dimsOrder.dimInd(d);
502
503         auto minStrideVal = idx == 0 ? desc.elemSize() : reqs._fixedStrides[perm[idx - 1]] * dims[perm[idx - 1]];
504         IE_ASSERT(val >= minStrideVal);
505
506         reqs._fixedStrides.set(d, val);
507     };
508
509     for (const auto& dim : dimOrderVec) {
510         const auto idx = dimToIeInd(dim, dims.size());
511         setStride(dim, strides[idx]);
512     }
513
514     for (int i = 0; i < MAX_DIMS_64; ++i) {
515         reqs.add(i, DimStride::Fixed);
516     }
517     return reqs;
518 }
519
520 void printTo(std::ostream& os, const StridesRequirement& reqs) {
521     os << "[" << std::endl;
522
523     for (int i = 0; i < MAX_DIMS_64; ++i) {
524         auto req = reqs.get(i);
525         if (req != DimStride::Any) {
526             printTo(os, i);
527             os << "=";
528             printTo(os, req);
529             os << std::endl;
530         }
531     }
532
533     os << "]";
534 }
535
536 void printTo(DotLabel& lbl, const StridesRequirement& reqs) {
537     DotLabel subLbl(lbl);
538     for (int i = 0; i < MAX_DIMS_64; ++i) {
539         auto req = reqs.get(i);
540         if (req != DimStride::Any) {
541             subLbl.appendPair(i, req);
542         }
543     }
544 }
545
546 namespace {
547
548 int applyStrideRequirement(int origStride, int index, const StridesRequirement& reqs) {
549     auto req = reqs.get(index);
550
551     if (req == DimStride::Any || req == DimStride::Compact) {
552         return origStride;
553     } else if (req == DimStride::Aligned) {
554         return alignVal(origStride, STRIDE_ALIGNMENT);
555     } else {
556         VPU_THROW_EXCEPTION << "Unknown stride requirement : " << req;
557     }
558 }
559
560 }  // namespace
561
562 DimValues calcStrides(const DataDesc& desc, const StridesRequirement& reqs) {
563     DimValues strides;
564
565     auto perm = desc.dimsOrder().toPermutation();
566     IE_ASSERT(!perm.empty());
567
568     strides = reqs.fixedStrides();
569
570     if (strides.empty()) {
571         strides.set(perm[0], desc.elemSize());
572         strides.set(perm[0], applyStrideRequirement(strides[perm[0]], 0, reqs));
573
574         for (std::size_t i = 1; i < perm.size(); i++) {
575             strides.set(perm[i], strides[perm[i - 1]] * desc.dim(perm[i - 1]));
576             strides.set(perm[i], applyStrideRequirement(strides[perm[i]], i, reqs));
577         }
578     }
579
580     return strides;
581 }
582
583 bool checkStride(
584         const DimValues& strides,
585         const DataDesc& desc,
586         int ind,
587         const StridesRequirement& reqs) {
588     const auto req = reqs.get(ind);
589     if (req == DimStride::Any) {
590         return true;
591     }
592
593     auto perm = desc.dimsOrder().toPermutation();
594     IE_ASSERT(!perm.empty());
595
596     auto strideVal = strides[perm[ind]];
597
598     if (req == DimStride::Compact) {
599         if (ind == 0) {
600             if (strideVal != desc.elemSize()) {
601                 return false;
602             }
603         } else {
604             if (strides[perm[ind]] != strides[perm[ind - 1]] * desc.dim(perm[ind - 1])) {
605                 return false;
606             }
607         }
608     } else if (req == DimStride::Aligned) {
609         if (strideVal % STRIDE_ALIGNMENT != 0) {
610             return false;
611         }
612     } else if (req == DimStride::Fixed) {
613         if (strideVal != reqs.getFixedStride(perm[ind])) {
614             return false;
615         }
616     } else {
617         VPU_THROW_EXCEPTION << "Unsupported stride requirement : " << req;
618     }
619
620     return true;
621 }
622
623 bool checkStrides(
624         const DataDesc& desc,
625         const DimValues& strides,
626         const StridesRequirement& reqs) {
627     auto perm = desc.dimsOrder().toPermutation();
628     IE_ASSERT(!perm.empty());
629
630     for (int i = 0; i < perm.size(); i++) {
631         if (!checkStride(strides, desc, i, reqs)) {
632             return false;
633         }
634     }
635
636     return true;
637 }
638
639 int calcTotalByteSize(const DataDesc& desc, const DimValues& strides) {
640     auto perm = desc.dimsOrder().toPermutation();
641     return strides[perm.back()] * desc.dim(perm.back());
642 }
643
644 DataType fromIEPrecision(const InferenceEngine::Precision& precision) {
645     switch (precision) {
646         case InferenceEngine::Precision::U8:   return DataType::U8;
647         case InferenceEngine::Precision::I8:   return DataType::I8;
648         case InferenceEngine::Precision::I32:  return DataType::S32;
649         case InferenceEngine::Precision::FP16: return DataType::FP16;
650         case InferenceEngine::Precision::FP32: return DataType::FP32;
651         default: VPU_THROW_EXCEPTION << precision << " isn't supported";
652     }
653 }
654
655 }  // namespace vpu