Add a section of how to link IE with CMake project (#99)
[platform/upstream/dldt.git] / inference-engine / ie_bridges / python / src / openvino / inference_engine / dnn_builder / dnn_builder.pyx
1 # #distutils: language=c++
2 #from cython.operator cimport dereference as deref
3 from libcpp.vector cimport vector
4 from libcpp.map cimport map
5 from libcpp.string cimport string
6 from ..ie_api cimport IENetwork, BlobBuffer
7 from .cimport dnn_builder_impl_defs as C
8 from .dnn_builder_impl_defs cimport Blob
9 import numpy as np
10
11
12 np_precision_map = {
13             "float32": "FP32",
14             "float16": "FP16",
15             "int32": "I32",
16             "int16": "I16",
17             "uint16": "U16",
18             "int8": "I8",
19             "uint8": "U8",
20         }
21 cdef class NetworkBuilder:
22     def __cinit__(self, name=None, IENetwork ie_net=None):
23         if name is not None and ie_net is not None:
24             raise AttributeError("Both name and ie_net arguments are defined")
25         elif name is not None:
26             self.impl = C.NetworkBuilder(name.encode())
27         elif ie_net is not None:
28             self.impl = C.NetworkBuilder().from_ie_network(ie_net.impl)
29
30     def build(self):
31         cdef INetwork i_net = INetwork()
32         i_net.impl = self.impl.build()
33         return i_net
34
35     def get_layer(self, id: int):
36         cdef LayerBuilder py_layer = LayerBuilder()
37         py_layer.impl = self.impl.getLayer(id)
38         return py_layer
39
40     @property
41     def layers(self):
42         cdef vector[C.LayerBuilder] c_layers = self.impl.getLayers()
43         cdef LayerBuilder py_layer
44         py_layers = {}
45         for l in c_layers:
46             py_layer = LayerBuilder()
47             py_layer.impl = l
48             py_layers[l.getName().decode()] = py_layer
49         return py_layers
50
51     def remove_layer(self, LayerBuilder layer):
52         self.impl.removeLayer(layer.impl)
53
54     def get_layer_connection(self, LayerBuilder layer):
55         cdef vector[C.Connection] c_connections = self.impl.getLayerConnections(layer.impl)
56         cdef Connection connection
57         connections = []
58         for con in c_connections:
59             connection = Connection()
60             connection.impl = con
61             connections.append(connection)
62         return connections
63
64     def disconnect(self, Connection connection):
65         self.impl.disconnect(connection.impl)
66
67     def connect(self, PortInfo input, PortInfo output):
68         self.impl.connect(input.impl, output.impl)
69
70     def add_layer(self, LayerBuilder layer, input_ports: list = None):
71         cdef vector[C.PortInfo] c_ports
72         cdef PortInfo c_port
73         if not input_ports:
74             return self.impl.addLayer(layer.impl)
75         else:
76             for p in input_ports:
77                 c_port = PortInfo(p.layer_id, p.port_id)
78                 c_ports.push_back(c_port.impl)
79             return self.impl.addAndConnectLayer(c_ports, layer.impl)
80
81 cdef class INetwork:
82     def __iter__(self):
83         cdef ILayer layer
84         layers = []
85         cdef vector[C.ILayer] c_layers = self.impl.layers
86         for l in c_layers:
87             layer = ILayer()
88             layer.impl = l
89             layers.append(layer)
90         return iter(layers)
91
92     @property
93     def layers(self):
94         cdef ILayer layer
95         layers = {}
96         cdef vector[C.ILayer] c_layers = self.impl.layers
97         for l in c_layers:
98             layer = ILayer()
99             layer.impl = l
100             layers[l.name.decode()] = layer
101         return layers
102
103     @property
104     def inputs(self):
105         cdef ILayer layer
106         layers = {}
107         cdef vector[C.ILayer] c_layers = self.impl.inputs
108         for l in c_layers:
109             layer = ILayer()
110             layer.impl = l
111             layers[l.name.decode()] = layer
112         return layers
113
114     @property
115     def outputs(self):
116         cdef ILayer layer
117         layers = {}
118         cdef vector[C.ILayer] c_layers = self.impl.outputs
119         for l in c_layers:
120             layer = ILayer()
121             layer.impl = l
122             layers[l.name.decode()] = layer
123         return layers
124
125     @property
126     def name(self):
127         return self.impl.name.decode()
128
129
130     @property
131     def size(self):
132         return self.impl.size
133
134     def get_layer_connection(self, layer: ILayer):
135         cdef Connection connection
136         connections = []
137         cdef vector[C.Connection] c_connections = self.impl.getLayerConnections(layer.id)
138         for con in c_connections:
139             connection = Connection()
140             connection.impl = con
141             connections.append(connection)
142         return connections
143
144     def to_ie_network(self):
145         cdef IENetwork net = IENetwork()
146         net.impl = self.impl.to_ie_network()
147         return net
148
149 cdef class ILayer:
150     @property
151     def name(self):
152         return self.impl.name.decode()
153
154     @property
155     def id(self):
156         return self.impl.id
157
158     @property
159     def type(self):
160         return self.impl.type.decode()
161
162     @property
163     def params(self):
164         return {k.decode(): v.decode() for k, v in self.impl.parameters}
165
166     @property
167     def input_ports(self):
168         cdef Port port
169         cdef vector[C.Port] c_ports = self.impl.in_ports
170         ports = []
171         for p in c_ports:
172             port = Port()
173             port.impl = p
174             ports.append(port)
175         return ports
176
177     @property
178     def output_ports(self):
179         cdef Port port
180         cdef vector[C.Port] c_ports = self.impl.out_ports
181         ports = []
182         for p in c_ports:
183             port = Port()
184             port.impl = p
185             ports.append(port)
186         return ports
187
188     @property
189     def constant_data(self):
190         cdef map[string, Blob.Ptr] c_constant_data
191         c_constant_data = self.impl.constant_data
192         constant_data = {}
193         cdef BlobBuffer weights_buffer
194         for weights in c_constant_data:
195             weights_buffer = BlobBuffer()
196             weights_buffer.reset(weights.second)
197             constant_data[weights.first.decode()] = weights_buffer.to_numpy()
198         return constant_data
199
200
201 cdef class Port:
202     def __cinit__(self, shape: list=[]):
203         cdef vector[size_t] c_shape
204         for d in shape:
205             c_shape.push_back(d)
206         self.impl = C.Port(c_shape)
207     @property
208     def shape(self):
209         return self.impl.shape
210
211 cdef class PortInfo:
212     def __cinit__(self, layer_id: int = -1, port_id: int = -1):
213         if layer_id != -1 and port_id != -1:
214             self.impl = C.PortInfo(layer_id, port_id)
215         else:
216             self.impl = C.PortInfo()
217     @property
218     def layer_id(self):
219         return self.impl.layer_id
220
221     @property
222     def port_id(self):
223         return self.impl.port_id
224
225     def __eq__(self, other):
226         return self.layer_id == other.layer_id and self.port_id == other.port_id
227
228     def __ne__(self, other):
229         return self.layer_id != other.layer_id and self.port_id != other.port_id
230
231 cdef class Connection:
232     def __cinit__(self, PortInfo input = None, PortInfo output = None):
233         if input and output:
234             self.impl = C.Connection(input.impl, output.impl)
235         else:
236             self.impl = C.Connection()
237     @property
238     def _from(self):
239         cdef PortInfo port_info = PortInfo()
240         port_info.impl = self.impl._from
241         return port_info
242
243     @property
244     def to(self):
245         cdef PortInfo port_info = PortInfo()
246         port_info.impl = self.impl.to
247         return port_info
248
249     def __eq__(self, other):
250         return self._from == other._from and self.to == other.to
251
252     def __ne__(self, other):
253         return self._from != other._from and self.to != other.to
254
255
256 def check_constant_data(data):
257     for k, v in data.items():
258         if not all([isinstance(x, type(v[0])) for x in v]):
259             raise TypeError("Elements of list for key {} have different data types! "
260                             "Please specify list of 'int' or 'float' values.".format(k))
261         if isinstance(v, list):
262             if isinstance(v[0], float):
263                 dtype = np.float32
264             elif isinstance(v[0], int):
265                 dtype = np.int32
266             else:
267                 raise TypeError("Unsupported precision of the data for key {}! Given {} but 'float  or 'int' precision expected".
268                               format(k, str(v.dtype)))
269             data[k] = np.asanyarray(v, dtype=dtype)
270         elif isinstance(v, np.ndarray):
271             pass
272         else:
273             raise TypeError("Unsupported data type for key '{}'. {} given but 'list' or 'numpy.ndarray' expected".
274                             format(k, type(v)))
275     return data
276
277
278 # TODO: Fix LAyerBuilder object copying - pass by reference
279 # cdef class LayerConstantData(dict):
280 #     def update(self, other=None, **kwargs):
281 #         if other:
282 #             other = check_constant_data(other)
283 #         cdef vector[size_t] dims
284 #         cdef Blob.Ptr blob_ptr
285 #         cdef BlobBuffer buffer
286 #         for k, v in other.items():
287 #             if k in self.keys() and (v.shape == self[k].shape and v.dtype == self[k].dtype):
288 #                 print("Reuse blob for {}\n".format(k))
289 #                 self[k][:] = v
290 #             else:
291 #                 for dim in v.shape:
292 #                     dims.push_back(dim)
293 #                 ie_precision = np_precision_map.get(str(v.dtype), None)
294 #                 if not ie_precision:
295 #                     raise BufferError("Unsupported precision of the data for key {}! Given {} but one of the {} precisions expected".
296 #                                       format(k, str(v.dtype), ", ".join(np_precision_map.keys())))
297 #                 blob_ptr = deref(self.impl).allocateBlob(dims, ie_precision.encode())
298 #                 buffer = BlobBuffer()
299 #                 buffer.reset(blob_ptr)
300 #                 np_buffer = buffer.to_numpy()
301 #                 np_buffer[:] = v
302 #                 deref(self.impl).addConstantData(k.encode(), blob_ptr)
303
304 cdef class LayerBuilder:
305
306     def __cinit__(self, type: str=None, name: str=None):
307         if name and type:
308             self.impl = C.LayerBuilder(name.encode(), type.encode())
309         else:
310             self.impl = C.LayerBuilder()
311
312     @property
313     def id(self):
314         return self.impl.id
315     @property
316     def name(self):
317         return self.impl.getName().decode()
318     @name.setter
319     def name(self, name: str):
320         self.impl.setName(name.encode())
321
322     @property
323     def type(self):
324         return self.impl.getType().decode()
325     @type.setter
326     def type(self, type: str):
327         self.impl.setType(type.encode())
328
329     @property
330     def input_ports(self):
331         cdef Port port
332         cdef vector[C.Port] c_ports = self.impl.getInputPorts()
333         py_ports = []
334         for p in c_ports:
335             port = Port()
336             port.impl = p
337             py_ports.append(port)
338         return py_ports
339
340     @input_ports.setter
341     def input_ports(self, ports: list):
342         cdef vector[C.Port] c_ports
343         cdef Port c_port
344         for p in ports:
345             c_port = Port(p.shape)
346             c_ports.push_back(c_port.impl)
347         self.impl.setInputPorts(c_ports)
348
349     @property
350     def output_ports(self):
351         cdef Port port
352         cdef vector[C.Port] c_ports = self.impl.getOutputPorts()
353         py_ports = []
354         for p in c_ports:
355             port = Port()
356             port.impl = p
357             py_ports.append(port)
358         return py_ports
359
360     @output_ports.setter
361     def output_ports(self, ports: list):
362         cdef vector[C.Port] c_ports
363         cdef Port c_port
364         for p in ports:
365             c_port = Port(p.shape)
366             c_ports.push_back(c_port.impl)
367         self.impl.setOutputPorts(c_ports)
368
369     @property
370     def params(self):
371         return {k.decode(): v.decode() for k, v in self.impl.getParameters()}
372
373     @params.setter
374     def params(self, params_map: dict):
375         cdef map[string, string] c_params_map
376         for k, v in params_map.items():
377             c_params_map[k.encode()] = str(v).encode()
378         self.impl.setParameters(c_params_map)
379
380     def build(self):
381         cdef ILayer layer = ILayer()
382         layer.impl = self.impl.build()
383         return layer
384
385     @property
386     def constant_data(self):
387         cdef map[string, Blob.Ptr] c_constant_data
388         c_constant_data = self.impl.getConstantData()
389         constant_data = {}
390         # TODO: Fix LAyerBuilder object copying - pass by reference
391         # constant_data = LayerConstantData()
392         # constant_data.impl = make_shared[C.LayerBuilder](self.impl)
393         cdef BlobBuffer weights_buffer
394         for weights in c_constant_data:
395             weights_buffer = BlobBuffer()
396             weights_buffer.reset(weights.second)
397             constant_data[weights.first.decode()] = weights_buffer.to_numpy()
398         return constant_data
399
400     @constant_data.setter
401     def constant_data(self, data: dict):
402         cdef vector[size_t] dims
403         cdef map[string, Blob.Ptr] c_constant_data
404         cdef Blob.Ptr blob_ptr
405         cdef BlobBuffer buffer
406         data = check_constant_data(data)
407         for k, v in data.items():
408             for dim in v.shape:
409                 dims.push_back(dim)
410             ie_precision = np_precision_map.get(str(v.dtype), None)
411             if not ie_precision:
412                 raise BufferError("Unsupported precision of the data for key {}! Given {} but one of the {} precisions expected".
413                                   format(k, str(v.dtype), ", ".join(np_precision_map.keys())))
414             blob_ptr = self.impl.allocateBlob(dims, ie_precision.encode())
415             buffer = BlobBuffer()
416             buffer.reset(blob_ptr)
417             np_buffer = buffer.to_numpy()
418             np_buffer[:] = v
419             c_constant_data[k.encode()] = blob_ptr
420
421         self.impl.setConstantData(c_constant_data)
422
423     # TODO: Implement get\setGraph when will be supported