From 7a709a8f8afa825db428b9b32866913484e3311b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?xavier=20dupr=C3=A9?= Date: Wed, 22 Jul 2020 14:40:05 +0200 Subject: [PATCH 1/3] Fixes #148, add operator Atan --- .../ut_onnxrt/test_onnxrt_python_runtime_.py | 5 +++- mlprodict/onnxrt/ops_cpu/_op_list.py | 1 + mlprodict/onnxrt/ops_cpu/op_atan.py | 26 +++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 mlprodict/onnxrt/ops_cpu/op_atan.py diff --git a/_unittests/ut_onnxrt/test_onnxrt_python_runtime_.py b/_unittests/ut_onnxrt/test_onnxrt_python_runtime_.py index bbdd667c7..e967c7c00 100644 --- a/_unittests/ut_onnxrt/test_onnxrt_python_runtime_.py +++ b/_unittests/ut_onnxrt/test_onnxrt_python_runtime_.py @@ -21,7 +21,7 @@ except ImportError: from sklearn.utils.testing import ignore_warnings from skl2onnx.algebra.onnx_ops import ( # pylint: disable=E0611 - OnnxAbs, OnnxAdd, OnnxArgMax, OnnxArgMin, + OnnxAbs, OnnxAdd, OnnxArgMax, OnnxArgMin, OnnxAtan, OnnxBatchNormalization, OnnxConcat, OnnxCeil, OnnxClip, OnnxConstant, OnnxConstantOfShape, @@ -396,6 +396,9 @@ def test_onnxt_runtime_argmin_12(self): self.assertEqualArray(numpy.array([2, 1], dtype=numpy.int64), got['Y'], decimal=6) + def test_onnxt_runtime_atan(self): + self.common_test_onnxt_runtime_unary(OnnxAtan, numpy.arctan) + def test_onnxt_runtime_batch_normalization(self): # input size: (1, 2, 1, 3) x = numpy.array([[[[-1, 0, 1]], [[2, 3, 4]]]]).astype(numpy.float32) diff --git a/mlprodict/onnxrt/ops_cpu/_op_list.py b/mlprodict/onnxrt/ops_cpu/_op_list.py index 628ebb734..40b9412b5 100644 --- a/mlprodict/onnxrt/ops_cpu/_op_list.py +++ b/mlprodict/onnxrt/ops_cpu/_op_list.py @@ -11,6 +11,7 @@ from .op_argmax import ArgMax from .op_argmin import ArgMin from .op_array_feature_extractor import ArrayFeatureExtractor +from .op_atan import Atan from .op_batch_normalization import BatchNormalization from .op_binarizer import Binarizer from .op_cast import Cast diff --git a/mlprodict/onnxrt/ops_cpu/op_atan.py b/mlprodict/onnxrt/ops_cpu/op_atan.py new file mode 100644 index 000000000..430e4a49f --- /dev/null +++ b/mlprodict/onnxrt/ops_cpu/op_atan.py @@ -0,0 +1,26 @@ +# -*- encoding: utf-8 -*- +# pylint: disable=E0203,E1101,C0111 +""" +@file +@brief Runtime operator. +""" +import numpy +from ._op import OpRunUnaryNum + + +class Atan(OpRunUnaryNum): + + def __init__(self, onnx_node, desc=None, **options): + OpRunUnaryNum.__init__(self, onnx_node, desc=desc, + **options) + + def _run(self, x): # pylint: disable=W0221 + if self.inplaces.get(0, False): + return self._run_inplace(x) + return (numpy.arctan(x), ) + + def _run_inplace(self, x): + return (numpy.arctan(x, out=x), ) + + def to_python(self, inputs): + return self._to_python_numpy(inputs, 'arctan') From 09fb8f9cd63d361643ca03fe6136eddbebfca80b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?xavier=20dupr=C3=A9?= Date: Wed, 22 Jul 2020 16:05:42 +0200 Subject: [PATCH 2/3] add unit test to test implementation of vectorial atan2 + renaming variable in python_compiled --- .../ut_onnxrt/test_onnxrt_python_runtime_.py | 22 +++++++++++++++++++ mlprodict/onnxrt/onnx_inference.py | 19 ++++++++++++---- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/_unittests/ut_onnxrt/test_onnxrt_python_runtime_.py b/_unittests/ut_onnxrt/test_onnxrt_python_runtime_.py index e967c7c00..7305e83b2 100644 --- a/_unittests/ut_onnxrt/test_onnxrt_python_runtime_.py +++ b/_unittests/ut_onnxrt/test_onnxrt_python_runtime_.py @@ -399,6 +399,28 @@ def test_onnxt_runtime_argmin_12(self): def test_onnxt_runtime_atan(self): self.common_test_onnxt_runtime_unary(OnnxAtan, numpy.arctan) + def test_onnxt_runtime_atan2(self): + test_pairs = [[y, x] for x in [3., -4., 0.] for y in [5., -6., 0.]] + y_val = numpy.array([y for y, x in test_pairs], dtype=numpy.float32) + x_val = numpy.array([x for y, x in test_pairs], dtype=numpy.float32) + + def atan2(y, x): + """ + sx = sign(x) + sy = sign(y) + pi_part = ((sy^2 - 1 - sy) * (sx - 1)) * (-pi / 2) + atan_part = atan(y/x) * sx^2 + result = pi_part + atan_part + """ + sx = numpy.sign(x) + sy = numpy.sign(y) + pi_part = (sy + sx * (sy ** 2 - 1)) * (sx - 1) * (-numpy.pi / 2) + atan_part = numpy.arctan(y / (x + (1 - sx ** 2))) * sx ** 2 + return atan_part + pi_part + + self.assertEqualArray( + numpy.arctan2(y_val, x_val), atan2(y_val, x_val)) + def test_onnxt_runtime_batch_normalization(self): # input size: (1, 2, 1, 3) x = numpy.array([[[[-1, 0, 1]], [[2, 3, 4]]]]).astype(numpy.float32) diff --git a/mlprodict/onnxrt/onnx_inference.py b/mlprodict/onnxrt/onnx_inference.py index e4a6d9460..54892bdef 100644 --- a/mlprodict/onnxrt/onnx_inference.py +++ b/mlprodict/onnxrt/onnx_inference.py @@ -1007,6 +1007,9 @@ def _build_compile_run(self): # to the onnx graph print(oinf2) """ + def clean_name(name): + return name.replace(":", "_").replace('.', '_') + # inits code = ['def compiled_run(dict_inputs):'] context = {} @@ -1018,25 +1021,33 @@ def _build_compile_run(self): inputs = self.input_names code.append(" # inputs") for inp in inputs: - code.append(" {0} = dict_inputs['{0}']".format(inp)) + code.append(" {0} = dict_inputs['{1}']".format( + clean_name(inp), inp)) # code for i, node in enumerate(self.sequence_): name = "n{}_{}".format(i, node.ops_.__class__.__name__.lower()) context[name] = node.ops_._run code.append(' ({1}, ) = {2}({0})'.format( - ', '.join(node.inputs), ', '.join(node.outputs), name)) + ', '.join(map(clean_name, node.inputs)), + ', '.join(map(clean_name, node.outputs)), + name)) # return code.append(' return {') for out in self.output_names: - code.append(" '{0}': {0},".format(out)) + code.append(" '{1}': {0},".format( + clean_name(out), out)) code.append(' }') final_code = '\n'.join(code) # compile the outcome context['self'] = self - obj = compile(final_code, "", 'exec') + try: + obj = compile(final_code, "", 'exec') + except SyntaxError as e: + raise SyntaxError( + "Unable to compile\n#####\n{}".format(final_code)) from e fcts_obj = [_ for _ in obj.co_consts if _ is not None and not isinstance(_, (bool, str, int))] fct = make_callable("compiled_run", fcts_obj[0], final_code, context) From 7154d9827500c62ddde53669842aafca56988465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?xavier=20dupr=C3=A9?= Date: Wed, 22 Jul 2020 16:15:04 +0200 Subject: [PATCH 3/3] documentation --- _unittests/ut_onnxrt/test_onnxrt_python_runtime_.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/_unittests/ut_onnxrt/test_onnxrt_python_runtime_.py b/_unittests/ut_onnxrt/test_onnxrt_python_runtime_.py index 7305e83b2..84551bebe 100644 --- a/_unittests/ut_onnxrt/test_onnxrt_python_runtime_.py +++ b/_unittests/ut_onnxrt/test_onnxrt_python_runtime_.py @@ -405,13 +405,10 @@ def test_onnxt_runtime_atan2(self): x_val = numpy.array([x for y, x in test_pairs], dtype=numpy.float32) def atan2(y, x): - """ - sx = sign(x) - sy = sign(y) - pi_part = ((sy^2 - 1 - sy) * (sx - 1)) * (-pi / 2) - atan_part = atan(y/x) * sx^2 - result = pi_part + atan_part - """ + # size: 100000 + # timeit arctan: 0.00205 + # timeit arctan2: 0.00361 + # timeit atan2: 0.00599 sx = numpy.sign(x) sy = numpy.sign(y) pi_part = (sy + sx * (sy ** 2 - 1)) * (sx - 1) * (-numpy.pi / 2)