def __call__(self, *input_values: NumericData) -> List[NumericData]: """Run computation on input values and return result.""" input_values = [np.array(input_value) for input_value in input_values] input_shapes = [get_shape(input_value) for input_value in input_values] param_names = [param.friendly_name for param in self.parameters] if self.network_cache.get(str(input_shapes)) is None: capsule = Function.to_capsule(self.function) cnn_network = IENetwork(capsule) if self.function.is_dynamic(): cnn_network.reshape(dict(zip(param_names, input_shapes))) # Convert unsupported inputs of the network _convert_inputs(cnn_network) self.network_cache[str(input_shapes)] = cnn_network else: cnn_network = self.network_cache[str(input_shapes)] executable_network = self.runtime.backend.load_network( cnn_network, self.runtime.backend_name) # Input validation if len(input_values) != len(self.parameters): raise UserInputError("Expected %s parameters, received %s.", len(self.parameters), len(input_values)) for parameter, input in zip(self.parameters, input_values): parameter_shape = parameter.get_output_partial_shape(0) input_shape = PartialShape(input.shape) if len(input.shape) > 0 and not parameter_shape.compatible( input_shape): raise UserInputError( "Provided tensor's shape: %s does not match the expected: %s.", input_shape, parameter_shape, ) request = executable_network.requests[0] request.infer(dict(zip(param_names, input_values))) # Set order of output blobs compatible with nG Function result_buffers = [ self.__get_ie_output_blob_buffer(request.output_blobs, result) for result in self.results ] # Since OV overwrite result data type we have to convert results to the original one. original_dtypes = [ get_dtype(result.get_output_element_type(0)) for result in self.results ] converted_buffers = [ buffer.astype(original_dtype) for buffer, original_dtype in zip(result_buffers, original_dtypes) ] return converted_buffers
def assert_list_of_ints(value_list: Iterable[int], message: str) -> None: """! Verify that the provided value is an iterable of integers.""" try: for value in value_list: if not isinstance(value, int): raise TypeError except TypeError: log.warning(message) raise UserInputError(message, value_list)
def __call__(self, *input_values: NumericData) -> List[NumericData]: """Run computation on input values and return result.""" input_values = [np.array(input_value) for input_value in input_values] # Input validation if len(input_values) != len(self.parameters): raise UserInputError("Expected %s parameters, received %s.", len(self.parameters), len(input_values)) for parameter, input in zip(self.parameters, input_values): parameter_shape = parameter.get_output_shape(0) if len(input.shape) > 0 and list(parameter_shape) != list( input.shape): raise UserInputError( "Provided tensor's shape: %s does not match the expected: %s.", list(input.shape), list(parameter_shape), ) request = self.executable_network.requests[0] request.infer(dict(zip(request._inputs_list, input_values))) return [blob.buffer for blob in request.output_blobs.values()]
def __call__(self, *input_values: NumericData) -> List[NumericData]: """Run computation on input values and return result.""" input_values = [np.array(input_value) for input_value in input_values] input_shapes = [get_shape(input_value) for input_value in input_values] if self.network_cache.get(str(input_shapes)) is None: capsule = Function.to_capsule(self.function) cnn_network = IENetwork(capsule) if self.function.is_dynamic(): param_names = [ param.friendly_name for param in self.parameters ] cnn_network.reshape(dict(zip(param_names, input_shapes))) self.network_cache[str(input_shapes)] = cnn_network else: cnn_network = self.network_cache[str(input_shapes)] executable_network = self.runtime.backend.load_network( cnn_network, self.runtime.backend_name) # Input validation if len(input_values) != len(self.parameters): raise UserInputError("Expected %s parameters, received %s.", len(self.parameters), len(input_values)) for parameter, input in zip(self.parameters, input_values): parameter_shape = parameter.get_output_partial_shape(0) input_shape = PartialShape(input.shape) if len(input.shape) > 0 and not parameter_shape.compatible( input_shape): raise UserInputError( "Provided tensor's shape: %s does not match the expected: %s.", input_shape, parameter_shape, ) request = executable_network.requests[0] request.infer(dict(zip(request._inputs_list, input_values))) return [blob.buffer for blob in request.output_blobs.values()]
def import_onnx_model( onnx_protobuf): # type: (onnx.ModelProto) -> List[Function] """ Import an ONNX Protocol Buffers model and convert it into a list of ngraph Functions. :param onnx_protobuf: ONNX Protocol Buffers model (onnx_pb2.ModelProto object) :return: list of ngraph Functions representing computations for each output. """ if not isinstance(onnx_protobuf, onnx.ModelProto): raise UserInputError( 'Input does not seem to be a properly formatted ONNX model.') return onnx_import.import_onnx_model(onnx_protobuf.SerializeToString())
def _check_value(op_name, attr_key, value, val_type, cond=None): # type: (str, str, Any, Type, Optional[Callable[[Any], bool]]) -> bool """! Check whether provided value satisfies specified criteria. @param op_name: The operator name which attributes are checked. @param attr_key: The attribute name. @param value: The value to check. @param val_type: Required value type. @param cond: The optional function running additional checks. :raises UserInputError: @return True if attribute satisfies all criterias. Otherwise False. """ if not np.issubdtype(type(value), val_type): raise UserInputError( '{} operator attribute "{}" value must by of type {}.'.format( op_name, attr_key, val_type)) if cond is not None and not cond(value): raise UserInputError( '{} operator attribute "{}" value does not satisfy provided condition.' .format(op_name, attr_key)) return True
def import_onnx_file(filename): # type: (str) -> List[Function] """ Import ONNX model from a Protocol Buffers file and convert to ngraph functions. :param filename: path to an ONNX file :return: List of imported ngraph Functions (see docs for import_onnx_model). """ try: onnx_protobuf = onnx.load(filename) except DecodeError: raise UserInputError( 'The provided file doesn\'t contain a properly formatted ONNX model.' ) return onnx_import.import_onnx_model(onnx_protobuf.SerializeToString())
def _convert_to_variant(item: Any) -> Variant: """Convert value to Variant class, otherwise throw error.""" if isinstance(item, Variant): return item variant_mapping = { int: VariantInt, str: VariantString, } new_type = variant_mapping.get(type(item), None) if new_type is None: raise UserInputError("Cannot map value to any of registered Variant classes", str(item)) return new_type(item)
def infer_dimensions(node_name, input_shape, output_shape): # type: (str, Shape, Shape) -> List[int] """Infer `output_shape` dimension values. :param node_name: The input node name. :param input_shape: The input data shape. :param output_shape: The requested output shape for the input node data. """ input_shape = list(input_shape) output_shape = list(output_shape) # If an output dimension is equal to zero its actual value is copied from the input shape # argument. for idx, dim in enumerate(output_shape): if dim == 0: try: output_shape[idx] = input_shape[idx] except IndexError: raise UserInputError( 'Reshape node (%s): can not copy dimension from the shape ' 'argument since requested index is out of range.', node_name) # Check whether there are dimensions equal to -1 in output_shape. There may be at most one # such case. Its value is then inferred from the size of the tensor and the remaining # dimensions. if output_shape.count(-1) > 1: raise UserInputError( 'Reshape node (%s): more than one dimension is set to (-1). Only one ' 'dimension value can be inferred.', node_name) elif -1 in output_shape: idx = output_shape.index(-1) output_shape[idx] = 1 output_shape[idx] = int( np.product(input_shape) / np.product(output_shape)) return output_shape
def reorder_axes(node, axes_order): # type: (NgraphNode, List[int]) -> NgraphNode """Permute axes according to specified axes_order parameter. :param node: The node which axes we want to permute. :param axes_order: The permutation of node tensor axes. :return: New node with permuted axes. """ out_shape = list(node.shape) if axes_order is None: axes_order = list(range(len(node.shape))) elif len(axes_order) != len(node.shape): raise UserInputError( 'Node (%s): provided axes count is different than ' 'input tensor rank.', node.name) else: for idx, axis in enumerate(axes_order): try: out_shape[idx] = node.shape[axis] except IndexError: raise UserInputError( 'Node (%s): provided axes indices are out ' 'of range.', node.name) return ng.reshape(node, out_shape, axes_order)
def _write_ndarray_to_tensor_view(value, tensor_view): # type: (np.ndarray, TensorViewType) -> None tensor_view_dtype = get_dtype(tensor_view.element_type) if list(tensor_view.shape) != list(value.shape) and len( value.shape) > 0: raise UserInputError( 'Provided tensor\'s shape: %s does not match the expected: %s.', list(value.shape), list(tensor_view.shape)) if value.dtype != tensor_view_dtype: log.warning( 'Attempting to write a %s value to a %s tensor. Will attempt type conversion.', value.dtype, tensor_view.element_type) value = value.astype(tensor_view_dtype) buffer_size = Computation._get_buffer_size(tensor_view.element_type, tensor_view.element_count) tensor_view.write(util.numpy_to_c(np.ascontiguousarray(value)), 0, buffer_size)
def check_valid_attribute(op_name, attr_dict, attr_key, val_type, cond=None, required=False): # type: (str, dict, str, Type, Optional[Callable[[Any], bool]], Optional[bool]) -> bool """! Check whether specified attribute satisfies given criteria. @param op_name: The operator name which attributes are checked. @param attr_dict: Dictionary containing key-value attributes to check. @param attr_key: Key value for validated attribute. @param val_type: Value type for validated attribute. @param cond: Any callable wich accept attribute value and returns True or False. @param required: Whether provided attribute key is not required. This mean it may be missing from provided dictionary. :raises UserInputError: @return True if attribute satisfies all criterias. Otherwise False. """ result = True if required and attr_key not in attr_dict: raise UserInputError( 'Provided dictionary is missing {} operator required attribute "{}"' .format(op_name, attr_key)) if attr_key not in attr_dict: return result attr_value = attr_dict[attr_key] if np.isscalar(attr_value): result = result and _check_value(op_name, attr_key, attr_value, val_type, cond) else: for v in attr_value: result = result and _check_value(op_name, attr_key, v, val_type, cond) return result
def __init__(self, model_proto): # type: (onnx.ModelProto) -> None self._proto = model_proto if not isinstance(model_proto, onnx.ModelProto): raise UserInputError( 'Input does not seem to be a properly formatted ONNX file.') # Parse op sets listed in model ai_onnx_opset_version = None for opset in model_proto.opset_import: if opset.domain in ['ai.onnx', '']: ai_onnx_opset_version = opset.version else: raise NotImplementedError( 'Operator set for domain %s ' 'is not supported', opset.domain) self.node_factory = get_node_factory( opset_version=ai_onnx_opset_version) self.graph = GraphWrapper(model_proto.graph, self) super(ModelWrapper, self).__init__(model_proto, self.graph)
def _write_ndarray_to_tensor_view(value: np.ndarray, tensor_view: Tensor) -> None: tensor_view_dtype = get_dtype(tensor_view.element_type) if list(tensor_view.shape) != list(value.shape) and len( value.shape) > 0: raise UserInputError( "Provided tensor's shape: %s does not match the expected: %s.", list(value.shape), list(tensor_view.shape), ) if value.dtype != tensor_view_dtype: log.warning( "Attempting to write a %s value to a %s tensor. Will attempt type conversion.", value.dtype, tensor_view.element_type, ) value = value.astype(tensor_view_dtype) buffer_size = Computation._get_buffer_size(tensor_view.element_type, tensor_view.element_count) nparray = np.ascontiguousarray(value) tensor_view.write(util.numpy_to_c(nparray), buffer_size)
def numpy_style_broadcast_output_shape(shape_a, shape_b): # type: (TensorShape, TensorShape) -> Tuple[TensorShape, TensorShape, TensorShape] """Calculate output shape of numpy-style broadcast operation. :param shape_a: shape of first input tensor :param shape_b: shape of the second input tensor :return: shape of the output tensor, full shape of input tensors """ output_shape = [] # type: List[int] shape_a = list(shape_a) shape_b = list(shape_b) rank_a = len(shape_a) rank_b = len(shape_b) max_rank = max(rank_a, rank_b) # left-pad A's shape with 1s until it also has p dimensions if rank_a < max_rank: for idx in range(max_rank - rank_a): shape_a.insert(0, 1) # left-pad B's shape with 1s until is also has p dimensions elif rank_b < max_rank: for idx in range(max_rank - rank_b): shape_b.insert(0, 1) for idx in range(max_rank - 1, -1, -1): idx_dim_a = shape_a[idx] idx_dim_b = shape_b[idx] if idx_dim_a != 1 and idx_dim_b != 1 and idx_dim_a != idx_dim_b: raise UserInputError( 'Shapes %s and %s are incompatible for broadcasting.', shape_a, shape_b) output_shape.insert(0, max(idx_dim_a, idx_dim_b)) return output_shape, shape_a, shape_b
def numpy_style_broadcast_for_binary_operation(onnx_node, ng_inputs): # type: (NodeWrapper, List[NgraphNode]) -> NgraphNode """ Cast shape of two nodes to make them compatible for an element-wise binary operation. :param onnx_node: a wrapped ONNX node :param ng_inputs: left and right node (inputs of the binary op) :return: left and right node after broadcasting """ left = ng_inputs[0] right = ng_inputs[1] dimensions_identical = list(left.shape) == list(right.shape) if dimensions_identical: return left, right try: output_shape, left_full_shape, right_full_shape = numpy_style_broadcast_output_shape( left.shape, right.shape) except UserInputError: raise UserInputError( '%s node (%s): Unable to broadcast shapes %s and %s.', onnx_node.op_type, onnx_node.name, left.shape, right.shape) if list(right.shape) != output_shape: one_pos = [i for i, dim in enumerate(right_full_shape) if dim == 1] right = ng.reshape(right, [dim for dim in right.shape if dim != 1]) # Squeeze right = ng.broadcast(right, output_shape, broadcast_axes=one_pos) if list(left.shape) != output_shape: one_pos = [i for i, dim in enumerate(left_full_shape) if dim == 1] left = ng.reshape(left, [dim for dim in left.shape if dim != 1]) left = ng.broadcast(left, output_shape, broadcast_axes=one_pos) return left, right
def i420_to_rgb( arg: NodeInput, arg_u: Optional[NodeInput] = None, arg_v: Optional[NodeInput] = None, name: Optional[str] = None, ) -> Node: """Return a node which performs I420toRGB operation. :param arg: The node providing single or Y plane data. :param arg_u: The node providing U plane data. Required for separate planes. :param arg_v: The node providing V plane data. Required for separate planes. :param name: The optional name for the created output node. :return: The new node performing I420toRGB operation. """ if arg_u is None and arg_v is None: inputs = as_nodes(arg) elif arg_u is not None and arg_v is not None: inputs = as_nodes(arg, arg_u, arg_v) else: raise UserInputError( "Operation I420toRGB must have one (single plane) or three (separate planes) inputs provided." ) return _get_node_factory_opset8().create("I420toRGB", inputs)
def create( self, op_type_name: str, arguments: Optional[List[Union[Node, Output]]] = None, attributes: Optional[Dict[str, Any]] = None, ) -> Node: """Create node object from provided description. The user does not have to provide all node's attributes, but only required ones. :param op_type_name: The operator type name. :param arguments: The operator arguments. :param attributes: The operator attributes. returns Node object representing requested operator with attributes set. """ if arguments is None and attributes is None: node = self.factory.create(op_type_name) node._attr_cache = {} node._attr_cache_valid = False return node if arguments is None and attributes is not None: raise UserInputError( 'Error: cannot create "{}" op without arguments.'.format( op_type_name)) if attributes is None: attributes = {} assert arguments is not None arguments = self._arguments_as_outputs(arguments) node = self.factory.create(op_type_name, arguments, attributes) # Currently we don't support any attribute getters & setters for TensorIterator node. if node.get_type_name() == "TensorIterator": return node # Set getters and setters for each node's attribute. # node.get_attribute_name() # node.set_attribute_name() # For compound (with more than one level of nesting) attributes of form ie.: # node.class_member_name.some_metric.attr_name: # node.get_some_metric_attr_name() # node.set_some_metric_attr_name() # Please see test_dyn_attributes.py for more usage examples. all_attributes = node.get_attributes() for attr_name in all_attributes.keys(): setattr( node, self._normalize_attr_name_getter(attr_name), partial(NodeFactory._get_node_attr_value, node, attr_name), ) setattr( node, self._normalize_attr_name_setter(attr_name), partial(NodeFactory._set_node_attr_value, node, attr_name), ) # Setup helper members for caching attribute values. # The cache would be lazily populated at first access attempt. node._attr_cache = {} node._attr_cache_valid = False return node