Coverage for mlprodict/onnxrt/ops_empty/_op.py: 71%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- encoding: utf-8 -*-
2"""
3@file
4@brief Shortcut to *ops_onnxruntime*.
5"""
6import numpy
7import onnx.defs
8from onnx.helper import make_tensor
9import skl2onnx.algebra.onnx_ops as alg
10try:
11 import skl2onnx.algebra.custom_ops as alg2
12except ImportError: # pragma: no cover
13 # older version of skl2onnx
14 alg2 = alg
15from ...onnx_tools.onnx2py_helper import guess_proto_dtype
16from ...onnx_tools.optim.graph_schema_helper import (
17 get_defined_inputs, get_defined_outputs, proto2vars)
20_schemas = {
21 schema.name: schema for schema in onnx.defs.get_all_schemas_with_history()}
24class OpRunOnnxEmpty:
25 """
26 Unique operator for an empty runtime.
27 """
29 def __init__(self, onnx_node, desc=None, variables=None,
30 dtype=None, **options):
31 """
32 :param onnx_node: :epkg:`onnx` node
33 :param desc: internal representation
34 :param variables: registered variables created by previous operators
35 :param dtype: float computation type
36 :param options: runtime options
37 """
38 self._provider = 'empty'
39 self.onnx_node = onnx_node
40 self.desc = desc
41 self._schema = _schemas.get(onnx_node.op_type, None)
42 if desc is not None:
43 if 'atts' in desc:
44 for a, b in desc['atts'].items():
45 if not isinstance(b, dict) or 'value' not in b:
46 raise ValueError( # pragma: no cover
47 "Unexpected value {}.".format(b))
48 options[a] = b['value']
50 self.options = options
51 self.dtype = dtype
52 self._init(variables)
54 def _name_mapping(self, inputs):
55 mapping = {}
56 new_inputs = []
57 for name in inputs:
58 if name in mapping:
59 i = 0
60 new_name = "{}_{}".format(name, i)
61 while new_name in mapping:
62 i += 1 # pragma: no cover
63 new_name = "{}_{}".format(name, i) # pragma: no cover
64 mapping[new_name] = name
65 new_inputs.append(new_name)
66 else:
67 new_inputs.append(name)
68 mapping[name] = name
69 return mapping, new_inputs
71 def _guess_proto_type(self, dtype):
72 return guess_proto_dtype(dtype)
74 def _init(self, variables=None):
75 """
76 Initializes the node.
78 @param variables registered variables created by previous operators
80 The current implementation for operator *Scan*
81 only works for matrices.
82 """
83 try:
84 self.alg_class = getattr(alg2, 'Onnx' + self.onnx_node.op_type)
85 except AttributeError:
86 try:
87 self.alg_class = getattr(alg, 'Onnx' + self.onnx_node.op_type)
88 except AttributeError:
89 self.alg_class = None
90 inputs = list(self.onnx_node.input)
91 self.mapping, self.inputs = self._name_mapping(inputs)
92 self.outputs = list(self.onnx_node.output)
94 options = self.options.copy()
95 target_opset = options.pop('target_opset', None)
96 domain = options.pop('domain', None)
97 # disable_optimisation = options.pop('disable_optimisation', False)
98 # ir_version = options.pop('ir_version', None)
100 if self.alg_class is None:
101 self.onnx_ = self.onnx_node
102 elif self.onnx_node.op_type == 'ConstantOfShape':
103 for k in options:
104 v = options[k]
105 if isinstance(v, numpy.ndarray):
106 options[k] = make_tensor(
107 k, self._guess_proto_type(v.dtype),
108 v.shape, v.tolist())
110 self.inst_ = self.alg_class(*self.inputs, output_names=self.outputs,
111 op_version=target_opset, **options)
112 inputs = get_defined_inputs(
113 self.inputs, variables, dtype=self.dtype)
114 try:
115 self.onnx_ = self.inst_.to_onnx(inputs, target_opset=target_opset,
116 domain=domain)
117 if "dim_value: 0" in str(self.onnx_):
118 raise RuntimeError( # pragma: no cover
119 "Probable issue as one dimension is null.\n--\n{}".format(
120 self.onnx_))
121 except AttributeError as e: # pragma: no cover
122 # older version of skl2onnx
123 self.onnx_ = self.inst_.to_onnx(inputs)
124 if "dim_value: 0" in str(self.onnx_):
125 raise RuntimeError(
126 "Probable issue as one dimension is null.\n--\n{}".format(
127 self.onnx_)) from e
128 elif self.onnx_node.op_type == 'Scan':
129 self.inst_ = self.alg_class(
130 *self.inputs, output_names=self.outputs,
131 op_version=target_opset, **options)
132 inputs = get_defined_inputs(
133 self.inputs, variables, dtype=self.dtype)
134 outputs = get_defined_outputs(
135 self.outputs, self.onnx_node, inputs, variables,
136 dtype=self.dtype)
137 inputs = [(name, cl.__class__([None, None]))
138 for (name, cl) in inputs]
139 outputs = [(name, cl.__class__([None, None]))
140 for (name, cl) in outputs]
141 self.onnx_ = self.inst_.to_onnx(inputs, outputs=outputs,
142 target_opset=target_opset,
143 domain=domain)
144 if "dim_value: 0" in str(self.onnx_):
145 raise RuntimeError( # pragma: no cover
146 "Probable issue as one dimension is null.\n--\n{}".format(
147 self.onnx_))
148 else:
149 self.inst_ = self.alg_class(*self.inputs, output_names=self.outputs,
150 op_version=target_opset, domain=domain,
151 **options)
152 inputs = get_defined_inputs(
153 self.inputs, variables, dtype=self.dtype)
155 try:
156 self.onnx_ = self.inst_.to_onnx(
157 inputs, target_opset=target_opset, domain=domain)
158 if "dim_value: 0" in str(self.onnx_):
159 raise RuntimeError( # pragma: no cover
160 "Probable issue as one dimension is null.\n--\n{}\n---\n{}".format(
161 self.onnx_, inputs))
162 except (RuntimeError, ValueError): # pragma: no cover
163 # Let's try again by forcing output types.
164 outputs = get_defined_outputs(
165 self.outputs, self.onnx_node, inputs, variables,
166 dtype=self.dtype)
167 self.onnx_ = self.inst_.to_onnx(inputs, outputs=outputs,
168 target_opset=target_opset,
169 domain=domain)
170 if "dim_value: 0" in str(self.onnx_):
171 raise RuntimeError( # pragma: no cover
172 "Probable issue as one dimension is null.\n--\n{}".format(
173 self.onnx_)) from e
175 if hasattr(self.onnx_, 'graph'):
176 if len(self.onnx_.graph.output) != len(self.outputs): # pragma: no cover
177 # Something is wrong, falls back to default plan.
178 outputs = get_defined_outputs(
179 self.outputs, self.onnx_node, inputs, variables,
180 dtype=self.dtype)
181 self.onnx_ = self.inst_.to_onnx(inputs, outputs=outputs,
182 target_opset=target_opset,
183 domain=domain)
184 if "dim_value: 0" in str(self.onnx_):
185 raise RuntimeError( # pragma: no cover
186 "Probable issue as one dimension is null.\n--\n{}".format(
187 self.onnx_))
188 else:
189 lo = list(self.onnx_.graph.output)
190 outputs = proto2vars(lo)
191 else:
192 outputs = [(o, None) for o in self.onnx_.output]
194 self.typed_outputs_ = outputs
196 def run(self, *args, **kwargs):
197 """
198 Should be overwritten.
199 """
200 # inputs = {name: val for name, val in zip(self.inputs, args)}
201 raise RuntimeError( # pragma: no cover
202 "This runtime does nothing. Running it is useless.")
204 def need_context(self):
205 """
206 Tells the runtime if this node needs the context
207 (all the results produced so far) as it may silently access
208 one of them (operator Loop).
209 The default answer is `False`.
210 """
211 return False