Search code examples
pythonnumba

'Bad token in signature' with numba & @guvectorize


I am trying decorator @guvectorize with a function that outputs a [m,2] array. (m is depending the size of the input)

When using following function signature, I get an error.

import numpy as np
from numba import guvectorize

@guvectorize('void(float64[:], float64, uint32[:,2])', '(m),()->(m,2)', nopython=True)
def array_copy(data, delta, result):
    for i, val in np.ndenumerate(data):
        # Open i.
        i, = i
        result[i,:] = [i, int(val)+delta]

data = np.arange(3, dtype='float64')
res = np.zeros((data.shape[0], 2), dtype='uint32')
array_copy(data, 3, res)

This function is only here to support the example. Here is the error message.

Traceback (most recent call last):

  File "<ipython-input-17-63f8983bbf61>", line 5, in <module>
    def array_copy(data, delta, result):

  File "/home/pierre/anaconda3/lib/python3.8/site-packages/numba/np/ufunc/decorators.py", line 177, in wrap
    guvec = GUVectorize(func, signature, **kws)

  File "/home/pierre/anaconda3/lib/python3.8/site-packages/numba/np/ufunc/decorators.py", line 49, in __new__
    return imp(func, signature, identity=identity, cache=cache,

  File "/home/pierre/anaconda3/lib/python3.8/site-packages/numba/np/ufunc/ufuncbuilder.py", line 298, in __init__
    self.sin, self.sout = parse_signature(signature)

  File "/home/pierre/anaconda3/lib/python3.8/site-packages/numba/np/ufunc/sigparse.py", line 49, in parse_signature
    outputs = list(parse(outs))

  File "/home/pierre/anaconda3/lib/python3.8/site-packages/numba/np/ufunc/sigparse.py", line 35, in parse
    raise ValueError('bad token in signature "%s"' % tok[1])

ValueError: bad token in signature "2"

Please, what is wrong? How may I specify that result will have 2 columns?


Solution

  • Current implementation of Numba doesn't implement all the features defined in Numpy's GUFunc signatures, particularly fixed dimensions.

    The trick is to pass the result array twice, so the dimensions of the result are inferred from that parameter.

    The following example vectorizes a function that calculates the sum and product of each row vector of 3 elements:

    import numpy as np
    from numba import guvectorize
    
    @guvectorize('void(float64[:], float64[:], float64[:])',
                 '(m),(n)->(n)',
                 nopython=True)
    def sum_and_product(data, dummy, result):
        result[0] = data[0] + data[1] + data[2]
        result[1] = data[0] * data[1] * data[2]
    

    Then:

    data = np.array([(1, 2, 3), (4, 5, 6), (7, 8, 9)], dtype=float)
    data
    array([[1., 2., 3.],
           [4., 5., 6.],
           [7., 8., 9.]])
    res = np.empty((3, 2), dtype=float)
    res = sum_and_product(data, res)
    res
    array([[  6.,   6.],
           [ 15., 120.],
           [ 24., 504.]])
    

    Notice that the signature specifies the dimensions of a single vector and, consequently, the code in the function operates on a single vector.