You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As discussed on gitter, I found a bug in list comprehension when passing a tuple as input:
@numba.njit
def f1_fine(array_tuple):
vals = []
for a in array_tuple:
vals.append(a[0])
return np.array(vals)
@numba.njit
def f2_fine(array_tuple):
return np.array([array_tuple[i][0] for i in range(len(array_tuple))])
@numba.njit
def f3_crash(array_tuple):
return np.array([a[0] for a in array_tuple])
at = (np.ones(4), np.ones(3))
f1_fine(at)
f2_fine(at)
f3_crash(at)
While these functions are pretty much the same, f3 does not compile with error
TypingError: Failed in nopython mode pipeline (step: nopython frontend)
No implementation of function Function(<intrinsic range_iter_len>) found for signature:
>>> <unknown function>(iter(UniTuple(array(float64, 1d, C) x 2)))
There are 2 candidate implementations:
- Of which 2 did not match due to:
Intrinsic of function '<unknown function>': File: numba/cpython/rangeobj.py: Line 183.
With argument(s): '(iter(UniTuple(array(float64, 1d, C) x 2)))':
No match.
During: resolving callee type: Function(<intrinsic range_iter_len>)
During: typing of call at <ipython-input-20-4047ac44d0b7> (15)
File "<ipython-input-20-4047ac44d0b7>", line 15:
def f3_crash(array_tuple):
return np.array([a[0] for a in array_tuple])
^
The function _find_iter_range raises GuardException when it does not find a range iterator. This exception is turned into None by guard (as expected). However, the following if statements uses that info for different things and essentially assumes that there was a range_iter and only differentiates between range iter with and without step (as far as I can tell). A fix for this specific situation is to insert require(range_def is not None), but I am not sure if that works in general.
The function _find_iter_range raises GuardException when it does not find a range iterator. This exception is turned into None by guard (as expected). However, the following if statements uses that info for different things and essentially assumes that there was a range_iter and only differentiates between range iter with and without step (as far as I can tell). A fix for this specific situation is to insert require(range_def is not None), but I am not sure if that works in general.
Thanks for debugging this @MegaIng, the fix you describe will prevent this problem from occurring and I think given the nature of what the optimisation subsequently does it is an appropriate way to fix it. A PR with unit test based on the bug added to:
Apparently the function range_iter_len is just misnamed. It is supposed to handle all possible things one might throw at an array comprehension, not just range iterators. So currently the correct fix is to tell range_iter_len how to deal with tuple iterators (which is what is suggested as solution in #6550). However, that means that for every iterator type there might be the function has to be implemented.
Alternatively, is there a way to check if a function is implemented for the given parameters? We could check that there, and if no implementation is found reject the optimization.
While writing the tests right now, I noticed that the most recent master apparently transforms f1_fine in such a way that it now also breaks.
Thanks. On which version of Numba does it work? I can't reproduce it working on 0.53.1:
$ cat issue7394.py
from numba import njit, version_info
import numpy as np
print(version_info)
@njit
def f1_fine(array_tuple):
vals = []
for a in array_tuple:
vals.append(a[0])
return np.array(vals)
at = (np.ones(4), np.ones(3))
f1_fine(at)
$ python issue7394.py
version_info(major=0, minor=53, patch=1, short=(0, 53), full=(0, 53, 1), string='0.53.1', tuple=('0', '53', '1'), git_revision=None)
Traceback (most recent call last):
File "issue7394.py", line 15, in <module>
f1_fine(at)
File "<path>/numba/core/dispatcher.py", line 420, in _compile_for_args
error_rewrite(e, 'typing')
File "<path>/numba/core/dispatcher.py", line 361, in error_rewrite
raise e.with_traceback(None)
numba.core.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend)
No implementation of function Function(<intrinsic range_iter_len>) found for signature:
>>> <unknown function>(iter(UniTuple(array(float64, 1d, C) x 2)))
There are 2 candidate implementations:
- Of which 2 did not match due to:
Intrinsic of function '<unknown function>': File: numba/cpython/rangeobj.py: Line 183.
With argument(s): '(iter(UniTuple(array(float64, 1d, C) x 2)))':
No match.
During: resolving callee type: Function(<intrinsic range_iter_len>)
During: typing of call at issue7394.py (8)
File "issue7394.py", line 8:
def f1_fine(array_tuple):
vals = []
@stuartarchibald I have two different environments: One for numba development, and another where I have numba installed from pip. Both are Python version 3.9.4, but on the one with numba master f1_fine doesn't work, on the other it does.
RE failing tests, thanks for trying this out. I've taken a bit more of a look at the code and agree that the original suggestion will not work for the reasons you noted here: #7394 (comment), i.e. if there's no range then range_iter_len is used and it needs to work out the length of the iterator.
I think the issue here is that as the array comprehension transform is untyped it's not possible to know prior to doing the transform whether range_iter_len actually supports computing a length for the type of the iterator.
Two things probably need doing:
Write code to handle the tuple case.
Raise a TypingError if none of the types are matched in range_iter_len with a message spelling out the problem.
Untested patch for the above items:
diff --git a/numba/cpython/rangeobj.py b/numba/cpython/rangeobj.py
index 0f61a4f..8a26cbe 100644
--- a/numba/cpython/rangeobj.py+++ b/numba/cpython/rangeobj.py@@ -7,7 +7,7 @@ import operator
import llvmlite.llvmpy.core as lc
from numba import prange
-from numba.core import types, cgutils+from numba.core import types, cgutils, errors
from numba.cpython.listobj import ListIterInstance
from numba.np.arrayobj import make_array
from numba.core.imputils import (lower_builtin, lower_cast,
@@ -213,7 +213,18 @@ def range_iter_len(typingctx, val):
# array iterates along the outer dimension
return impl_ret_untracked(context, builder, intp_t, shape[0])
return signature(types.intp, val), codegen
-+ elif isinstance(val, types.UniTupleIter):+ def codegen(context, builder, sig, args):+ (iterty,) = sig.args+ tuplety = iterty.container+ intp_t = context.get_value_type(types.intp)+ count_const = intp_t(tuplety.count)+ return impl_ret_untracked(context, builder, intp_t, count_const)+ return signature(types.intp, val), codegen+ else:+ msg = ('Unsupported iterator found in array comprehension, try '+ 'preallocating the array and filling manually.')+ raise errors.TypingError(msg)
I came to the same conclusion. I also think that range_iter_len should be renamed (I choose comprehension_iter_len). It probably should also be moved to another module, including the tests, but for the moment I am just going to leave it there.
I came to the same conclusion. I also think that range_iter_len should be renamed (I choose comprehension_iter_len). It probably should also be moved to another module, including the tests, but for the moment I am just going to leave it there.
From what I can work out, this function is an intrinsic which when called on an iterable, returns the length of the iterable. I think it's constructing something like:
x = <some iterable>
count = range_iter_len(x)
... do something with count
Activity
MegaIng commentedon Sep 9, 2021
Seems to be related to #6550
I also poked around a bit and found what I believe to be the source of the error:
numba/numba/core/inline_closurecall.py
Line 1044 in e6c4bd1
The function
_find_iter_rangeraisesGuardExceptionwhen it does not find arangeiterator. This exception is turned intoNonebyguard(as expected). However, the followingifstatements uses that info for different things and essentially assumes that there was arange_iterand only differentiates between range iter with and without step (as far as I can tell). A fix for this specific situation is to insertrequire(range_def is not None), but I am not sure if that works in general.[-]iteration fails in list comprehension[/-][+]iteration fails in tuple comprehension[/+]stuartarchibald commentedon Sep 10, 2021
Thanks for debugging this @MegaIng, the fix you describe will prevent this problem from occurring and I think given the nature of what the optimisation subsequently does it is an appropriate way to fix it. A PR with unit test based on the bug added to:
numba/numba/tests/test_comprehension.py
Line 241 in e6c4bd1
MegaIng commentedon Sep 10, 2021
While writing the tests right now, I noticed that the most recent master apparently transforms
f1_finein such a way that it now also breaks.MegaIng commentedon Sep 10, 2021
The fix does not work. It breaks a few tests in the same class:
numba/numba/tests/test_comprehension.py
Line 401 in e6c4bd1
numba/numba/tests/test_comprehension.py
Line 274 in e6c4bd1
numba/numba/tests/test_comprehension.py
Line 342 in e6c4bd1
Apparently the function
range_iter_lenis just misnamed. It is supposed to handle all possible things one might throw at an array comprehension, not just range iterators. So currently the correct fix is to tellrange_iter_lenhow to deal with tuple iterators (which is what is suggested as solution in #6550). However, that means that for every iterator type there might be the function has to be implemented.Alternatively, is there a way to check if a function is implemented for the given parameters? We could check that there, and if no implementation is found reject the optimization.
stuartarchibald commentedon Sep 10, 2021
Thanks. On which version of Numba does it work? I can't reproduce it working on 0.53.1:
MegaIng commentedon Sep 10, 2021
@stuartarchibald For me it works on 0.54.0 and on 0.53.1
Python version differences are probably the problem.
stuartarchibald commentedon Sep 10, 2021
Yes, are you on 3.7? I tested this on 3.9.
MegaIng commentedon Sep 10, 2021
@stuartarchibald I have two different environments: One for numba development, and another where I have numba installed from pip. Both are Python version 3.9.4, but on the one with numba master
f1_finedoesn't work, on the other it does.MegaIng commentedon Sep 10, 2021
Ok, no worries, I am stupid. My one version of
f1_finedid not have anumba.jitdecorator. It fails everywhere.ogauthe commentedon Sep 10, 2021
I run
f1_finewith python3.7.11, numpy1.20.3and numba0.53.1+8.gddf817af0.dirty, it works. So yes probably python version.stuartarchibald commentedon Sep 10, 2021
RE failing tests, thanks for trying this out. I've taken a bit more of a look at the code and agree that the original suggestion will not work for the reasons you noted here: #7394 (comment), i.e. if there's no
rangethenrange_iter_lenis used and it needs to work out the length of the iterator.I think the issue here is that as the array comprehension transform is untyped it's not possible to know prior to doing the transform whether
range_iter_lenactually supports computing a length for the type of the iterator.Two things probably need doing:
TypingErrorif none of the types are matched inrange_iter_lenwith a message spelling out the problem.Untested patch for the above items:
MegaIng commentedon Sep 10, 2021
I came to the same conclusion. I also think that
range_iter_lenshould be renamed (I choosecomprehension_iter_len). It probably should also be moved to another module, including the tests, but for the moment I am just going to leave it there.Fix numba#7394 and numba#6550 & Added test & improved error message
stuartarchibald commentedon Sep 10, 2021
From what I can work out, this function is an intrinsic which when called on an iterable, returns the length of the iterable. I think it's constructing something like:
maybe
length_of_iterablewould be a good fit?Merge pull request #7395 from MegaIng/comprehension_iter