I am trying to implement Numba on a simple Monte Carlo integrator.
import numpy as np
import numba
import time
#numba.njit
def integrator(integrand,
lower_bounds,
upper_bounds,
n):
if len(lower_bounds) != len(upper_bounds):
raise ValueError("Lower and upper bounds are of different dimensions.")
x = np.random.uniform(lower_bounds,
upper_bounds,
(n, len(lower_bounds)))
summation = 0
for i in x:
summation += integrand(i)
domain = np.prod(np.subtract(upper_bounds, lower_bounds))
integral = domain/n*summation
return integral
start = time.time()
integrator(numba.njit(lambda x: x**2),
np.array([-5]),
np.array([5]),
10000000)
end = time.time()
print(end-start)
However, I am getting the following error:
>>> integrator(numba.njit(lambda x: x**2),
... np.array([-5]),
... np.array([5]),
... 10000000)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\numba\core\dispatcher.py", line 468, in _compile_for_args
error_rewrite(e, 'typing')
File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\numba\core\dispatcher.py", line 409, in error_rewrite
raise e.with_traceback(None)
numba.core.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend)
←[1m←[1m←[1mNo implementation of function Function(<built-in method uniform of numpy.random.mtrand.RandomState object at 0x0000019A9E477040>) found for signature:
>>> uniform(array(int32, 1d, C), array(int32, 1d, C), UniTuple(int64 x 2))
There are 4 candidate implementations:
←[1m - Of which 4 did not match due to:
Overload in function '_OverloadWrapper._build.<locals>.ol_generated': File: numba\core\overload_glue.py: Line 129.
With argument(s): '(array(int32, 1d, C), array(int32, 1d, C), UniTuple(int64 x 2))':←[0m
←[1m Rejected as the implementation raised a specific error:
TypingError: ←[1mNo match←[0m←[0m
raised from C:\Users\user\AppData\Local\Programs\Python\Python310\lib\site-packages\numba\core\overload_glue.py:162
←[0m
←[0m←[1mDuring: resolving callee type: Function(<built-in method uniform of numpy.random.mtrand.RandomState object at 0x0000019A9E477040>)←[0m
←[0m←[1mDuring: typing of call at <stdin> (21)
←[0m
←[1m
File "<stdin>", line 21:←[0m
←[1m<source missing, REPL/exec in use?>←[0m
It seems that there is some issue with passing dimensions as a tuple argument to np.random.uniform(). However, I know that np.random.uniform() is supported by Numba and using tuples there is necessary. What is the problem here?
I'm trying to call a vectorized function (numba.guvectorize) inside another guvectorize'd function:
import numpy as np
from numba import guvectorize
#guvectorize("(float64[:], float64[:])", "(n) -> (n)")
def pregain(device_gain, out):
gain = np.cumsum(device_gain)[:-1]
out[:] = np.concatenate((np.array([0.0]), gain))
#guvectorize("(float64[:], float64[:])", "(n) -> (n)")
def call_pregain(device_gain, out):
pgain = pregain(device_gain)
out[:] = pgain
call_pregain(np.array([12, -1.5, 8, -1, 2, -0.8, 15]))
But I get this error and numba falls back to object mode:
NumbaWarning: Compilation is falling back to object mode WITHOUT looplifting enabled because Function "call_pregain" failed type inference due to: Untyped global name 'pregain': Cannot determine Numba type of <class 'numba.np.ufunc.gufunc.GUFunc'>
File "mvce.py", line 13:
def call_pregain(device_gain, out):
pgain = pregain(device_gain)
^
I have some problems with the application of np.argsort in combination with numba.
The input coord is a two-dimensional array consisting of coordinate which need to be sorted in counter-clockwise manner. All the variables are float64 and numy array's.
#jit
def sortVertices(coord):
yr = coord[:, 1]
xc = coord[:, 0]
xl = np.array([xc.size])
yl = np.array([yr.size])
center_xc = np.sum(xc)/xl
center_yr = np.sum(yr)/yl
theta = np.arctan2(yr-center_yr, xc-center_xc) * 180.0 / np.pi
indices = np.argsort(theta)
x = xc[indices]
y = yr[indices]
coord_new = np.vstack((x, y)).T
return coord_new
I update numba. The error:
NumbaWarning:
Compilation is falling back to object mode WITH looplifting enabled because Function sortVertices failed at nopython mode lowering due to: Failed in nopython mode pipeline (step: nopython frontend)
No implementation of function Function(<function make_quicksort_impl.<locals>.run_quicksort at 0x1298bc9d8>) found for signature:
run_quicksort(array(float64, 1d, C))
There are 2 candidate implementations:
- Of which 2 did not match due to:
Overload in function 'register_jitable.<locals>.wrap.<locals>.ov_wrap': File: numba/core/extending.py: Line 150.
With argument(s): '(array(float64, 1d, C))':
Rejected as the implementation raised a specific error:
AttributeError: 'function' object has no attribute 'get_call_template'
raised from /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numba/core/types/functions.py:511\
Thank you in advance.
Hello fellow programmers
I am trying to make a discrete Fourier transform in this minimal working example with the numba.njit decorator:
import numba
import numpy as np
import scipy
import scipy.fftpack
#numba.njit
def main():
wave = [[[0.09254795, 0.10001078, 0.10744892, 0.07755555, 0.08506225, 0.09254795],
[0.09907245, 0.10706145, 0.11502401, 0.08302302, 0.09105898, 0.09907245],
[0.09565098, 0.10336405, 0.11105158, 0.08015589, 0.08791429, 0.09565098],
[0.00181467, 0.001961, 0.00210684, 0.0015207, 0.00166789, 0.00181467]],
[[-0.45816267, - 0.46058367, - 0.46289091, - 0.45298182, - 0.45562851, -0.45816267],
[-0.49046506, - 0.49305676, - 0.49552669, - 0.48491893, - 0.48775223, -0.49046506],
[-0.47352483, - 0.47602701, - 0.47841162, - 0.46817027, - 0.4709057, -0.47352483],
[-0.00898358, - 0.00903105, - 0.00907629, - 0.008882, - 0.00893389, -0.00898358]],
[[0.36561472, 0.36057289, 0.355442, 0.37542627, 0.37056626, 0.36561472],
[0.39139261, 0.38599531, 0.38050268, 0.40189591, 0.39669325, 0.39139261],
[0.37787385, 0.37266296, 0.36736003, 0.38801438, 0.38299141, 0.37787385],
[0.00716892, 0.00707006, 0.00696945, 0.0073613, 0.00726601, 0.00716892]]]
new_fft = scipy.fftpack.fft(wave)
if __name__ == '__main__':
main()
Output:
C:\Users\Artur\Anaconda\python.exe C:/Users/Artur/Desktop/RL_framework/help_functions/test2.py
Traceback (most recent call last):
File "C:/Users/Artur/Desktop/RL_framework/help_functions/test2.py", line 25, in <module>
main()
File "C:\Users\Artur\Anaconda\lib\site-packages\numba\core\dispatcher.py", line 401, in _compile_for_args
error_rewrite(e, 'typing')
File "C:\Users\Artur\Anaconda\lib\site-packages\numba\core\dispatcher.py", line 344, in error_rewrite
reraise(type(e), e, None)
File "C:\Users\Artur\Anaconda\lib\site-packages\numba\core\utils.py", line 80, in reraise
raise value.with_traceback(tb)
numba.core.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Unknown attribute 'fft' of type Module(<module 'scipy.fftpack' from 'C:\\Users\\Artur\\Anaconda\\lib\\site-packages\\scipy\\fftpack\\__init__.py'>)
File "test2.py", line 21:
def main():
<source elided>
new_fft = scipy.fftpack.fft(wave)
^
[1] During: typing of get attribute at C:/Users/Artur/Desktop/RL_framework/help_functions/test2.py (21)
File "test2.py", line 21:
def main():
<source elided>
new_fft = scipy.fftpack.fft(wave)
^
Process finished with exit code 1
Unfortunately scipy.fftpack.fft seems to be a legacy function that is not supported by numba. So I searched for alternatives. I found two:
1.
scipy.fft(wave) which is the updated version of the above mentioned legacy function. It produces this error output:
C:\Users\Artur\Anaconda\python.exe C:/Users/Artur/Desktop/RL_framework/help_functions/test2.py
Traceback (most recent call last):
File "C:/Users/Artur/Desktop/RL_framework/help_functions/test2.py", line 25, in <module>
main()
File "C:\Users\Artur\Anaconda\lib\site-packages\numba\core\dispatcher.py", line 401, in _compile_for_args
error_rewrite(e, 'typing')
File "C:\Users\Artur\Anaconda\lib\site-packages\numba\core\dispatcher.py", line 344, in error_rewrite
reraise(type(e), e, None)
File "C:\Users\Artur\Anaconda\lib\site-packages\numba\core\utils.py", line 80, in reraise
raise value.with_traceback(tb)
numba.core.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Invalid use of Module(<module 'scipy.fft' from 'C:\\Users\\Artur\\Anaconda\\lib\\site-packages\\scipy\\fft\\__init__.py'>) with parameters (list(list(list(float64))))
No type info available for Module(<module 'scipy.fft' from 'C:\\Users\\Artur\\Anaconda\\lib\\site-packages\\scipy\\fft\\__init__.py'>) as a callable.
[1] During: resolving callee type: Module(<module 'scipy.fft' from 'C:\\Users\\Artur\\Anaconda\\lib\\site-packages\\scipy\\fft\\__init__.py'>)
[2] During: typing of call at C:/Users/Artur/Desktop/RL_framework/help_functions/test2.py (21)
File "test2.py", line 21:
def main():
<source elided>
new_fft = scipy.fft(wave)
^
Process finished with exit code 1
2.
np.fft.fft(wave) which seems to be supported but also produces an error:
C:\Users\Artur\Anaconda\python.exe C:/Users/Artur/Desktop/RL_framework/help_functions/test2.py
Traceback (most recent call last):
File "C:/Users/Artur/Desktop/RL_framework/help_functions/test2.py", line 25, in <module>
main()
File "C:\Users\Artur\Anaconda\lib\site-packages\numba\core\dispatcher.py", line 401, in _compile_for_args
error_rewrite(e, 'typing')
File "C:\Users\Artur\Anaconda\lib\site-packages\numba\core\dispatcher.py", line 344, in error_rewrite
reraise(type(e), e, None)
File "C:\Users\Artur\Anaconda\lib\site-packages\numba\core\utils.py", line 80, in reraise
raise value.with_traceback(tb)
numba.core.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Unknown attribute 'fft' of type Module(<module 'numpy.fft' from 'C:\\Users\\Artur\\Anaconda\\lib\\site-packages\\numpy\\fft\\__init__.py'>)
File "test2.py", line 21:
def main():
<source elided>
new_fft = np.fft.fft(wave)
^
[1] During: typing of get attribute at C:/Users/Artur/Desktop/RL_framework/help_functions/test2.py (21)
File "test2.py", line 21:
def main():
<source elided>
new_fft = np.fft.fft(wave)
^
Process finished with exit code 1
Do you know a fft function, that works with the numba.njit decorator?
If you are happy with a 1D DFT you might as well use an FFT.
Here, is reported a Numba-friendly implementation fft_1d() working on arbitrary input sizes:
import cmath
import numpy as np
import numba as nb
#nb.jit
def ilog2(n):
result = -1
if n < 0:
n = -n
while n > 0:
n >>= 1
result += 1
return result
#nb.njit(fastmath=True)
def reverse_bits(val, width):
result = 0
for _ in range(width):
result = (result << 1) | (val & 1)
val >>= 1
return result
#nb.njit(fastmath=True)
def fft_1d_radix2_rbi(arr, direct=True):
arr = np.asarray(arr, dtype=np.complex128)
n = len(arr)
levels = ilog2(n)
e_arr = np.empty_like(arr)
coeff = (-2j if direct else 2j) * cmath.pi / n
for i in range(n):
e_arr[i] = cmath.exp(coeff * i)
result = np.empty_like(arr)
for i in range(n):
result[i] = arr[reverse_bits(i, levels)]
# Radix-2 decimation-in-time FFT
size = 2
while size <= n:
half_size = size // 2
step = n // size
for i in range(0, n, size):
k = 0
for j in range(i, i + half_size):
temp = result[j + half_size] * e_arr[k]
result[j + half_size] = result[j] - temp
result[j] += temp
k += step
size *= 2
return result
#nb.njit(fastmath=True)
def fft_1d_arb(arr, fft_1d_r2=fft_1d_radix2_rbi):
"""1D FFT for arbitrary inputs using chirp z-transform"""
arr = np.asarray(arr, dtype=np.complex128)
n = len(arr)
m = 1 << (ilog2(n) + 2)
e_arr = np.empty(n, dtype=np.complex128)
for i in range(n):
e_arr[i] = cmath.exp(-1j * cmath.pi * (i * i) / n)
result = np.zeros(m, dtype=np.complex128)
result[:n] = arr * e_arr
coeff = np.zeros_like(result)
coeff[:n] = e_arr.conjugate()
coeff[-n + 1:] = e_arr[:0:-1].conjugate()
return fft_convolve(result, coeff, fft_1d_r2)[:n] * e_arr / m
#nb.njit(fastmath=True)
def fft_convolve(a_arr, b_arr, fft_1d_r2=fft_1d_radix2_rbi):
return fft_1d_r2(fft_1d_r2(a_arr) * fft_1d_r2(b_arr), False)
#nb.njit(fastmath=True)
def fft_1d(arr):
n = len(arr)
if not n & (n - 1):
return fft_1d_radix2_rbi(arr)
else:
return fft_1d_arb(arr)
Compared to the naïve DFT algorithm (dft_1d() which is fundamentally the same as this), you are gaining orders of magnitude, while you are still a typically a lot slower than np.fft.fft().
The relative speed varies greatly depending on the input sizes.
For power-of-2 inputs, this is typically within one order of magnitude of np.fft.fft().
For non-power-of-2, this is typically within two orders of magnitude of np.fft.fft().
For worst-case (prime numbers or so, here is power-of-2 + 1), this is a times as fast as np.fft.fft().
The non-linear behavior of the FFT timings are the result of the need for a more complex algorithm for arbitrary input sizes that are not power-of-2. This affects both this implementation and the one from np.fft.fft(), but np.fft.fft() contains a lot more optimizations which make it perform much better on average.
Alternate implementations of power-of-2 FFT are shown here.
The numba documentation mentioned that np.fft.fft is not support. A solution is to use the objmode context to call python functions that are not supported yet. Only the part inside the objmode context will run in object mode, and therefore can be slow. For you particular case, this part will not be that slow because np.fft.fft is already very fast as pointed by #tstanisl as the first comment of the question. Here is as example
from numba import njit
import numpy as np
#njit()
def compute_fft(x):
y = np.zeros(., dtype=np.complex128)
with objmode(y='type[:]'):
y = np.fft.fft(x)
return y
#njit()
def main():
...
x = np.random.randint(100)
fft_x = compute_fft(x)
...
I was able to find a workaround. Now, keep in mind that functions like numpy.fft.fft have lots of convenience operations, so if you're not stuck like me, you should use them.
Following njit function does a discrete fourier transform on a one dimensional array:
import numba
import numpy as np
import cmath
def dft(wave=None):
dft = np.fft.fft(wave)
return dft
#numba.njit
def dft_njit(wave=None):
N = len(wave)
dft_njit = np.zeros(N, dtype=np.complex128)
for i in range(N):
series_element = 0
for n in range(N):
series_element += wave[n] * cmath.exp(-2j * cmath.pi * i * n * (1 / N))
dft_njit[i] = series_element
return dft_njit
if __name__ == '__main__':
wave = [1,2,3,4,5]
wave = np.array(wave)
print(f' dft: \n{dft(wave=wave)}')
print(f' dft_njit: \n{dft_njit(wave=wave)}')
Output:
dft:
[15. +0.j -2.5+3.4409548j -2.5+0.81229924j -2.5-0.81229924j
-2.5-3.4409548j ]
dft_njit:
[15. +0.j -2.5+3.4409548j -2.5+0.81229924j -2.5-0.81229924j
-2.5-3.4409548j ]
Why can't Numba's jit compile a simple Numpy array operation?
Here is a minimal non-working example that reproduces Numba's failure to compile
import numpy as np
from numba import jit
rows = 10
columns = 999999
A = np.empty((rows, columns))
b = np.linspace(0, 1, num=rows)
#jit(nopython=True)
def replicate(A, b):
for i in range(A.shape[1]):
A[:, i] = b
return A #optional
replicate(a, b)
With the following error:
TypingError: Failed at nopython (nopython frontend)
Cannot resolve setitem: array(float64, 1d, C, nonconst)[(slice3_type, int64)] = array(float64, 1d, C, nonconst)
File "<ipython-input-32-db24fbe2922f>", line 12
Am I doing something wrong?
As an aside, I need nopython mode because in my real-life situation I need to perform array addition, multiplication with scalars and array populating with other arrays frequently. and my understanding is that in object mode I won't be able to do loop jitting and thus I won't see any real performance boost on the execution.
Numba doesn't support numpy slicing in nopython mode. Try unrolling the loops explicitly:
rows = 10
columns = 999999
a = np.empty((rows, columns))
b = np.linspace(0, 1, num=rows)
#jit(nopython=True)
def replicate(A, b):
for i in xrange(A.shape[0]):
for j in xrange(A.shape[1]):
A[i, j] = b[i]
return A #optional
replicate(a, b)