Reputation: 389
How to improve perfomance for G(x,y) for this type of evaluation structure:
from scipy import integrate,infty
def f(x):
"""
Some complicated function
"""
pass
def F(x):
"""
Integration of f
"""
value = integrate.quad(f,-infty,x)
return value
def g(x,y):
"""
Another complicated function which uses F(x)!
"""
pass
def G(x,y):
"""
The function for which I want to improve perfomance
"""
value = integrate.quad(g,-infty,+infty,args=(y))
return value
What I want is to substitute F(x) evaluation with its reference to it which have been done previously.
After of using scipy.interpolate.interp1d
and decorator for clarity, my code looks like:
class interpolate_function():
"""
Returns interpolated function in given range
"""
def __init__(self,tmin=-20,tmax=+20):
self.tmin = tmin
self.tmax = tmax
def __call__(self,expX):
tmin = self.tmin
tmax = self.tmax
from numpy import linspace
t = linspace(tmin,tmax,2000)
import scipy.interpolate as inter
#expX_interp = inter.PchipInterpolator(t,W.expX(t))
from scipy import vectorize
expX = vectorize(expX)
expX_interp = inter.interp1d(t,expX(t),kind='linear')
return expX_interp
from scipy import integrate,infty
def f(x):
"""
Some complicated function
"""
pass
@interpolate_function(tmin=-20,tmax=+20)
def F(x):
"""
Integration of f
"""
value = integrate.quad(f,-infty,x)
return value
def g(x,y):
"""
Another complicated function which uses F(x)!
"""
pass
def G(x,y):
"""
The function for which I want to improve perfomance
"""
value = integrate.quad(g,-infty,+infty,args=(y))
return value
Therefore the main code except decorator remains unchanged but performance is boosted around 3000 times.
Upvotes: 1
Views: 221
Reputation: 3865
For every call to F(x)
, you are doing an integration in (-infinity, x), that is your bottleneck. Instead, I would perform the integration to a set of points and create an interpolating function. So, if your values of x
are between 0 and 10, you can perform the integrals from -infinity to 0, 0.5... 9.5, 10 (as thin grid as you need) and interpolate on them.
Edit:
In order to build the grid more efficiently, you can use the additive properties of the integrals. So, F(0) = int_infty^0 f(x), F(1) = F(0) + int_0^1 f(x), etc. You can use different sizes for the grid, as long as you keep track of them. Also, to be safe from weird interpolating effects, I would use pchip
(Hermite polinomials, always reach maximum and minimum in the borders) or linear interpolation.
Upvotes: 2
Reputation: 122062
The typical way to do this is "memoization", using a decorator:
def memo(f):
cache = {}
def func(*args):
if args not in cache:
cache[args] = f(*args)
return cache[args]
return func
@memo
def F(x):
"""
Integration of f
"""
value = integrate.quad(f,-infty,x)
return value
Now whenever you call F(x)
, you are really calling func(x)
, and if F
has already been evaluated for that value of x
it is returned straight from the cache
, rather than calling F
again.
Note that, as Davidmh points out, this trades speed for space; you have to store all of the previously-evaluated results. Also, you only get a benefit where you are evaluating exactly the same value of x
multiple times.
Upvotes: 1
Reputation: 1957
In my experience, the key to optimizing for efficiency in numpy/scipy is to minimize the amount of python code execution. If at all possible, any loops should not be done as python loops, but as matrix-vector operations or similar; essentially, you want to move loops to within numpy/scipy libraries.
In your case, I would try to avoid calling other python functions from g
and try to write it as a short series of numpy/scipy calls.
Upvotes: 0