Coverage for mlprodict/grammar/grammar_sklearn/grammar/gactions.py: 94%
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"""
2@file
3@brief Action definition.
4"""
5import numpy
6from .api_extension import AutoAction
7from .gtypes import (
8 MLType, MLNumTypeFloat32, MLNumTypeFloat64,
9 MLTensor, MLNumTypeInt32, MLNumTypeInt64, MLNumTypeBool)
12class MLAction(AutoAction):
13 """
14 Base class for every action.
15 """
17 def __init__(self, inputs, output, name, children=None):
18 """
19 @param inputs type of inputs
20 @param output output type
21 @param name a name which identifies the action
22 @param children actions used to compute this one
23 """
24 if not isinstance(inputs, list):
25 raise TypeError(
26 'inputs must be a list of MLType.') # pragma: no cover
27 for t in inputs:
28 if not isinstance(t, MLType):
29 raise TypeError( # pragma: no cover
30 "Every input must be a MLType not '{0}'.".format(type(t)))
31 if not isinstance(output, MLType):
32 raise TypeError('output must be of MLType.') # pragma: no cover
33 self.inputs = inputs
34 self.output = output
35 self.name = name
36 self.children = children if children is not None else []
37 for child in self.children:
38 if not isinstance(child, MLAction): # pragma: no cover
39 raise TypeError("All children must be of type MLAction")
41 def execute(self, **kwargs):
42 """
43 Computes the action. Returns the output.
44 """
45 # It must be overwritten.
46 self.children_results_ = [child.execute(
47 **kwargs) for child in self.children]
48 for v, tv in zip(self.children_results_, self.inputs):
49 tv.validate(v)
51 @property
52 def ChildrenResults(self):
53 """
54 Return the last execution results.
55 """
56 return self.children_results_
58 def enumerate_variables(self):
59 """
60 Enumerates all variables.
61 """
62 for child in self.children:
63 for var in child.enumerate_variables():
64 yield var
66 def graph_execution(self):
67 """
68 Returns a formated string which retruns the outputs.
69 """
70 rows = []
71 rows.append("-- BEGIN {0} {3} id={1} output={2}".format(
72 self.name, id(self), self.output._cache, getattr(self, "comment", "")))
73 for i, ch in enumerate(self.children):
74 gr = ch.graph_execution()
75 temp = [" " + li for li in gr.split("\n")]
76 temp[0] = " {0}-".format(i) + temp[0][4:]
77 rows.extend(temp)
78 rows.append(
79 "-- END {0} -- output={1}".format(self.name, self.output._cache))
80 return "\n".join(rows)
82 @AutoAction.cache
83 def _export_json(self, hook=None, result_name=None):
84 val = {"output": self.output._export_json()}
85 if self.children:
86 val["action"] = dict(name=self.name,
87 variants=[c._export_json(hook=hook) for c in self.children])
88 else:
89 val["action"] = dict(name=self.name)
90 if self.inputs:
91 val["input"] = [i._export_json(hook=hook)
92 for i in self.inputs]
93 return val
95 @AutoAction.cache
96 def _export_c(self, hook=None, result_name=None):
97 if result_name is None:
98 raise ValueError(
99 "result_name must not be None") # pragma: no cover
100 rows = []
101 rows.append("// {0}-{1} - children".format(id(self), self.name))
102 names = []
103 if self.children:
104 for i, c in enumerate(self.children):
105 rname = "{0}{1}{2}".format(
106 result_name, getattr(self, "cname", ""), i)
107 dc = c._export_c(hook=hook, result_name=rname)
108 if not dc['cache']:
109 rows.append(dc['code'])
110 names.append(dc['result_name'])
111 rows.append("// {0}-{1} - itself".format(id(self), self.name))
112 res = "\n".join(rows)
113 return {'code': res, 'result_name': result_name, 'child_names': names}
116class MLActionCst(MLAction):
117 """
118 Constant
119 """
121 def __init__(self, cst, inout_type=None, comment=None):
122 """
123 @param cst constant
124 @param inout_type type
125 @param comment comment
126 """
127 if inout_type is None:
128 inout_type = MLActionCst.guess_type(cst)
129 MLAction.__init__(self, [], inout_type, "cst")
130 inout_type.validate(cst)
131 self.cst = cst
132 self.comment = comment
134 @staticmethod
135 def guess_type(value):
136 """
137 Guesses a type given a value.
138 """
139 if isinstance(value, numpy.float32):
140 return MLNumTypeFloat32()
141 if isinstance(value, numpy.float64):
142 return MLNumTypeFloat64()
143 if isinstance(value, (int, numpy.int32)):
144 return MLNumTypeInt32()
145 if isinstance(value, (int, numpy.int64)):
146 return MLNumTypeInt64()
147 if isinstance(value, numpy.ndarray):
148 a = numpy.zeros(1, value.dtype)
149 t = MLActionCst.guess_type(a[0])
150 return MLTensor(t, value.shape)
151 raise NotImplementedError( # pragma: no cover
152 "Not implemented for type '{0}'".format(type(value)))
154 def execute(self, **kwargs):
155 MLAction.execute(self, **kwargs)
156 return self.output.validate(self.cst)
158 def graph_execution(self):
159 if self.comment:
160 return "cst: {0} = {1}".format(self.comment, self.cst)
161 return "cst: {0}".format(self.cst)
163 @AutoAction.cache
164 def _export_json(self, hook=None, result_name=None):
165 res = {"name": "cst",
166 "value": self.output._format_value_json(self.cst, hook=hook)}
167 if hasattr(self, "comment"):
168 res["comment"] = self.comment
169 return res
171 @AutoAction.cache
172 def _export_c(self, hook=None, result_name=None):
173 if result_name is None:
174 raise ValueError("result_name cannot be None.") # pragma: no cover
175 dc = self.output._export_c(hook='declare', result_name=result_name)
176 res = "{0} = {1};".format(
177 dc['code'], self.output._format_value_c(self.cst))
178 if self.comment:
179 res += " // {0}".format(self.comment)
180 return {'code': res, 'result_name': result_name}
183class MLActionVar(MLActionCst):
184 """
185 Variable. The constant is only needed to guess the
186 variable type.
187 """
189 def __init__(self, value, name, inout_type=None):
190 """
191 @param value value
192 @param name variable name
193 @param inout_type type
194 """
195 MLActionCst.__init__(self, value, inout_type)
196 self.name = "var"
197 self.name_var = name
199 def execute(self, **kwargs):
200 MLAction.execute(self, **kwargs)
201 if self.name_var not in kwargs:
202 raise KeyError( # pragma: no cover
203 "Unable to find variable name '{0}'".format(self.name_var))
204 return self.output.validate(kwargs[self.name_var])
206 def enumerate_variables(self):
207 """
208 Enumerates itself.
209 """
210 yield self
212 def graph_execution(self):
213 return "var: {0} = {1} ({2})".format(self.name_var, self.name, self.output._cache)
215 @AutoAction.cache
216 def _export_json(self, hook=None, result_name=None):
217 return {"name": "var", "value": self.name_var}
219 @AutoAction.cache
220 def _export_c(self, hook=None, result_name=None):
221 if result_name is None:
222 raise ValueError( # pragma: no cover
223 "result_name must not be None")
224 dc = self.output._export_c(hook='typeref', result_name=result_name)
225 res = "{0} = {1};".format(dc['code'], self.name_var)
226 return {'code': res, 'result_name': result_name}
229class MLActionFunctionCall(MLAction):
230 """
231 Any function call.
232 """
234 def __init__(self, name, output, *acts):
235 """
236 @param name function name
237 @param output type
238 @param *acts list of arguments
239 """
240 for act in acts:
241 if not isinstance(act, MLAction):
242 raise TypeError( # pragma: no cover
243 "All element of acts must be MLAction not '{0}'.".format(type(act)))
244 MLAction.__init__(self, [act.output for act in acts],
245 output, name, children=acts)
246 self.cname = 'c'
248 def _optional_parameters(self):
249 """
250 Returns additional parameters to add the function call.
251 """
252 return None
254 @AutoAction.cache
255 def _export_c(self, hook=None, result_name=None):
256 if result_name is None:
257 raise ValueError(
258 "result_name must not be None") # pragma: no cover
259 dcf = MLAction._export_c(self, hook=hook, result_name=result_name)
260 rows = [dcf['code']]
261 fcall = ", ".join(dcf['child_names'])
262 add = self._optional_parameters() # pylint: disable=E1128
263 if add is not None:
264 fcall = ", ".join([fcall, add])
265 dc = self.output._export_c(hook='declare', result_name=result_name)
266 rows.append(dc['code'] + ";")
267 ep = self.output._byref_c()
268 type_list = "_".join(c.output.CTypeSingle for c in self.children)
269 rows.append("{0}_{4}({3}{1}, {2});".format(
270 self.name, result_name, fcall, ep, type_list))
271 rows.append("// {0}-{1} - done".format(id(self), self.name))
272 # Addition printf to debug the C++ code.
273 # rows.append('printf("C++ {1} %f\\n", {0});'.format(result_name, self.name))
274 res = {'code': "\n".join(rows), 'result_name': dcf['result_name']}
275 return res
278class MLActionBinary(MLAction):
279 """
280 Any binary operation.
281 """
283 def __init__(self, act1, act2, name):
284 """
285 @param act1 first element
286 @param act2 second element
287 @param name operator name
288 """
289 if not isinstance(act1, MLAction):
290 raise TypeError("act1 must be MLAction.") # pragma: no cover
291 if not isinstance(act2, MLAction):
292 raise TypeError("act2 must be MLAction.") # pragma: no cover
293 MLAction.__init__(self, [act1.output, act2.output], act2.output, name,
294 children=[act1, act2])
296 @AutoAction.cache
297 def _export_c(self, hook=None, result_name=None):
298 if result_name is None:
299 raise ValueError(
300 "result_name must not be None") # pragma: no cover
301 dc = MLAction._export_c(self, hook=hook, result_name=result_name)
302 rows = [dc['code']]
303 dc2 = self.output._export_c(hook='type')
304 op = "{2} {0} = {0}0 {1} {0}1;".format(
305 result_name, self.name, dc2['code'])
306 rows.append(op)
307 rows.append("// {0}-{1} - done".format(id(self), self.name))
308 return {'code': "\n".join(rows), 'result_name': result_name}
311class MLActionUnary(MLAction):
312 """
313 Any binary operation.
314 """
316 def __init__(self, act1, name):
317 """
318 @param act1 element
319 @param name operator name
320 """
321 if not isinstance(act1, MLAction):
322 raise TypeError("act1 must be MLAction.") # pragma: no cover
323 MLAction.__init__(self, [act1.output], act1.output, name,
324 children=[act1])
326 @AutoAction.cache
327 def _export_c(self, hook=None, result_name=None):
328 if result_name is None:
329 raise ValueError( # pragma: no cover
330 "result_name must not be None")
331 dc = MLAction._export_c(self, hook=hook, result_name=result_name)
332 rows = [dc['code']]
333 op = "auto {0} = {1} {0}0;".format(result_name, self.name)
334 rows.append(op)
335 rows.append("// {0}-{1} - done".format(id(self), self.name))
336 return {'code': "\n".join(rows), 'result_name': result_name}
339class MLActionConcat(MLActionFunctionCall):
340 """
341 Concatenate number of arrays into an array.
342 """
344 def __init__(self, act1, act2):
345 """
346 @param act1 first element
347 @param act2 second element
348 """
349 if not isinstance(act1, MLAction):
350 raise TypeError("act1 must be MLAction.") # pragma: no cover
351 if not isinstance(act2, MLAction):
352 raise TypeError("act2 must be MLAction.") # pragma: no cover
353 n1 = (1 if isinstance(act1.output, (MLNumTypeFloat32, MLNumTypeFloat64))
354 else act1.output.dim[0])
355 n2 = (1 if isinstance(act2.output, (MLNumTypeFloat32, MLNumTypeFloat64))
356 else act2.output.dim[0])
357 MLActionFunctionCall.__init__(self, "concat", MLTensor(
358 act1.output.__class__(), (n1 + n2,)), act1, act2)
360 def execute(self, **kwargs):
361 """
362 Concatenation
363 """
364 MLActionFunctionCall.execute(self, **kwargs)
365 res = self.ChildrenResults
366 return self.output.validate(numpy.array(res))
369class MLActionCast(MLActionUnary):
370 """
371 Cast into another type.
372 """
374 def __init__(self, act1, new_type):
375 """
376 @param act1 element
377 @param new_type new type
378 """
379 MLActionUnary.__init__(self, act1, "cast")
380 self.output = new_type
382 def execute(self, **kwargs):
383 MLActionUnary.execute(self, **kwargs)
384 res = self.ChildrenResults
385 return self.output.validate(self.output.cast(res[0]))
387 @AutoAction.cache
388 def _export_c(self, hook=None, result_name=None):
389 raise NotImplementedError( # pragma: no cover
390 "Not enough information to do it here.")
393class MLActionIfElse(MLAction):
394 """
395 Addition
396 """
398 def __init__(self, cond, act1, act2, check_type=True, comment=None):
399 """
400 @param cond condition
401 @param act1 first action
402 @param ect2 second action
403 @param check_type check ype
404 @param comment comment
405 """
406 if not isinstance(act1, MLAction):
407 raise TypeError("act1 must be MLAction.") # pragma: no cover
408 if not isinstance(act2, MLAction):
409 raise TypeError("act2 must be MLAction.") # pragma: no cover
410 if not isinstance(cond, MLAction):
411 raise TypeError("cond must be MLAction.") # pragma: no cover
412 if not isinstance(cond.output, MLNumTypeBool):
413 raise TypeError( # pragma: no cover
414 "No boolean condition {0}".format(type(cond.output)))
415 if check_type and type(act1.output) != type(act2.output):
416 raise TypeError("Not the same input type {0} != {1}".format( # pragma: no cover
417 type(act1.output), type(act2.output)))
418 MLAction.__init__(self, [cond.output, act1.output, act2.output], act2.output, "if",
419 children=[cond, act1, act2])
420 self.comment = comment
422 def execute(self, **kwargs):
423 self.children_results_ = [
424 self.children[0].execute(**kwargs), None, None]
425 self.inputs[0].validate(self.children_results_[0])
426 if self.children_results_[0]:
427 self.children_results_[1] = self.children[1].execute(**kwargs)
428 self.inputs[1].validate(self.children_results_[1])
429 res = self.children_results_[1]
430 else:
431 self.children_results_[2] = self.children[2].execute(**kwargs)
432 self.inputs[2].validate(self.children_results_[2])
433 res = self.children_results_[2]
434 return self.output.validate(res)
436 @AutoAction.cache
437 def _export_c(self, hook=None, result_name=None):
438 if result_name is None:
439 raise ValueError(
440 "result_name must not be None") # pragma: no cover
441 dc = MLAction._export_c(self, hook=hook, result_name=result_name)
442 rows = [dc['code']]
443 dc2 = self.output._export_c(hook='type')
444 op = "{1} {0} = {0}0 ? {0}1 : {0}2;".format(result_name, dc2['code'])
445 rows.append(op)
446 rows.append("// {0}-{1} - done".format(id(self), self.name))
447 return {'code': "\n".join(rows), 'result_name': result_name}
450class MLActionReturn(MLAction):
451 """
452 Returns a results.
453 """
455 def __init__(self, act):
456 """
457 @param act action to return
458 """
459 MLAction.__init__(self, [act.output],
460 act.output, "return", children=[act])
462 def execute(self, **kwargs):
463 MLAction.execute(self, **kwargs)
464 res = self.ChildrenResults
465 return self.output.validate(res[0])
467 @AutoAction.cache
468 def _export_c(self, hook=None, result_name=None):
469 if len(self.children) != 1:
470 raise ValueError(
471 "Only one result can be returned.") # pragma: no cover
472 if result_name is None:
473 raise ValueError(
474 "result_name must not be None") # pragma: no cover
475 dc = self.children[0]._export_c(hook=hook, result_name=result_name)
476 if not dc['cache']:
477 code = dc['code']
478 else:
479 code = ''
481 add = self.output._copy_c(
482 result_name, result_name[:-1], hook="typeref")
483 code += "\n" + add
484 return {'code': code, 'result_name': result_name}
487class MLActionFunction(MLActionUnary):
488 """
489 A function.
490 """
492 def __init__(self, act, name):
493 """
494 @param act action
495 @param name name
496 """
497 if not isinstance(act, MLActionReturn):
498 raise NotImplementedError( # pragma: no cover
499 "Last result must be MLActionReturn.")
500 MLActionUnary.__init__(self, act, name)
502 def execute(self, **kwargs):
503 MLActionUnary.execute(self, **kwargs)
504 res = self.ChildrenResults
505 return self.output.validate(res[0])
507 @AutoAction.cache
508 def _export_c(self, hook=None, result_name=None):
509 if result_name is None:
510 raise ValueError(
511 "result_name must not be None") # pragma: no cover
512 if len(self.children) != 1:
513 raise ValueError(
514 "The function must return one result.") # pragma: no cover
515 if result_name[-1] == '0':
516 raise ValueError( # pragma: no cover
517 "result_name '{0}' cannot end with 0.".format(result_name))
519 vars = {v.name: v for v in self.enumerate_variables()}
520 vars = [_[1] for _ in list(sorted(vars.items()))]
521 parameters = ", ".join("{0} {1}".format(
522 v.output._export_c(hook='type')['code'], v.name_var) for v in vars)
523 typename = self.children[0].output._export_c(
524 hook='typeref', result_name=result_name)['code']
525 signature = "int {1} ({0}, {2})".format(
526 typename, self.name, parameters)
527 dc = MLAction._export_c(self, hook=hook, result_name=result_name)
528 code = dc['code']
529 rows = [signature, "{"]
530 rows.extend(" " + line for line in code.split("\n"))
531 rows.extend(
532 [' return 0;', " // {0}-{1} - done".format(id(self), self.name), '}'])
533 return {'code': "\n".join(rows), 'result_name': result_name}