Publishing 2019 R1.1 content and Myriad plugin sources (#162)
[platform/upstream/dldt.git] / inference-engine / src / vpu / graph_transformer / src / stages / eltwise.cpp
1 // Copyright (C) 2018-2019 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 //
4
5 #include <vpu/frontend/frontend.hpp>
6
7 #include <vector>
8 #include <string>
9 #include <unordered_set>
10 #include <memory>
11 #include <set>
12 #include <limits>
13 #include <algorithm>
14
15 #include <vpu/utils/numeric.hpp>
16
17 namespace vpu {
18
19 namespace {
20
21 class EltwiseStage final : public StageNode {
22 private:
23     StagePtr cloneImpl() const override {
24         return std::make_shared<EltwiseStage>(*this);
25     }
26
27     DataMap<float> propagateScaleFactorsImpl(
28             const DataMap<float>& inputScales,
29             ScalePropagationStep step) override {
30         IE_ASSERT(_inputEdges.size() == 2);
31         IE_ASSERT(_outputEdges.size() == 1);
32
33         auto output = _outputEdges[0]->output();
34
35         DataMap<float> out;
36
37         if (_type != StageType::Prod &&
38             step == ScalePropagationStep::Propagate) {
39             // Keep the largest input scale factor.
40             auto maxScale = std::numeric_limits<float>::lowest();
41             for (const auto& inEdge : _inputEdges) {
42                 maxScale = std::max(maxScale, inputScales.at(inEdge->input()));
43             }
44
45             for (const auto& inEdge : _inputEdges) {
46                 auto curScale = inputScales.at(inEdge->input());
47
48                 if (!isFloatEqual(curScale, maxScale)) {
49                     out[inEdge->input()] = maxScale / curScale;
50                 }
51             }
52
53             out[output] = maxScale;
54         } else {
55             // Eltwise can only propagate scaling for Sum and Max cases.
56             for (const auto& inEdge : _inputEdges) {
57                 out[inEdge->input()] = 1.0f;
58             }
59
60             out[output] = 1.0f;
61         }
62
63         return out;
64     }
65
66     DataMap<DimsOrder> propagateDataOrderImpl() const override {
67         IE_ASSERT(_inputEdges.size() == 2);
68         IE_ASSERT(_outputEdges.size() == 1);
69
70         auto input0 = _inputEdges[0]->input();
71         auto input1 = _inputEdges[1]->input();
72         auto output = _outputEdges[0]->output();
73
74         auto in0Desc = input0->desc();
75         auto in1Desc = input1->desc();
76         auto outDesc = output->desc();
77
78         auto finalOrder  = in0Desc.numDims() >= in1Desc.numDims() ? in0Desc.dimsOrder() : in1Desc.dimsOrder();
79         auto secondOrder = in0Desc.numDims() >= in1Desc.numDims() ? in1Desc.dimsOrder() : in0Desc.dimsOrder();
80         if (secondOrder.numDims() >= 3) {
81             if (secondOrder.dimInd(Dim::C) == 1 /*HCW*/) {
82                 finalOrder = secondOrder;
83             } else if (secondOrder.dimInd(Dim::C) == 2 /*CHW*/ && finalOrder.dimInd(Dim::C) != 1 /*HCW*/) {
84                 finalOrder = secondOrder;
85             }
86         }
87         if (outDesc.numDims() > finalOrder.numDims())
88             finalOrder = outDesc.dimsOrder();
89
90         DataMap<DimsOrder> out;
91
92         out[input0] = finalOrder.numDims() == in0Desc.numDims() ? finalOrder : in0Desc.dimsOrder();
93         out[input1] = finalOrder.numDims() == in1Desc.numDims() ? finalOrder : in1Desc.dimsOrder();
94         out[output] = finalOrder;
95
96         return out;
97     }
98
99     DataMap<StridesRequirement> getDataStridesRequirementsImpl() const override {
100         return DataMap<StridesRequirement>();
101     }
102
103     void finalizeDataLayoutImpl() override {
104     }
105
106     DataMap<BatchSupport> getBatchSupportInfoImpl() const override {
107         return DataMap<BatchSupport>();
108     }
109
110     StageSHAVEsRequirements getSHAVEsRequirementsImpl() const override {
111         return StageSHAVEsRequirements::CanBeLimited;
112     }
113
114     void finalCheckImpl() const override {
115     }
116
117     void serializeParamsImpl(BlobSerializer& serializer) const override {
118         auto coeff1 = attrs().getOrDefault<float>("coeff1", 1.0f);
119         auto coeff2 = attrs().getOrDefault<float>("coeff2", 1.0f);
120
121         serializer.append(static_cast<float>(coeff1));
122         serializer.append(static_cast<float>(coeff2));
123     }
124
125     void serializeDataImpl(BlobSerializer& serializer) const override {
126         IE_ASSERT(_inputEdges.size() == 2);
127         IE_ASSERT(_outputEdges.size() == 1);
128         IE_ASSERT(_tempBufferEdges.empty());
129
130         auto input0 = _inputEdges[0]->input();
131         auto input1 = _inputEdges[1]->input();
132         auto output = _outputEdges[0]->output();
133
134         input0->serializeNewBuffer(serializer, output->desc().dimsOrder());
135         output->serializeNewBuffer(serializer);
136         input1->serializeNewBuffer(serializer, output->desc().dimsOrder());
137     }
138 };
139
140 }  // namespace
141
142 void FrontEnd::parseEltwise(
143         const Model::Ptr& model,
144         const ie::CNNLayerPtr& _layer,
145         const DataVector& inputs,
146         const DataVector& outputs) {
147     IE_ASSERT(inputs.size() >= 2);
148     IE_ASSERT(outputs.size() == 1);
149
150     auto layer = std::dynamic_pointer_cast<ie::EltwiseLayer>(_layer);
151     IE_ASSERT(layer != nullptr);
152
153     auto stageType = StageType::None;
154     auto subCoefficient = 1.0f;
155     switch (layer->_operation) {
156     case ie::EltwiseLayer::eOperation::Sum:
157         stageType = StageType::Sum;
158         break;
159     case ie::EltwiseLayer::eOperation::Prod:
160         stageType = StageType::Prod;
161         break;
162     case ie::EltwiseLayer::eOperation::Max:
163         stageType = StageType::Max;
164         break;
165     case ie::EltwiseLayer::eOperation::Sub:
166         if (inputs.size() > 2) {
167             VPU_THROW_EXCEPTION << "Eltwise operation: " << layer->_operation << " with multiple inputs is not supported";
168         }
169         stageType = StageType::Sum;
170         subCoefficient = -1.f;
171         break;
172     default:
173         VPU_THROW_EXCEPTION << "Eltwise operation" << layer->_operation << " is not supported";
174     }
175
176     if (stageType != StageType::Sum && !layer->coeff.empty()) {
177         VPU_THROW_EXCEPTION << layer->name << " coefficients only supported for Sum/Sub operations.";
178     }
179
180     auto output = outputs[0];
181
182     auto tempOutput = output;
183     if (inputs.size() > 2) {
184         tempOutput = model->duplicateData(
185             output,
186             formatString("@temp@1/%d", inputs.size() - 2));
187     }
188
189     DataVector tempInputs(2);
190     tempInputs[0] = inputs[0];
191     tempInputs[1] = inputs[1];
192
193     auto stage = model->addNewStage<EltwiseStage>(
194         layer->name,
195         stageType,
196         layer,
197         tempInputs,
198         {tempOutput});
199
200     if (layer->coeff.size() > 0) {
201         stage->attrs().set<float>("coeff1", layer->coeff[0]);
202     }
203     if (layer->coeff.size() > 1 || subCoefficient != 1.0f) {
204         stage->attrs().set<float>("coeff2", subCoefficient * (layer->coeff.size() > 1 ? layer->coeff[1] : 1.0f));
205     }
206
207     tempInputs[0] = tempOutput;
208     for (int ind = 2; ind < inputs.size(); ++ind) {
209         tempInputs[1] = inputs[ind];
210
211         if (ind + 1 == inputs.size()) {
212             tempOutput = output;
213         } else {
214             tempOutput = model->duplicateData(
215                 output,
216                 formatString("@temp@%d/%d", ind, inputs.size() - 2));
217         }
218
219         stage = model->addNewStage<EltwiseStage>(
220             layer->name + "@" + std::to_string(ind - 1),
221             stageType,
222             layer,
223             tempInputs,
224             {tempOutput});
225
226         if (layer->coeff.size() > ind) {
227             stage->attrs().set<float>("coeff2", layer->coeff[ind]);
228         }
229
230         tempInputs[0] = tempOutput;
231     }
232 }
233
234 Stage StageBuilder::addSumStage(
235         const Model::Ptr& model,
236         const std::string& name,
237         const ie::CNNLayerPtr& layer,
238         const Data& input0,
239         const Data& input1,
240         const Data& output) {
241     return model->addNewStage<EltwiseStage>(
242         name,
243         StageType::Sum,
244         layer,
245         {input0, input1},
246         {output});
247 }
248
249 }  // namespace vpu