Jānis Erdmanis
Jānis Erdmanis

Reputation: 389

Improve performance for returning value

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.

Edit

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

Answers (3)

Davidmh
Davidmh

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

jonrsharpe
jonrsharpe

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

jrennie
jrennie

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

Related Questions