numba: The tuple built-in is not supported in nopython mode
The tuple([iterable]) python built-in function doesn’t work in nopython mode. Is there any plan to support this in nopython mode?
This would be very helpful for some functions like numpy.ndindex(*shape) which in numba must be called with a tuple. In addition in nopython mode generating a tuple by tuple addition (concatenation) in a for loop does not work.
Minimal function for use of tuple:
@jit(nopython=True)
def g():
tuple()
Resulting error:
Untyped global name 'tuple': cannot determine Numba type of <class 'type'>
Trying to generate a tuple using a for loop:
@jit(nopython=True)
def foo():
t = ()
for i in range(-10, 11):
t += (i,)
return t
Resulting error:
cannot unify () and (int64 x 1) for 't'
Also after reading through the documentation (section 2.6) I had expected that this would work. Here are the relevant parts of that documentation:
2.6. Supported Python features¶
Apart from the Language part below, which applies to both object mode and nopython mode, this page only lists the features supported in nopython mode.
2.6.2.3. tuple
The following operations are supported:
tuple construction tuple unpacking comparison between tuples iteration and indexing over homogenous tuples addition (concatenation) between tuples slicing tuples with a constant slice
About this issue
- Original URL
- State: open
- Created 6 years ago
- Reactions: 9
- Comments: 29 (7 by maintainers)
Your statement surprised me because I had tested generating an empty list by replacing tuple with list in my first example which fails with a TypingError. It seems numba doesn’t know what type list to generate and in reality there is no sane default option. If you do pass a list or tuple (with all elements of compatible types? It seems int and float work together, but not string and int or string and float.) to the list built-in then yes it does work; however, this doesn’t work for tuple.
For nopython mode the current answer is yes. A lot of the numpy functions require a shape parameter (e.g. numpy.zeros, numpy.ones, numpy.reshape) for which currently a tuple must be passed. Since there is no way to generate the necessary shape tuple in nopython mode curretly it has to be passed into the jitted function as a parameter. Here is an example:
give the following error
This is perhaps not a big deal, but as a new user of numba and a fundamentally python user (I’m not used to using other languages) it is quite jarring to run into many situations where the pythonic way of programming has failed when attempting to use numba in nopython mode to gain the best speedup.
My second example was an attempt to get around the lack of the tuple built-in. I don’t see a way of generating an arbitrary length tuple by using another variable because it seems that numba tries to infer the type and length of the declared tuple and cannot dynamically change it. So for example if I use another variable:
it’ll still fail with a TypingError.
I know this is an old issue, but I simply wanted to point out that since dynamic tuple creation is not supported in nopython mode, it limits what functions we can use. For instance, in the following example I’m trying to implement a numba compatible
np.squeezemethod:This fails because the only signature available for reshape is one where the shape argument is an int or tuple of ints. Is there a workaround I’m not thinking of? Here clearly the size of the shape parameter
dimsis not known, but its size is upper-bounded easily as it will never be longer thanarr.shape. Can this not be done statically at compile time?Is there a workaround if the length of a tuple is known and fixed? Does PR #5169 help?
Since we can already concatenate tuples and create them from literals, I wrote a function that creates a tuple creator dispatcher, given you know the length of the tuple at compile time:
Here are some examples of it solving some of the use cases in @krrk’s post above:
@ziofil I might have found an “unsafe” solution in the case in which the iterable to convert is an ndarray and with
numbaat version 0.52.0. The idea being to useto_fixed_tuplewhich can be found innumba.np.unsafe.ndarray. Although it is unsafe, it seems to work. Note that into_fixed_tuple(arr, length), length must be a constant. If it is not, but there is only a small set of possibilities for what this length might be, I found a solution which seems to work for me. It usesfunctools.lru_cacheto cache one (compiled) version of your njitted function making use ofto_fixed_tuple, per possible tuple length. As an example, here is a way to convert a 2D ndarrayXto a dictionary where each key is the tuple version of a row ofX(and values are all NaN):You can try it with e.g.
On my machine at least,
to_dictwill only njit the first time around and a compiled cached version will be used from then on. So if I doI get compilation overhead in the first two blocks but not the third or fourth.
Wondering what the experts think!
@ehsantn I agree with @Rik-de-Kort about the type inferencing. It seems that numba can determine the type and length of lists in nopython mode and if that is known then for at least those cases it should be easy to create a tuple from a list.
For example in the function below numba can create the list and infers that it is of type float64. It also knows its length.
Perhaps I am missing something, but it seems that numba could use the type and length of the list to create a tuple say if I called
tuple(l)in the above function?