-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow users to define custom atomic operators #235
Comments
some progress: the whole trick is to make the custom functions available when file class EvalContext:
def __init__(self, subclass_locals=None):
self.funcs = {}
for key in subclass_locals:
if callable(subclass_locals[key]):
self.funcs[key] = subclass_locals[key]
def __enter__(self):
return self.funcs
def __exit__(self, type, value, traceback):
pass
def myeval(expr, context=EvalContext):
func_str = f"""\
def _f(x):
return {expr}
"""
with context() as c:
exec(func_str, c, locals())
return locals()["_f"] file from myeval import myeval, EvalContext
class MyContext(EvalContext):
def __init__(self):
def myadd(x):
return x[0] + x[1]
super().__init__(locals())
if __name__ == '__main__':
expr = "x[0] * myadd(x)"
f = myeval(expr, MyContext)
x = [3, 7]
assert f(x) == x[0] * (x[0] + x[1]) |
ok, i think i've found an implementation that allows for a nice API: we use a decorator to add user-defined atomic operations to a global (module-local) dict that is used during file CUSTOM_ATOMIC_OPERATIONS = {}
def atomic_operation(f):
CUSTOM_ATOMIC_OPERATIONS[f.__name__] = f
return f
def myeval(expr):
func_str = f"""\
def _f(x):
return {expr}
"""
exec(func_str, CUSTOM_ATOMIC_OPERATIONS, locals())
return locals()["_f"] file import numpy as np
from myeval_decorator import myeval, atomic_operation
@atomic_operation
def myadd(x):
return x[0] + x[1]
@atomic_operation
def mynumpyadd(x):
return np.sum(x)
if __name__ == '__main__':
expr = "x[0] * mynumpyadd(x)"
f = myeval(expr)
x = [3, 7]
assert f(x) == x[0] * (x[0] + x[1]) @mschmidt87 @HenrikMettler what do you think? |
Wow this fancy stuff! 😍 |
partly. the general idea is that one can use arbitrary functions in the definition of node operations, see the start of this issue. however, the compilation will still have to be made for one specific target we support, i.e., python function, numpy-compatible, torch-compatible, or sympy compatible. does that answer your question? if not could you provide a code sample of what you have in mind? |
Ah yes I understand, thanks. There is much more to library specifics in the compilation calls, than in the simple |
cartesian_graph.py
As discussed in #234, currently the compilation of computational graphs, for example to a numpy-compatible function, relies on the presence of the necessary imports (i.e., numpy) incartesian_graph.py
. We should try to avoid such dependency.Users should be able to define custom atomic operators.
One possibility is to allow the user to set up a "compilation context" in which
eval
is called for the respective compilation targets. This could maybe also enable users to make use of custom functions inOperatorNode
, e.g.,:The text was updated successfully, but these errors were encountered: