microsoft/onnxruntime-extensions
Publicmirrored from https://github.com/microsoft/onnxruntime-extensionsAvailable
ocos/pyfunc/_ocos.py
81lines · modecode
| 1 | # Copyright (c) Microsoft Corporation. All rights reserved. |
| 2 | # Licensed under the MIT License. See License.txt in the project root for |
| 3 | # license information. |
| 4 | ############################################################################### |
| 5 | |
| 6 | from pathlib import Path |
| 7 | from ._ortcustomops import PyCustomOpDef, add_custom_op |
| 8 | |
| 9 | |
| 10 | def get_library_path(): |
| 11 | pkg_dir = Path(__file__).parent |
| 12 | return str(pkg_dir / "_ortcustomops.pyd") |
| 13 | |
| 14 | |
| 15 | class Opdef: |
| 16 | |
| 17 | _odlist = {} |
| 18 | |
| 19 | def __init__(self, op_type, func): |
| 20 | self.op_type = op_type |
| 21 | self.body = func |
| 22 | self._id = id(self) |
| 23 | |
| 24 | @staticmethod |
| 25 | def declare(*args, **kwargs): |
| 26 | if len(args) > 0 and hasattr(args[0], '__call__'): |
| 27 | raise RuntimeError("Unexpected arguments {}.".format(args)) |
| 28 | # return Opdef._create(args[0]) |
| 29 | return lambda f: Opdef._create(f, *args, **kwargs) |
| 30 | |
| 31 | @staticmethod |
| 32 | def _create(func, *args, **kwargs): |
| 33 | name = kwargs.get('op_type', None) |
| 34 | op_type = name or func.__name__ |
| 35 | opdef = Opdef(op_type, func) |
| 36 | od_id = id(opdef) |
| 37 | |
| 38 | # Tells python this object cannot be destroyed |
| 39 | # because it is also stored in C++ container. |
| 40 | Opdef._odlist[od_id] = opdef |
| 41 | opdef._nativedef = PyCustomOpDef() |
| 42 | opdef._nativedef.op_type = op_type |
| 43 | opdef._nativedef.obj_id = od_id |
| 44 | |
| 45 | # TODO: add handle more types and multiple inputs/outputs. |
| 46 | # by default the op is single in/out |
| 47 | inputs = kwargs.get('inputs', None) |
| 48 | if inputs is None: |
| 49 | inputs = [PyCustomOpDef.dt_float] |
| 50 | opdef._nativedef.input_types = inputs |
| 51 | outputs = kwargs.get('outputs', None) |
| 52 | if outputs is None: |
| 53 | outputs = [PyCustomOpDef.dt_float] |
| 54 | opdef._nativedef.output_types = outputs |
| 55 | add_custom_op(opdef._nativedef) |
| 56 | return opdef |
| 57 | |
| 58 | def __call__(self, *args, **kwargs): |
| 59 | return self.body(*args, **kwargs) |
| 60 | |
| 61 | |
| 62 | def _on_pyop_invocation(k_id, feed): |
| 63 | if k_id not in Opdef._odlist: |
| 64 | raise RuntimeError( |
| 65 | "Unable to find function id={}. " |
| 66 | "Did you decorate the operator with @onnx_op?.".format(k_id)) |
| 67 | op_ = Opdef._odlist[k_id] |
| 68 | rv = op_.body(*feed) |
| 69 | if isinstance(rv, tuple): |
| 70 | # Multiple outputs. |
| 71 | res = [] |
| 72 | for r in rv: |
| 73 | res.append(r.shape) |
| 74 | res.append(r.flatten().tolist()) |
| 75 | res = tuple(res) |
| 76 | else: |
| 77 | res = (rv.shape, rv.flatten().tolist()) |
| 78 | return (k_id, ) + res |
| 79 | |
| 80 | |
| 81 | PyCustomOpDef.install_hooker(_on_pyop_invocation) |