I have tried to get a faster computation of integrals using python numba. Even though the timing using numba is almost 10 times faster for a single computation, when I loop the process for redefining the integral, it becomes awfully slow. I have tried using other decorators like @vectorize
or @jit
but with no success. Any tips on how to do it?
import numpy as np
import datetime as dd
from scipy.integrate import quad
from numba import cfunc, types, carray
tempText = 'Time Elapsed: {0:.6f} sec'
arr = np.arange(0.01,1.01,0.01)
out = np.zeros_like(arr)
def tryThis(): # beginner's solution
for i in range(len(arr)):
def integrand(t):
return np.exp(-arr[i]*t)/t**2
def do_integrate(func):
return quad(func,1,np.inf)[0]
out[i] = do_integrate(integrand)
# print (out)
init = dd.datetime.now()
tryThis()
print (tempText.format((dd.datetime.now()-init).total_seconds()))
Time Elapsed: 0.047950 sec
def try2VectorizeThat(): # using numpy
def do_integrate(arr):
def integrand(t):
return np.exp(-arr*t)/t**2
return quad(integrand,1,np.inf)[0]
do_integrate = np.vectorize(do_integrate)
out = do_integrate(arr)
# print (out)
init = dd.datetime.now()
try2VectorizeThat()
print (tempText.format((dd.datetime.now()-init).total_seconds()))
Time Elapsed: 0.026424 sec
def tryThisFaster(): # attempting to use numba
for i in range(len(arr)):
def get_integrand(*args):
a = args[0]
def integrand(t):
return np.exp(-a*t)/t**2
return integrand
nb_integrand = cfunc("float64(float64)")(get_integrand(arr[i]))
def do_integrate(func):
return quad(func,1,np.inf)[0]
out[i] = do_integrate(nb_integrand.ctypes)
# print (out)
init = dd.datetime.now()
tryThisFaster()
print (tempText.format((dd.datetime.now()-init).total_seconds()))
Time Elapsed: 1.905140 sec
Note that you are measuring time for assigning variables and defining function included.
Also, numba
can become (or seem) slower when the job is too small, as it needs time to compile itself and then applied.
Placing integrand
outside the loop and decorating with @njit
can give you some performance boost. Let's see some comparisons:
from numba import njit
@njit
def integrand(t, i):
return np.exp(-arr[i]*t)/t**2
def tryFaster():
for i in range(len(arr)):
out[i] = quad(integrand, 1, np.inf, args=(i))[0]
Time taken when len(arr) = 100
:
arr = np.arange(0.01,1.01,0.01)
%timeit tryThis()
# 29.9 ms ± 4.59 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit tryFaster()
# 4.99 ms ± 11.5 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
Time taken when len(arr) = 10,000
:
arr = np.arange(0.01,100.01,0.01)
%timeit tryThis()
# 1.43 s ± 208 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit tryFaster()
# 142 ms ± 17.7 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)