Publishing 2019 R1 content
[platform/upstream/dldt.git] / inference-engine / tests / unit / engines / mkldnn / graph / structure / graph_optimization_test.cpp
1 // Copyright (C) 2018-2019 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 //
4
5 #include <gtest/gtest.h>
6 #include <gmock/gmock-spec-builders.h>
7 #include "mkldnn_plugin/mkldnn_graph.h"
8
9 #include "single_layer_common.hpp"
10 #include <mkldnn_plugin/mkldnn_extension_utils.h>
11 #include <mkldnn_plugin/mkldnn_extension_mngr.h>
12 #include "tests_common.hpp"
13 #include "../test_graph.hpp"
14
15
16 using namespace ::testing;
17 using namespace std;
18 using namespace mkldnn;
19
20 class MKLDNNGraphOptimizationTests: public TestsCommon {};
21
22 TEST_F(MKLDNNGraphOptimizationTests, TestNoFuseConvSumWithOneInput) {
23     std::string model = R"V0G0N(
24 <net name="AlexNet" version="2" batch="1">
25     <layers>
26         <layer name="data" type="Input" precision="FP32" id="0">
27             <output>
28                 <port id="0">
29                     <dim>1</dim>
30                     <dim>3</dim>
31                     <dim>5</dim>
32                     <dim>5</dim>
33                 </port>
34             </output>
35         </layer>
36         <layer name="conv1" type="Convolution" precision="FP32" id="1">
37             <convolution_data stride-x="1" stride-y="1" pad-x="0" pad-y="0" kernel-x="1" kernel-y="1" output="3" group="1"/>
38             <input>
39                 <port id="1">
40                     <dim>1</dim>
41                     <dim>3</dim>
42                     <dim>5</dim>
43                     <dim>5</dim>
44                 </port>
45             </input>
46             <output>
47                 <port id="2">
48                     <dim>1</dim>
49                     <dim>3</dim>
50                     <dim>5</dim>
51                     <dim>5</dim>
52                 </port>
53             </output>
54             <weights offset="0" size="36"/>
55             <biases offset="36" size="12"/>
56         </layer>
57         <layer name="res2a" type="Eltwise" precision="FP32" id="2">
58             <elementwise_data operation="sum"/>
59             <input>
60                 <port id="3">
61                     <dim>1</dim>
62                     <dim>3</dim>
63                     <dim>5</dim>
64                     <dim>5</dim>
65                 </port>
66                 <port id="4">
67                     <dim>1</dim>
68                     <dim>3</dim>
69                     <dim>5</dim>
70                     <dim>5</dim>
71                 </port>
72             </input>
73             <output>
74                 <port id="5">
75                     <dim>1</dim>
76                     <dim>3</dim>
77                     <dim>5</dim>
78                     <dim>5</dim>
79                 </port>
80             </output>
81         </layer>
82     </layers>
83     <edges>
84         <edge from-layer="0" from-port="0" to-layer="1" to-port="1"/>
85         <edge from-layer="0" from-port="0" to-layer="2" to-port="3"/>
86         <edge from-layer="1" from-port="2" to-layer="2" to-port="4"/>
87     </edges>
88 </net>
89
90 )V0G0N";
91
92     InferenceEngine::CNNNetReader net_reader;
93     ASSERT_NO_THROW(net_reader.ReadNetwork(model.data(), model.length()));
94
95     InferenceEngine::TBlob<uint8_t> *weights = new InferenceEngine::TBlob<uint8_t>(InferenceEngine::Precision::U8, InferenceEngine::C, {48});
96     weights->allocate();
97     float * data = weights->buffer();
98
99     fill_data((float *) weights->buffer(), weights->size() / sizeof(float));
100
101     InferenceEngine::TBlob<uint8_t>::Ptr weights_ptr = InferenceEngine::TBlob<uint8_t>::Ptr(weights);
102
103     net_reader.SetWeights(weights_ptr);
104
105     MKLDNNGraphTestClass graph;
106     ASSERT_NO_THROW(graph.CreateGraph(net_reader.getNetwork()));
107
108     bool fused = true;
109     auto& nodes = graph.getNodes();
110     for (auto &node : nodes) {
111         if (node->getType() == MKLDNNPlugin::Convolution) {
112             fused = false;
113         }
114     }
115     ASSERT_FALSE(fused);
116 }
117
118 TEST_F(MKLDNNGraphOptimizationTests, TestNoCrashForFuseConvSumAndInput) {
119     std::string model = R"V0G0N(
120 <net name="AlexNet" version="2" batch="1">
121     <layers>
122         <layer name="data" type="Input" precision="FP32" id="0">
123             <output>
124                 <port id="0">
125                     <dim>1</dim>
126                     <dim>3</dim>
127                     <dim>5</dim>
128                     <dim>5</dim>
129                 </port>
130             </output>
131         </layer>
132         <layer name="conv1" type="Convolution" precision="FP32" id="1">
133             <convolution_data stride-x="1" stride-y="1" pad-x="0" pad-y="0" kernel-x="1" kernel-y="1" output="3" group="1"/>
134             <input>
135                 <port id="1">
136                     <dim>1</dim>
137                     <dim>3</dim>
138                     <dim>5</dim>
139                     <dim>5</dim>
140                 </port>
141             </input>
142             <output>
143                 <port id="2">
144                     <dim>1</dim>
145                     <dim>3</dim>
146                     <dim>5</dim>
147                     <dim>5</dim>
148                 </port>
149             </output>
150             <weights offset="0" size="36"/>
151             <biases offset="36" size="12"/>
152         </layer>
153         <layer name="relu1" type="ReLU" precision="FP32" id="2">
154             <input>
155                 <port id="1">
156                     <dim>1</dim>
157                     <dim>3</dim>
158                     <dim>5</dim>
159                     <dim>5</dim>
160                 </port>
161             </input>
162             <output>
163                 <port id="2">
164                     <dim>1</dim>
165                     <dim>3</dim>
166                     <dim>5</dim>
167                     <dim>5</dim>
168                 </port>
169             </output>
170         </layer>
171         <layer name="res2a" type="Eltwise" precision="FP32" id="3">
172             <elementwise_data operation="sum"/>
173             <input>
174                 <port id="3">
175                     <dim>1</dim>
176                     <dim>3</dim>
177                     <dim>5</dim>
178                     <dim>5</dim>
179                 </port>
180                 <port id="4">
181                     <dim>1</dim>
182                     <dim>3</dim>
183                     <dim>5</dim>
184                     <dim>5</dim>
185                 </port>
186             </input>
187             <output>
188                 <port id="5">
189                     <dim>1</dim>
190                     <dim>3</dim>
191                     <dim>5</dim>
192                     <dim>5</dim>
193                 </port>
194             </output>
195         </layer>
196     </layers>
197     <edges>
198         <edge from-layer="0" from-port="0" to-layer="1" to-port="1"/>
199         <edge from-layer="0" from-port="0" to-layer="2" to-port="1"/>
200         <edge from-layer="1" from-port="2" to-layer="3" to-port="3"/>
201         <edge from-layer="2" from-port="2" to-layer="3" to-port="4"/>
202     </edges>
203 </net>
204
205 )V0G0N";
206
207     InferenceEngine::CNNNetReader net_reader;
208     ASSERT_NO_THROW(net_reader.ReadNetwork(model.data(), model.length()));
209
210     InferenceEngine::TBlob<uint8_t> *weights = new InferenceEngine::TBlob<uint8_t>(InferenceEngine::Precision::U8, InferenceEngine::C, {48});
211     weights->allocate();
212     float * data = weights->buffer();
213
214     fill_data((float *) weights->buffer(), weights->size() / sizeof(float));
215
216     InferenceEngine::TBlob<uint8_t>::Ptr weights_ptr = InferenceEngine::TBlob<uint8_t>::Ptr(weights);
217
218     net_reader.SetWeights(weights_ptr);
219
220     MKLDNNGraphTestClass graph;
221     ASSERT_NO_THROW(graph.CreateGraph(net_reader.getNetwork()));
222
223     bool fused = false;
224     auto& nodes = graph.getNodes();
225     for (auto &node : nodes) {
226         if (node->getType() == MKLDNNPlugin::Convolution_Sum) {
227             fused = true;
228         }
229     }
230     ASSERT_TRUE(fused);
231 }
232
233 namespace GraphOptimizationUtils {
234
235 using fake_ext_factory = std::function<InferenceEngine::ILayerImplFactory*(const InferenceEngine::CNNLayer *)>;
236
237 class FakeReLUImpl : public InferenceEngine::ILayerExecImpl {
238 public:
239     FakeReLUImpl(const InferenceEngine::CNNLayer *layer) {
240         cnnLayer = const_cast<InferenceEngine::CNNLayer *>(layer);
241     }
242     InferenceEngine::StatusCode getSupportedConfigurations(std::vector<InferenceEngine::LayerConfig>& conf, InferenceEngine::ResponseDesc *resp) noexcept override {
243         InferenceEngine::LayerConfig config;
244         config.dynBatchSupport = 0;
245         if (cnnLayer->outData.size() != 1 && cnnLayer->insData.size() != 1)
246             return InferenceEngine::GENERAL_ERROR;
247         InferenceEngine::DataConfig cfg;
248         cfg.constant = false;
249         cfg.inPlace = 0;
250         InferenceEngine::SizeVector order;
251         for(size_t i = 0; i < cnnLayer->outData[0]->getTensorDesc().getDims().size(); i++) {
252             order.push_back(i);
253         }
254         cfg.desc = InferenceEngine::TensorDesc(cnnLayer->outData[0]->getTensorDesc().getPrecision(),
255                                                cnnLayer->outData[0]->getTensorDesc().getDims(),
256                                                {cnnLayer->outData[0]->getTensorDesc().getDims(), order});
257         config.outConfs.push_back(cfg);
258         config.inConfs.push_back(cfg);
259         conf.push_back(config);
260         return InferenceEngine::OK;
261     }
262     InferenceEngine::StatusCode init(InferenceEngine::LayerConfig& config, InferenceEngine::ResponseDesc *resp) noexcept override {
263         if (config.dynBatchSupport)
264             return InferenceEngine::NOT_IMPLEMENTED;
265         for(auto input : config.inConfs) {
266             if (input.constant)
267                 return InferenceEngine::GENERAL_ERROR;
268         }
269         for(auto output : config.outConfs) {
270             if (output.constant)
271                 return InferenceEngine::GENERAL_ERROR;
272         }
273         return InferenceEngine::OK;
274     }
275     InferenceEngine::StatusCode execute(std::vector<InferenceEngine::Blob::Ptr>& inputs, std::vector<InferenceEngine::Blob::Ptr>& outputs, InferenceEngine::ResponseDesc *resp) noexcept override {
276         const float *src_data = inputs[0]->buffer();
277         float *dst_data = outputs[0]->buffer();
278         if (src_data != dst_data)
279             return InferenceEngine::GENERAL_ERROR;
280         return InferenceEngine::OK;
281     }
282
283 private:
284     InferenceEngine::CNNLayer* cnnLayer;
285 };
286
287 class FakeReLUFactory : public InferenceEngine::ILayerImplFactory {
288 public:
289     FakeReLUFactory(const InferenceEngine::CNNLayer *layer) {
290         cnnLayer = const_cast<InferenceEngine::CNNLayer *>(layer);
291     }
292     // set output shapes by input shapes.
293     InferenceEngine::StatusCode getShapes(const std::vector<InferenceEngine::TensorDesc>& inShapes, std::vector<InferenceEngine::TensorDesc>& outShapes, InferenceEngine::ResponseDesc *resp) noexcept override {
294         outShapes.push_back(inShapes[0]);
295         return InferenceEngine::OK;
296     }
297     // First implementation has more priority than next
298     InferenceEngine::StatusCode getImplementations(std::vector<InferenceEngine::ILayerImpl::Ptr>& impls, InferenceEngine::ResponseDesc *resp) noexcept override {
299         impls.push_back(InferenceEngine::ILayerImpl::Ptr(new FakeReLUImpl(cnnLayer)));
300         return InferenceEngine::OK;
301     }
302
303 private:
304     InferenceEngine::CNNLayer * cnnLayer;
305 };
306
307 class FakeFabric : public InferenceEngine::IExtension {
308 public:
309     FakeFabric() {
310         factories["ReLU"] = [](const InferenceEngine::CNNLayer * cnnLayer) -> InferenceEngine::ILayerImplFactory* { return new FakeReLUFactory(cnnLayer); };
311     }
312
313     virtual ~FakeFabric() {
314         factories.clear();
315     }
316
317     void GetVersion(const InferenceEngine::Version *&versionInfo) const noexcept override {}
318     void SetLogCallback(InferenceEngine::IErrorListener &listener) noexcept override {}
319     void Unload() noexcept override {}
320     void Release() noexcept override {
321         delete this;
322     }
323     InferenceEngine::StatusCode getPrimitiveTypes(char**& types, unsigned int& size, InferenceEngine::ResponseDesc* resp) noexcept override {
324         types = new char *[factories.size()];
325         size_t count = 0;
326         for (auto it = factories.begin(); it != factories.end(); it++, count ++) {
327             types[count] = new char[it->first.size() + 1];
328             std::copy(it->first.begin(), it->first.end(), types[count]);
329             types[count][it->first.size() ] = '\0';
330         }
331         return InferenceEngine::OK;
332     };
333     InferenceEngine::StatusCode getFactoryFor(InferenceEngine::ILayerImplFactory *&factory,
334                                               const InferenceEngine::CNNLayer *cnnLayer,
335                                               InferenceEngine::ResponseDesc *resp) noexcept override {
336         if (factories.find(cnnLayer->type) == factories.end()) {
337             std::string errorMsg = std::string("Factory for ") + cnnLayer->type + " wasn't found!";
338             errorMsg.copy(resp->msg, sizeof(resp->msg) - 1);
339             return InferenceEngine::NOT_FOUND;
340         }
341         factory = factories[cnnLayer->type](cnnLayer);
342         return InferenceEngine::OK;
343     }
344
345     InferenceEngine::StatusCode getShapeInferImpl(InferenceEngine::IShapeInferImpl::Ptr& impl, const char* type,
346                                                   InferenceEngine::ResponseDesc* resp) noexcept override {
347         return InferenceEngine::NOT_IMPLEMENTED;
348     }
349
350 private:
351     std::map<std::string, fake_ext_factory> factories;
352 };
353 }
354
355 TEST_F(MKLDNNGraphOptimizationTests, TestNoFuseCustomActivation) {
356     std::string model = R"V0G0N(
357 <net name="AlexNet" version="2" batch="1">
358     <layers>
359         <layer name="data" type="Input" precision="FP32" id="0">
360             <output>
361                 <port id="0">
362                     <dim>1</dim>
363                     <dim>3</dim>
364                     <dim>227</dim>
365                     <dim>227</dim>
366                 </port>
367             </output>
368         </layer>
369         <layer name="conv1" type="Convolution" precision="FP32" id="1">
370             <convolution_data stride-x="4" stride-y="4" pad-x="0" pad-y="0" kernel-x="11" kernel-y="11" output="96" group="1"/>
371             <input>
372                 <port id="1">
373                     <dim>1</dim>
374                     <dim>3</dim>
375                     <dim>227</dim>
376                     <dim>227</dim>
377                 </port>
378             </input>
379             <output>
380                 <port id="2">
381                     <dim>1</dim>
382                     <dim>96</dim>
383                     <dim>55</dim>
384                     <dim>55</dim>
385                 </port>
386             </output>
387             <weights offset="0" size="139392"/>
388             <biases offset="139392" size="384"/>
389         </layer>
390         <layer name="relu1" type="ReLU" precision="FP32" id="2">
391             <input>
392                 <port id="3">
393                     <dim>1</dim>
394                     <dim>96</dim>
395                     <dim>55</dim>
396                     <dim>55</dim>
397                 </port>
398             </input>
399             <output>
400                 <port id="4">
401                     <dim>1</dim>
402                     <dim>96</dim>
403                     <dim>55</dim>
404                     <dim>55</dim>
405                 </port>
406             </output>
407         </layer>
408     </layers>
409     <edges>
410         <edge from-layer="0" from-port="0" to-layer="1" to-port="1"/>
411         <edge from-layer="1" from-port="2" to-layer="2" to-port="3"/>
412     </edges>
413 </net>
414 )V0G0N";
415
416     std::shared_ptr<InferenceEngine::IExtension> extension;
417     extension.reset(new GraphOptimizationUtils::FakeFabric());
418     MKLDNNPlugin::MKLDNNExtensionManager::Ptr extMgr(new MKLDNNPlugin::MKLDNNExtensionManager());
419     extMgr->AddExtension(extension);
420
421     InferenceEngine::CNNNetReader net_reader;
422     ASSERT_NO_THROW(net_reader.ReadNetwork(model.data(), model.length()));
423
424     InferenceEngine::TBlob<uint8_t> *weights = new InferenceEngine::TBlob<uint8_t>(InferenceEngine::Precision::U8, InferenceEngine::C, {139776});
425     weights->allocate();
426     float * data = weights->buffer();
427
428     fill_data((float *) weights->buffer(), weights->size() / sizeof(float));
429
430     InferenceEngine::TBlob<uint8_t>::Ptr weights_ptr = InferenceEngine::TBlob<uint8_t>::Ptr(weights);
431
432     net_reader.SetWeights(weights_ptr);
433
434     MKLDNNGraphTestClass graph;
435     ASSERT_NO_THROW(graph.CreateGraph(net_reader.getNetwork(), extMgr));
436
437     bool fused = true;
438     auto& nodes = graph.getNodes();
439     for (auto &node : nodes) {
440         if (node->getType() == MKLDNNPlugin::Convolution) {
441             fused = false;
442         }
443     }
444     ASSERT_FALSE(fused);
445 }