Numba does not recognize numpy.maximum.accumulate(), how to revise the following code?

Viewed 596

Error message: TypingError: Failed in nopython mode pipeline (step: nopython frontend) Unknown attribute 'accumulate' of type Function().

How to revise the following code? Thank you.

import numba
import numpy as np


@numba.jit(nopython=True)
def maxdd(x):
    temp = np.maximum.accumulate(x) - x
    ide = len(x) - np.argmax(temp[::-1]) - 1
    ids = np.argmax(x[:ide])
    mdd = x[ide] - x[ids]
    ide += 1
    return mdd, ids, ide
2 Answers

I have tried using vectorized ufunc to calculate running maximum in this way:

@numba.vectorize(["int32(int32,int32)","int64(int64,int64)","float32(float32,float32)"])
@numba.njit()
def nmax(x, y):
    if x>y:
        return x
    else:
        return y
@numba.njit()
def test():
    a = np.arange(12)[::-1]
    print(a)
    return nmax.accumulate(a)
test()

However, in Numba 0.46.0 this throws error

Unknown attribute 'accumulate' of type Function(numba._DUFunc 'nmax')

as currently it seems that Numba can't work with vectorized functions in nopython mode (?), unfortunately, cause un-njiting test gives expected

[11 10 9 8 7 6 5 4 3 2 1 0]

array([11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11])

For now we have to manually code such function as

@numba.njit()
def running_max(x):
    rmax=x[0]
    y=np.empty_like(x)
    for i,val in enumerate(x):
        if val>rmax: rmax=val
        y[i]=rmax
    return y

I have raised an issue with Numba team.

I came across the same problem and wanted to elaborate on Anatoly's answer for how to get around this. As mentioned, .accumulate is not supported by numba. However, the numpy documentation on .accumulate provides a hint:

For a one-dimensional array, accumulate produces results equivalent to:

r = np.empty(len(A))
t = op.identity        # op = the ufunc being applied to A's  elements
for i in range(len(A)):
    t = op(t, A[i])
    r[i] = t
return r

So, let's first convert this to a proper function:

def numba_maximum_accumulate(A):
    r = np.empty(len(A))
    t = op.identity
    for i in range(len(A)):
        t = op(t, A[i])
        r[i] = t
    return r

Next, we swap out op for our desired operation np.maximum

def numba_maximum_accumulate(A):
    r = np.empty(len(A))
    t = np.maximum.identity
    for i in range(len(A)):
        t = mp.maximum(t, A[i])
        r[i] = t
    return r

However, we'll quickly learn that the identity of np.maximum is None and this is undesirable and, instead, we should seed this process by simply setting t = np.-inf (Note that in the case of np.minimum, you'll want to seed with t = np.inf instead).

def numba_maximum_accumulate(A):
    r = np.empty(len(A))
    t = -np.inf
    for i in range(len(A)):
        t = mp.maximum(t, A[i])
        r[i] = t
    return r

Finally, let's JIT our function:

from numba import njit

@njit()
def numba_maximum_accumulate(A):
    r = np.empty(len(A))
    t = -np.inf
    for i in range(len(A)):
        t = mp.maximum(t, A[i])
        r[i] = t
    return r

Now you should be able to call this function on your data.

Related