Search code examples
pythonnumba

numba :cannot determine Numba type of <class 'function'> python


Here is my code.

from numba import jit
import numpy as np
import time

from pandas.core.common import flatten
from numba import njit
b_wi=[[1,2,3,4],[6,7,8,9,10,11]] #b_wi is a subset of x
f_wi=[[5,4,2,7,9],[5,4,3,7,2,3,4]]

# @jit(nopython=True)
def stopF_w(x,b_wi,f,di):
    if di=='left':
        return f[np.searchsorted(-b_wi,-x,side='left')]
    if di=='right':
        return f[np.searchsorted( b_wi, x,side='right')]
    # this f :need to be an element in f_nslst
    #b_wi :need to be an element in b_wilst

@njit(parallel=True)
def averageF_w(x,b_wilst,f_nslst,di):
    a=np.zeros(x.shape[0])
    for b_wi,f in zip(b_wilst,f_nslst):
        a[:]  += stopF_w(x,np.asarray(b_wi),np.asarray(f),di)
    return a

intval= np.unique(list(flatten(b_wi)))
x=np.concatenate(([-10000],(intval[:-1]+intval[1:])/2,[10000]))  #b_wi is a subset of x. That is why I can use this.
averageF_w(x,b_wi,f_wi,'right')

It raises error: TypingError: cannot determine Numba type of <class 'function'>. Does anyone know how to fix it please? Thank you very much.


Solution

  • There are several problems to handle: first, you've commented out the @jit decorator of your first function, stopF_w.

    If you uncomment it, you'll resolve your current error. Unfortunately, you will immediately run into several other errors. if your numba version is up to date, you'll see an error pertaining to "reflected lists".

    Basically, your inputs b_wi and f_wi are lists of variable length lists, which cannot be converted into uniform numpy arrays. E.g.: if instead of [[1,2,3,4],[6,7,8,9,10,11]], if b_wi was something like [[1,2,3, 4, 6], [7, 8, 9, 10, 11]] (easily convertible to an array of shape (2, 5) then it would work without any problems. To get your variable length lists to work with numba, you need to rely on a Typed List, which is a bit cumbersome.

    from numba import jit
    import numpy as np
    import time
    
    from pandas.core.common import flatten
    from numba import njit
    
    from numba.typed import List
    
    b_wi=[[1,2,3,4], [6,7,8,9,10,11]]
    f_wi=[[5,4,2,7,9], [5,4,3,7,2,3,4]]
    
    ###########################
    # Create typed Lists
    ###########################
    b_wi_nb = List()
    for i in range(len(b_wi)):
        b = List()
        for j in range(len(b_wi[i])):
            b.append(b_wi[i][j])
        b_wi_nb.append(b)
    
    f_wi_nb = List()
    for i in range(len(f_wi)):
        f = List()
        for j in range(len(f_wi[i])):
            f.append(f_wi[i][j])
        f_wi_nb.append(f)
    

    We will use b_wi_nb and f_wi_nb as inputs later on.

    Another problem: the function stopF_w has two "if" blocks. If neither condition is met, is may return None, which is unacceptable to numba for your use case. So alongside uncommenting the jit decorator, you also need to change the condition to if-else, or if-elif-else as applicable.

    @jit(nopython=True)
    def stopF_w(x,b_wi,f,di):
        if di=='left':
            return f[np.searchsorted(-b_wi,-x,side='left')]
        else:
            return f[np.searchsorted( b_wi, x,side='right')]
    

    After the above changes, numba should work.

    @jit(nopython=True, parallel=True)
    def averageF_w(x, b_wilst, f_nslst, di):
        a = np.zeros(x.shape[0])
        for b_wi, f in zip(b_wilst,f_nslst):
            a  += stopF_w(x, np.asarray(b_wi), np.asarray(f), di)
        return a
    
    intval= np.unique(list(flatten(b_wi)))
    x=np.concatenate(([-10000],(intval[:-1]+intval[1:])/2,[10000]))
    
    ##############################
    # initial compiles
    ##############################
    stopF_w(np.arange(1),np.arange(1),np.arange(1),'right')
    averageF_w(np.arange(10),np.arange(6).reshape(2, 3),np.arange(6).reshape(2, 3),'right')
    ##############################
    
    
    
    averageF_w(x, b_wi_nb, f_wi_nb,'right')