-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Feature Request: Function as first-class type #3405
Comments
Thanks for the report. I can reproduce. I think this is because the type information about the result of calling member function |
Note that this is not specific to jitclass: if you decorate the foo function with an |
You're right, thanks, I updated the example |
Thanks for the update, I think I see what's going on now. I thought that this was something more complicated to do with the jitclass internals but I believe in actual fact the issue is more simple. It is that when doing type inference Numba sees the passed in functions as unique types, at present it doesn't know about what the function itself returns, there is no "function type" type. As a result the type inference mechanism sees something equivalent in objectionableness to: @njit
def g(arg): # fails
if arg:
f = np.int32(a)
else:
f = np.array(10)
out = f()
return out Moving this to a feature request, functions as first-class types. |
hi, two questions:
thanks! |
It would enable #2542, I believe. As for roadmap, the soonest would be in the Medium Term bucket, probably post 1.0. |
thanks @seibert |
@sklam: What do you think is the level of effort to make a function type that matches on type signature? (rather than what we have now, where every function is its own type) |
I don't have a good estimate yet. But, I have included some notes on possible solutions to it. Let's use the following code as an example: def ex(f, g, choose, args0, args1):
if choose:
h = f # pos A
else:
h = g # pos B
x = h(*args0) # pos C
y = h(*args1) # pos D
return x, y Option 1. Continuing to allow FunctionType to reference the actual function objects.Thinking through type inference:
Thinking through lowering:
Notes:
Option 2. Make functions runtime objectsThinking through type inference:
(backing up....)
Whenever Notes:
|
I'll go out on a limb here, as this is so far from my area of expertise. what about a more limited version that requires the functions to be set in advance? I am thinking about sklam's chain in #2542 or this example, which is a modified version of that chain. @nb.njit
def ident(out, x):
pass
def chain_assign(fs, inner=ident):
head, tail = fs[-1], fs[:-1]
item = len(fs)-1
@nb.njit
def assign(out, x):
inner(out, x)
out[item] = head(x)
if tail:
return chain_assign(tail, assign)
else:
return assign In practice this defines an iterator over functions, but an immutable one. I guess this is what makes it work, avoiding the type-checking and lowering problems described above. def ex(func_tuple, choice_index, args0, args1):
x = func_tuple[choice_index](*args0) # pos C
y = func_tuple[choice_index](*args1) # pos D
return x, y Here's where my lack of experience with the internals of Numba stops me. I cannot reproduce the step by step analysis sklam's did. I hope the idea is not too stupid, though. |
I think function types need to carry the signature to have consistent typing and straight-forward implementation (similar to C). In this way, things like containers of functions as in #2542 will be easy as well. Following @sklam's example (assuming sig0 as typing template of
At lowering, the signature is known from function variable type, and the function pointer is taken from function variable value and called. Also, supporting more advanced cases like inlining specific cases can be done using literals, value-based specialization, etc. Does this make sense? |
@luk-f-a, there is still a difficulty in typing the code: def ex(func_tuple, choice_index, args0, args1):
x = func_tuple[choice_index](*args0) # pos C
y = func_tuple[choice_index](*args1) # pos D
return x, y It is difficulty to determine the function type from @ehsantn's suggestion will require explicit annotation from the user. For instance, sig = Signature(...)
f_sig = f.compile(sig)
g_sig = g.compile(sig)
exc(f_sig, g_sig, ...) In fact, we already have the |
@sklam, thanks, I guess I was confused by the following example:
This works well, despite in theory not being able to determine the function type from Would compile be explicitly necessary? or would it be enough to do @njit(sig_str)
def f
@njit(sig_str)
def g
exc(f, g,...) |
Explicit compile is probably not necessary and As for effort, I do not expect the initial changes would be big but there may be a lot of subtle points to make it useful. It'd worth to do a quick experimental patch to see how involved it would be. |
Heads up: with PR #4967, all the examples in this issue work provided the input functions are |
Looking forward to seeing this develop. I am finding the new |
I think having |
I am getting a type unification error that I can't logically explain:the same code works when I'm not inside a jitclass (seefoo()
vsC.foo()
)even inside the jitclass, the unification can work under some conditions (seeC.bar
)EDIT: as noticed by @ogrisel this isn't related to jitclass but more generally to the nopython mode. I updated the example and the log.
Minimal working example:
Error log:
The text was updated successfully, but these errors were encountered: