Publishing 2019 R1 content
[platform/upstream/dldt.git] / inference-engine / tests / helpers / xml_net_builder.hpp
1 // Copyright (C) 2018-2019 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 //
4
5 #pragma once
6
7 #include "xml_father.hpp"
8
9 #include <memory>
10 #include <string>
11 #include <utility>
12 #include <vector>
13 #include <map>
14
15 namespace testing {
16
17 struct CropData {
18     size_t axis;
19     size_t offset;
20     size_t dim;
21 };
22
23 typedef std::vector<CropData> CropParams;
24
25 struct InOutShapes {
26     std::vector<std::vector<size_t>> inDims;
27     std::vector<std::vector<size_t>> outDims;
28
29     friend std::ostream& operator<<(std::ostream& os, InOutShapes const& inout) {
30         auto dumpVec = [](const std::vector<size_t>& vec) -> std::string {
31             if (vec.empty()) return "[]";
32             std::stringstream oss;
33             oss << "[" << vec[0];
34             for (size_t i = 1; i < vec.size(); i++) oss << "," << vec[i];
35             oss << "]";
36             return oss.str();
37         };
38
39         for (size_t i = 0; i < inout.inDims.size(); i++) {
40             os << "input" << "[" << i << "]: " << dumpVec(inout.inDims[i]) << ", ";
41         }
42         for (size_t i = 0; i < inout.outDims.size(); i++) {
43             os << "output" << "[" << i << "]: " << dumpVec(inout.outDims[i]) << ", ";
44         }
45         return os;
46     };
47 };
48
49 template<class T>
50 struct get_recursion_level;
51
52 template<class T>
53 struct get_recursion_level<testing::Token<T>> : public get_recursion_level<T> {
54     static const int value = get_recursion_level<T>::value;
55     typedef testing::Token<T> type;
56 };
57
58 template<>
59 struct get_recursion_level<testing::Token<XMLFather>> {
60     static const int value = 1;
61     typedef testing::Token<XMLFather> type;
62 };
63
64
65 template<class T, int L = get_recursion_level<T>::value>
66 class XMLToken;
67
68 template<int N>
69 struct TokenType {
70     typedef testing::Token<typename TokenType<N - 1>::type> type;
71 };
72
73 template<>
74 struct TokenType<0> {
75     typedef XMLFather type;
76 };
77
78 /**
79  * @class Singletone that is responsible for generation unique indexes for layers and ports.
80  */
81 class IDManager {
82 public:
83     IDManager() = default;
84 //    IDManager(IDManager const&) = delete;
85     void operator=(IDManager const&)  = delete;
86
87     /**
88      * @brief Returns new unique number for layer to be used in IR
89      */
90     size_t getNextLayerID();
91
92     /**
93      * @brief Returns new unique number for port to be used in IR
94      */
95     size_t getNextPortID();
96
97     /**
98      * @brief Reset numbers for layers and ports. It's convenient to always start new network from zero number.
99      */
100     void reset();
101
102 private:
103     size_t layerID = 0;
104     size_t portID = 0;
105 };
106
107 /**
108  * @class Contains basic information about layer that is used on IR creation
109  */
110 class LayerDesc {
111     /**
112      * @struct Contains basic information about port in terms of IR
113      */
114     struct LayerPortData {
115         size_t portID;
116         std::vector<size_t> dims;
117
118         /**
119          * @brief Constructor
120          * @param _portID - unique port number
121          * @param _dims - shape of the port
122          */
123         LayerPortData(size_t _portID, std::vector<size_t> _dims) : portID(_portID), dims(std::move(_dims)) {}
124     };
125
126     size_t _currentInPort = 0;
127     size_t _currentOutPort = 0;
128     size_t _layerID;
129     std::vector<LayerPortData> _inPortsID;
130     std::vector<LayerPortData> _outPortsID;
131     std::string _type;
132 public:
133     using Ptr = std::shared_ptr<LayerDesc>;
134
135     /**
136      * @brief Constructor
137      * @param type - string with type of the layer
138      * @param shapes - reference to the structure with input and output shapes
139      */
140     explicit LayerDesc(std::string type, InOutShapes& shapes, IDManager &id_manager);
141
142     /**
143      * @brief Resets current input and output ports to iterate over all input and output ports
144      */
145     void resetPortIDs();
146
147     /**
148      * @brief Returns basic information about next input port. It throws exception when current input post is the last.
149      * @return @LayerPortData
150      */
151     LayerPortData getNextInData();
152
153     /**
154      * @brief Returns basic information about next output port. It throws exception when current output port is the last.
155      * @return @LayerPortData
156      */
157     LayerPortData getNextOutData();
158
159     /**
160      * @brief Returns layer number
161      */
162     size_t getLayerID() const;
163
164     /**
165      * @brief Returns layer number
166      */
167     std::string getLayerName() const;
168
169     /**
170      * @brief Returns number of inputs
171      */
172     size_t getInputsSize() const;
173
174     /**
175      * @brief Returns number of outputs
176      */
177     size_t getOutputsSize() const;
178 };
179
180
181 /**
182  * @class Builder to add edges between layers in IR
183  */
184 class EdgesBuilder {
185     testing::Token<testing::Token<XMLFather>>& nodeEdges;
186     std::vector<LayerDesc::Ptr> layersDesc;
187
188 public:
189     /**
190      * @brief Constructor
191      * @param _nodeEdges - node with edges to add to
192      * @param _layersDesc - container with information about layers: id and dimensions of input/output ports, layer id
193      */
194     EdgesBuilder(typename testing::Token<testing::Token<XMLFather>>& _nodeEdges,
195                  std::vector<LayerDesc::Ptr> _layersDesc) : nodeEdges(_nodeEdges), layersDesc(std::move(_layersDesc)) {
196         for (const auto& desc:layersDesc) {
197             desc->resetPortIDs();
198         }
199     }
200
201     /**
202      * @brief Adds edge between 2 layers with layer1 and layer2 numbers.
203      * Current output port of layer1 is connected with current input port of layer2
204      */
205     EdgesBuilder& connect(size_t layer1, size_t layer2);
206
207     /**
208      * @brief finalizes xml creation and returns its string representation
209      */
210     std::string finish();
211 };
212
213 // BUILDER
214 template<int Version>
215 class XmlNetBuilder {
216     size_t layersNum = 0;
217     std::vector<LayerDesc::Ptr> layersDesc;
218     std::shared_ptr<XMLFather> root;
219     testing::Token<testing::Token<XMLFather>>& xml;
220     IDManager id_manager;
221
222     XmlNetBuilder(std::shared_ptr<XMLFather> _root,
223                   typename testing::Token<testing::Token<XMLFather>>& _xml) : xml(_xml), root(_root) {};
224
225 public:
226     static XmlNetBuilder buildNetworkWithOneInput(
227             std::string name = "AlexNet", std::vector<size_t> dims = {1, 3, 227, 227}, std::string precision = "Q78") {
228         std::shared_ptr<XMLFather> root = std::make_shared<XMLFather>();
229         auto &exp = root->node("net").attr("name", name).attr("precision", precision).attr("version", Version);
230         if (Version == 1) {
231             auto &expFinal = exp.node("input").attr("name", "data");
232             addDims(expFinal, dims);
233             return XmlNetBuilder(root, expFinal.close().node("layers"));
234         } else {
235             auto &expFinal = exp.attr("batch", 1);
236             return XmlNetBuilder(root, expFinal.node("layers")).addInputLayer(precision, dims);
237         }
238     }
239
240     static XmlNetBuilder buildBody() {
241         auto root = std::make_shared<XMLFather>(XMLFather::make_without_schema());
242         auto &exp = root->node("body");
243         return XmlNetBuilder(root, exp.node("layers"));
244     }
245
246     XmlNetBuilder& havingLayers() {
247         return *this;
248     }
249
250     EdgesBuilder havingEdges() {
251         auto& exp = xml.close();
252         return EdgesBuilder(exp.node("edges"), layersDesc);
253     }
254
255     XmlNetBuilder& cropLayer(CropParams params, const InOutShapes& inout) {
256         std::map<std::string, std::string> generalParams;
257         for (CropData crop : params) {
258             generalParams["axis"] = std::to_string(crop.axis);
259             generalParams["offset"] = std::to_string(crop.offset);
260             generalParams["dim"] = std::to_string(crop.dim);
261         }
262         return addLayer("Crop", "", &generalParams, inout, 0, 0, "crop-data");
263     }
264
265     XmlNetBuilder& convolutionLayer(const std::string& precision, const InOutShapes& inout) {
266         std::map<std::string, std::string> params{
267                 {"stride-x", "4"},
268                 {"stride-y", "4"},
269                 {"pad-x",    "0"},
270                 {"pad-y",    "0"},
271                 {"kernel-x", "11"},
272                 {"kernel-y", "11"},
273                 {"output",   "96"},
274         };
275         return addLayer("Convolution", precision, &params, inout, 0, 0, "convolution_data");
276     }
277
278     XmlNetBuilder& poolingLayer(const InOutShapes& inout) {
279         std::map<std::string, std::string> params{
280                 {"stride-x", "4"},
281                 {"stride-y", "4"},
282                 {"pad-x",    "0"},
283                 {"pad-y",    "0"},
284                 {"kernel-x", "11"},
285                 {"kernel-y", "11"},
286         };
287         return addLayer("Pooling", "", &params, inout, 0, 0, "pooling_data");
288     }
289
290     struct TIPortMap { int from_l, from_p, to_l, to_p, axis, stride, start, end; };
291
292     XmlNetBuilder& TILayer(InOutShapes inout,
293                            std::string body,
294                            std::vector<TIPortMap> inMap,
295                            std::vector<TIPortMap> outMap,
296                            std::vector<TIPortMap> backMap) {
297         auto builder = XMLFather::make_without_schema();
298         // Port map section
299         auto &ports = builder.node("port_map");
300         auto fill_port_map_info = [&] (std::string name, TIPortMap m) {
301             auto & exp =  ports.node(name)
302                     .attr("external_port_id", m.from_p)
303                     .attr("internal_layer_id", m.to_l)
304                     .attr("internal_port_id", m.to_p);
305             if (m.axis != -1)
306                 exp.attr("axis", m.axis).attr("stride", m.stride).attr("start", m.start).attr("end", m.end);
307             exp.close();
308         };
309         for (auto &m : inMap)  fill_port_map_info("input", m);
310         for (auto &m : outMap) fill_port_map_info("output", m);
311         ports.close();
312         // BackEdge map section
313         auto &backedges = builder.node("back_edges");
314         for (auto &m : backMap) {
315             backedges.node("edge")
316                     .attr("from-layer", m.from_l)
317                     .attr("from-port", m.from_p)
318                     .attr("to-layer", m.to_l)
319                     .attr("to-port", m.to_p).close();
320         }
321         backedges.close();
322         // Serialize all TI info
323         std::string content = builder;
324         content += body;
325
326         return addLayer("TensorIterator", "FP32", nullptr, inout, 0,0, "data", content);
327     }
328
329     XmlNetBuilder& addLayer(const std::string& type,
330                             const std::string& precision,
331                             std::map<std::string, std::string>* params,
332                             InOutShapes inout,
333                             int weightsSize = 0,
334                             int biasesSize = 0,
335                             std::string layerDataName = "data",
336                             std::string content = "") {
337         layersNum++;
338         auto layerDesc = std::make_shared<LayerDesc>(type, inout, id_manager);
339         layersDesc.push_back(layerDesc);
340
341         auto& layer = xml.node("layer").attr("name", layerDesc->getLayerName()).attr("precision", precision)
342                 .attr("type", type).attr("id", layerDesc->getLayerID());
343         if (params != nullptr) {
344             auto& data = layer.node(layerDataName);
345             for (auto& kv : *params) {
346                 data = data.attr(kv.first, kv.second);
347             }
348             layer = data.close();
349         }
350         addPorts(layer, layerDesc);
351         if (weightsSize != 0) {
352             layer = layer.node("weights").attr("offset", 0).attr("size", weightsSize).close();
353             if (biasesSize != 0) {
354                 layer = layer.node("biases").attr("offset", weightsSize).attr("size", biasesSize).close();
355             }
356         }
357         if (!content.empty())
358             layer.add_content(content);
359         layer.close();
360         return *this;
361     }
362
363     XmlNetBuilder& addInputLayer(const std::string& precision, const std::vector<size_t>& out) {
364         InOutShapes inout{};
365         inout.outDims.push_back(out);
366         return addLayer("Input", precision, nullptr, inout);
367     }
368
369     std::string finish(std::vector<std::pair<std::string, std::string>>* edges) {
370         auto& exp = xml.close();
371         auto& node_edges = exp.node("edges");
372
373         for (auto& kv : *edges) {
374             std::string from[] = {kv.first.substr(0, kv.first.find(',')),
375                                   kv.first.substr(kv.first.find(',') + 1, kv.first.length())};
376             std::string to[] = {kv.second.substr(0, kv.second.find(',')),
377                                 kv.second.substr(kv.second.find(',') + 1, kv.second.length())};
378             node_edges.node("edge").attr("from-layer", from[0]).attr("from-port", from[1])
379                     .attr("to-layer", to[0]).attr("to-port", to[1]).close();
380         }
381
382         node_edges.close();
383         return exp;
384     }
385
386     std::string finish(bool addInputPreProcess = true) {
387         auto& exp = xml.close();
388         addEdges(exp);
389         if (addInputPreProcess) {
390             addPreProcess(exp);
391         }
392         return exp;
393     }
394
395 private:
396     template<class T>
397     static void addDims(T& place, std::vector<size_t> dims) {
398         for (auto dim : dims) {
399             place.node("dim", dim);
400         }
401     }
402
403     template<class T>
404     void addPorts(T& layer, const LayerDesc::Ptr& layerDesc) {
405         layerDesc->resetPortIDs();
406         size_t numPorts = layerDesc->getInputsSize();
407         if (numPorts) {
408             auto& node = layer.node("input");
409             for (size_t i = 0; i < numPorts; i++) {
410                 auto inData = layerDesc->getNextInData();
411                 addPortInfo(node, inData.portID, inData.dims);
412             }
413             node.close();
414         }
415         numPorts = layerDesc->getOutputsSize();
416         if (numPorts) {
417             auto& node = layer.node("output");
418             for (size_t i = 0; i < numPorts; i++) {
419                 auto outData = layerDesc->getNextOutData();
420                 addPortInfo(node, outData.portID, outData.dims);
421             }
422             node.close();
423         }
424     }
425
426     template<class T>
427     static void addPortInfo(T& layer, size_t portNum, std::vector<size_t> dims) {
428         auto& place = layer.node("port").attr("id", portNum);
429         addDims(place, dims);
430         place.close();
431     }
432
433     template<class T>
434     void addEdges(T& mainContent) {
435         size_t firstLayerNum = Version >= 2 ? 0 : 1;
436         if (layersNum <= firstLayerNum) {
437             return;
438         }
439         auto& edges = mainContent.node("edges");
440         for (size_t i = 0; i < layersDesc.size(); i++) {
441             layersDesc[i]->resetPortIDs();
442         }
443         for (size_t i = firstLayerNum; i < layersDesc.size() - 1; i++) {
444             edges.node("edge")
445                     .attr("from-layer", layersDesc[i]->getLayerID())
446                     .attr("from-port", layersDesc[i]->getNextOutData().portID)
447                     .attr("to-layer", layersDesc[i + 1]->getLayerID())
448                     .attr("to-port", layersDesc[i + 1]->getNextInData().portID).close();
449         }
450         edges.close();
451     }
452
453     template<class T>
454     void addPreProcess(T& mainContent) {
455         auto& preProcess = mainContent.node("pre-process");
456         if (Version >= 2) {
457             preProcess.attr("reference-layer-name", layersDesc[0]->getLayerName());
458         }
459         preProcess.close();
460     }
461 };
462
463 typedef XmlNetBuilder<1> V1NetBuilder;
464 typedef XmlNetBuilder<2> V2NetBuilder;
465
466 }  // namespace testing