Why is Numba failing to compile this function? - python

I am trying to compile the following function with Numba:
#njit(fastmath=True, nogil=True)
def generate_items(array, start):
array_positions = np.empty(SIZE, dtype=np.int64)
count = 0
while count < SIZE - start:
new_array = mutate(np.empty(0, dtype=np.uint8))
if len(new_array) > 0:
array_positions[count] = len(array) # <<=== FAILS HERE
array = np.append(array, np.append(new_array, 255))
count += 1
return array, array_positions
But it fails on the indicated line above with this error message:
numba.core.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Cannot unify array(uint8, 1d, C) and array(int64, 1d, C) for 'array.3', defined at ...
Which doesn't seem to make sense since I'm just assigning an int (the result on len) to an array that has a dtype of np.int64?
Note that array is of type np.uint8 - but I'm not assigning the array itself so this message makes no sense to me.
I attempted to refactor to this:
tmp = len(array) # <<=== FAILS HERE
array_positions[count] = tmp
But then it fails there... same message.
I also tried replacing len(array) by array.size since this is a 1d array, but same error.
Can anyone see why this is failing?
I'm on Python 3.7 and Numba 0.50
Thanks!

First issue is that you use an unsupported function mutate (as stated here).
Next, as the error says, you try to add arrays with different data types in
np.append(array, np.append(new_array, 255))
where the array is of type int64 and new_array holds values of type uint8. If you've used #jit it would have showed you a warning of falling to object mode, but because you've enforced the nonpython mode by using the #njit decorator, it throws an error.
Cheers.

Related

How to use numba?

I meet a problem about using numba jit decorator (#nb.jit)! Here is the warning from jupyter notebook,
NumbaWarning:
Compilation is falling back to object mode WITH looplifting enabled because Function "get_nb_freq" failed type inference due to: No implementation of function Function(<function dotenter image description here at 0x00000190AC399B80>) found for signature:
This is complete information:
Here is my code:
#numba.jit
def get_nb_freq( nb_count = None, onehot_ct = None):
# nb_freq = onehot_ct.T # nb_count
nb_freq = np.dot(onehot_ct.T, nb_count)
res = nb_freq/nb_freq.sum(axis = 1).reshape(Num_celltype,-1)
return res
## onehot_ct is array, and its shape is (921600,4)
## nb_count is array, and its shape is the same as onehot_ct
## Num_celltype equals 4
Based on your mentioned shapes we can create the arrays as:
onehot_ct = np.random.rand(921600, 4)
nb_count = np.random.rand(921600, 4)
Your prepared code will be work correctly and get an answer like:
[[0.25013102754197963 0.25021461207825463 0.2496806287276126 0.24997373165215303]
[0.2501574139037384 0.25018726649940737 0.24975108864220968 0.24990423095464467]
[0.25020550587624757 0.2501303498983212 0.24978335463279314 0.24988078959263807]
[0.2501855533482036 0.2500913419625523 0.24979681404573967 0.24992629064350436]]
So, it shows the code is working and the problem seems to be related to type of the arrays, that numba can not recognize them. So, signature may be helpful here, which by we can recognize the types manually for the function. So, based on the error I think the following signature will pass your issue:
#nb.jit("float64[:, ::1](float64[:, ::1], float32[:, ::1])")
def get_nb_freq( nb_count = None, onehot_ct = None):
nb_freq = np.dot(onehot_ct.T, nb_count)
res = nb_freq/nb_freq.sum(axis=1).reshape(4, -1)
return res
But it will stuck again if you test by get_nb_freq(nb_count.astype(np.float64), onehot_ct.astype(np.float32)), So another cause could be related to unequal types in np.dot. So, use the onehot_ct array as array type np.float64, could pass the issue:
#nb.jit("float64[:, ::1](float64[:, ::1], float32[:, ::1])")
def get_nb_freq( nb_count, onehot_ct):
nb_freq = np.dot(onehot_ct.astype(np.float64).T, nb_count)
res = nb_freq/nb_freq.sum(axis=1).reshape(4, -1)
return res
It ran on my machine with this correction. I recommend write numba equivalent codes (like this for np.dot) instead using np.dot or …, which can be much faster.

numba: overload of function

I'm trying out numba, the python package that is said to make my nparray super fast. I want to run my function in nonpython mode. What it essentially does is that it takes in an 20x20 array, assigns random numbers to each of its elements, calculate its inverse matrix, then return it.
But here's the problem, when I initialize the array result with np.zeros(), my script crashes and gives me an error message 'overload of function zeros'.
Could someone kindly tell me what is going on? Much appreciated.
from numba import njit
import time
import numpy as np
import random
arr = np.zeros((20,20),dtype = float)
#njit
def aFunctionWithNumba (incomingArray):
result = np.zeros(np.shape(incomingArray), dtype = float)
for i in range(len(incomingArray[0])):
for j in range(len(incomingArray[1])):
incomingArray[i,j] = random.randrange(105150,1541586)
result = np.linalg.inv(incomingArray)
return result
t0 = time.time()
fastArray = aFunctionWithNumba(arr)
t1 = time.time()
s1 = t1 - t0
Here's the full error message:
Exception has occurred: TypingError Failed in nopython mode pipeline (step: nopython frontend) No implementation of function Function(<built-in function zeros>) found for signature:
>>> zeros(UniTuple(int64 x 2), dtype=Function(<class 'float'>)) There are 2 candidate implementations:
- Of which 2 did not match due to: Overload of function 'zeros': File: numba\core\typing\npydecl.py: Line 511.
With argument(s): '(UniTuple(int64 x 2), dtype=Function(<class 'float'>))': No match.
During: resolving callee type: Function(<built-in function zeros>) During: typing of call at c:\Users\Eric\Desktop\testNumba.py (9)
File "testNumba.py", line 9: def aFunctionWithNumba (incomingArray):
result = np.zeros(np.shape(incomingArray), dtype = float)
^ File "C:\Users\Eric\Desktop\testNumba.py", line 25, in <module>
fastArray = aFunctionWithNumba(arr)
The error
You should use Numpy or Numba types inside JITted functions.
Changing the following line your code works:
result = np.zeros(np.shape(incomingArray), dtype=np.float64)
But your code will be more generic using:
result = np.zeros(incomingArray.shape, dtype=incomingArray.dtype)
Or, even better:
result = np.zeros_like(incomingArray)
The timing
The first time you call a JITted function it will take some time to compile it, much longer than the time it will take to execute it. So you should call the function with the same parameter types once before you make any timings.
Additional optimization
If you are interested in comparing the execution time of nested loops with or without Numba, your code is fine. Otherwise you can replace the loops with something like:
incomingArray[:] = np.random.random(incomingArray.shape) * (1541586 - 105150) + 105150

Numpy aggregate function shims, typing, and np.sort() in Numba

I'm working with Numba (0.44) and Numpy in nopython mode. Presently, Numba doesn't support Numpy aggregation functions across an arbitrary axis, it only supports computing these aggregates over a whole array. Given the situation, I decided to take a crack and creating some shims.
In code:
np.min(array) # This works with Numba 0.44
np.min(array, axis = 0) # This does not work with Numba 0.44 (no axis argument allowed)
Here's an example of a shim, designed to reproduce np.min(array):
import numpy as np
import numba
#numba.jit(nopython = True)
def npmin (X, axis = -1):
"""
Shim for broadcastable np.min().
Allows np.min(array), np.min(array, axis = 0), and np.min(array, axis = 1)
Note that the argument axis = -1 computes on the entire array.
"""
if axis == 0:
_min = np.sort(X.transpose())[:,0]
elif axis == 1:
_min = np.sort(X)[:,0]
else:
_min = np.sort(np.sort(X)[:,0])[0]
return _min
Without Numba, the shim works as expected and recapitulates the behavior of np.min() upto a 2D array. Note that I'm using axis = -1 as a means of allowing the summing of the entire array – similar behavior to invoking np.min(array) without an axis argument.
Unfortunately, once I throw Numba into the mix, i get an error. Here's the trace:
Traceback (most recent call last):
File "shims.py", line 81, in <module>
_min = npmin(a)
File "/usr/local/lib/python3.7/site-packages/numba/dispatcher.py", line 348, in _compile_for_args
error_rewrite(e, 'typing')
File "/usr/local/lib/python3.7/site-packages/numba/dispatcher.py", line 315, in error_rewrite
reraise(type(e), e, None)
File "/usr/local/lib/python3.7/site-packages/numba/six.py", line 658, in reraise
raise value.with_traceback(tb)
numba.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Invalid use of Function(<function sort at 0x10abd5ea0>) with argument(s) of type(s): (array(int64, 2d, F))
* parameterized
In definition 0:
All templates rejected
This error is usually caused by passing an argument of a type that is unsupported by the named function.
[1] During: resolving callee type: Function(<function sort at 0x10abd5ea0>)
[2] During: typing of call at shims.py (27)
File "shims.py", line 27:
def npmin (X, axis = -1):
<source elided>
if axis == 0:
_min = np.sort(X.transpose())[:,0]
^
This is not usually a problem with Numba itself but instead often caused by
the use of unsupported features or an issue in resolving types.
To see Python/NumPy features supported by the latest release of Numba visit:
http://numba.pydata.org/numba-doc/dev/reference/pysupported.html
and
http://numba.pydata.org/numba-doc/dev/reference/numpysupported.html
For more information about typing errors and how to debug them visit:
http://numba.pydata.org/numba-doc/latest/user/troubleshoot.html#my-code-doesn-t-compile
If you think your code should work with Numba, please report the error message
and traceback, along with a minimal reproducer at:
https://github.com/numba/numba/issues/new
I've verified that all the functions I'm using and their respective arguments are supported in Numba 0.44. Of course, the stack trace says the issue is with my call to np.sort(array), but I suspect this may be a typing issue because the function can return either a scalar (without axis argument) or 2D-array (with axis argument).
That said, I have a few of questions:
Is there an issue with my implementation; can anyone pinpoint an unsupported feature I'm using, as suggested by the stack trace?
Or rather, does this appear to be a bug with Numba?
More generally, are these kinds of shims presently possible with Numba (0.44)?
Here's an alternative shim for 2d arrays:
#numba.jit(nopython=True)
def npmin2(X, axis=0):
if axis == 0:
_min = np.empty(X.shape[1])
for i in range(X.shape[1]):
_min[i] = np.min(X[:,i])
elif axis == 1:
_min = np.empty(X.shape[0])
for i in range(X.shape[0]):
_min[i] = np.min(X[i,:])
return _min
although you'll have to figure out a workaround for the axis=-1 case, because that's going to return a scalar, and the other arguments will return arrays and Numba will not be able to "unify" the return type into something consistent.
The performance, on my machine at least, seems to be roughly comparable to just calling the equivalent np.min, with sometimes np.min being faster and other times npmin2 winning out, depending on the input array size and axis.

How to do indexing properly in numba.cuda?

I am writing a code which needs to do some indexing in python using numba.
However, I cannot do it correctly.
It seems something is prohibited.
The code is as follows:
from numba import cuda
import numpy as np
#cuda.jit
def function(output, size, random_array):
i_p, i_k1, i_k2 = cuda.grid(3)
if i_p<size and i_k1<size and i_k2<size:
a1=i_p**2+i_k1
a2=i_p**2+i_k2
a3=i_k1**2+i_k2**2
a=[a1,a2,a3]
for i in range(len(random_array)):
output[i_p,i_k1,i_k2,i] = a[int(random_array[i])]
output=cuda.device_array((10,10,10,5))
random_array=cuda.to_device(np.array([np.random.random()*3 for i in range(5)]))
size=10
threadsperblock = (8, 8, 8)
blockspergridx=(size + (threadsperblock[0] - 1)) // threadsperblock[0]
blockspergrid = ((blockspergridx, blockspergridx, blockspergridx))
# Start the kernel
function[blockspergrid, threadsperblock](output, size, random_array)
print(output.copy_to_host())
It yields an error:
LoweringError: Failed at nopython (nopython mode backend)
'CUDATargetContext' object has no attribute 'build_list'
File "<ipython-input-57-6058e2bfe8b9>", line 10
[1] During: lowering "$40.21 = build_list(items=[Var(a1, <ipython-input-57-6058e2bfe8b9> (7)), Var(a2, <ipython-input-57-6058e2bfe8b9> (8)), Var(a3, <ipython-input-57-6058e2bfe8b9> (9))])" at <ipython-input-57-6058e2bfe8b9> (10
Can anyone help me with this?
One choice is to feed a also as an input of the function, but when a is really large like some 1000*1000*1000*7 array, it always gives me out off memory.
The problem has nothing to do with array indexing. Within the kernel, this line:
a=[a1,a2,a3]
is not supported. You cannot create a list within a #cuda.jit function. The exact list of supported Python types within kernels is fully documented here.

Cython: How to declare numpy.argwhere()

I tried to Cythonize part of my code as following to hopefully gain some speed:
# cython: boundscheck=False
import numpy as np
cimport numpy as np
import time
cpdef object my_function(np.ndarray[np.double_t, ndim = 1] array_a,
np.ndarray[np.double_t, ndim = 1] array_b,
int n_rows,
int n_columns):
cdef double minimum_of_neighbours, difference, change
cdef int i
cdef np.ndarray[np.int_t, ndim =1] locations
locations = np.argwhere(array_a > 0)
for i in locations:
minimum_of_neighbours = min(array_a[i - n_columns], array_a[i+1], array_a[i + n_columns], array_a[i-1])
if array_a[i] - minimum_of_neighbours < 0:
difference = minimum_of_neighbours - array_a[i]
change = min(difference, array_a[i] / 5.)
array_a[i] += change
array_b[i] -= change * 5.
print time.time()
return array_a, array_b
I can compile it without an error but when I use the function I got this error:
from cythonized_code import my_function
import numpy as np
array_a = np.random.uniform(low=-100, high=100, size = 100).astype(np.double)
array_b = np.random.uniform(low=0, high=20, size = 100).astype(np.double)
a, b = my_function(array_a,array_b,5,20)
# which gives me this error:
# locations = np.argwhere(array_a > 0)
# ValueError: Buffer has wrong number of dimensions (expected 1, got 2)
Do I need to declare locations type here? The reason I wanted to declare it is that it has yellow colour in the annotated HTML file generated by compiling the code.
It's a good idea not to use the python-functionality locations[i], because it is too expensive: Python would create a full-fledged Python-integer* from the lowly c-integer (which is what is stored in the locations-numpy array), register it in the garbage collector, then cast it back to int, destroy the Python-object - quite an overhead.
To get a direct access to the lowly c-integers one needs to bind locations to a type. The normal course of action would be too look up, which properties locations has:
>>> locations.ndim
2
>>> locations.dtype
dtype('int64')
which translates to cdef np.ndarray[np.int64_t, ndim =2] locations.
However, this will (probably, can not check it right now) not be enough to get rid of Python-overhead because of a Cython-quirk:
for i in locations:
...
will not be interpreted as a raw-array access but will invoke the Python-machinery. See for example here.
So you will have to change it to:
for index in range(len(locations)):
i=locations[index][0]
then Cython "understands", that you want the access to the raw c-int64 array.
Actually, it is not completely true: In this case first an nd.array is created (e.g. locations[0] or locations[1]) and then __Pyx_PyInt_As_int (which is more or less an alias for [PyLong_AsLongAndOverflow][2]) is called, which creates a PyLongObject, from which C-int value is obtained before the temporary PyLongObject and nd.array are destructed.
Here we get lucky, because length-1 numpy-arrays can be converted to Python scalars. The code would not work if the second dimension of locations would be >1.

Categories