【问题标题】:Improve performance for returning value提高返回值的性能
【发布时间】:2014-04-24 10:48:54
【问题描述】:

对于这种类型的评估结构,如何提高 G(x,y) 的性能:

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

我想要的是将 F(x) 评估替换为之前已经完成的对它的引用。

编辑

在使用scipy.interpolate.interp1d 和装饰器后,我的代码看起来像:

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

因此除装饰器外的主要代码保持不变,但性能提升了约 3000 倍。

【问题讨论】:

    标签: python numpy scipy


    【解决方案1】:

    对于每次调用 F(x),您都在 (-infinity, x) 中进行集成,这是您的瓶颈。相反,我将对一组点执行积分并创建一个插值函数。因此,如果您的 x 值介于 0 和 10 之间,您可以执行从 -infinity 到 0、0.5... 9.5、10 的积分(尽可能细的网格)并对其进行插值。

    编辑:

    为了更有效地构建网格,您可以使用积分的加法属性。所以,F(0) = int_infty^0 f(x), F(1) = F(0) + int_0^1 f(x) 等等。只要你保持跟踪,你可以为网格使用不同的大小其中。另外,为了避免奇怪的插值效应,我会使用pchip(Hermite 多项式,总是在边界达到最大值和最小值)或线性插值。

    【讨论】:

    • 似乎插值是解决我的问题的关键,但为什么插值数据的准确评估可以更快? x 值从 -infinity 到 100 并且已知 F(x) 对于较大的负值变化不大。如何选择样品?
    • F(4.5) 涉及在 -infinity 和 4.5 之间对f(x) 的多次评估;但插值函数只需要一次多项式评估。
    • 知道了 ;) 现在我需要选择样本。是否有可用的自适应方法(更小的步骤以更快地改变功能)?对于 -infinity,-100 可能是一个很好的近似值。
    • 我想不到,除了滚动你自己的。如果你想创建一个不定积分,你可以在邮件列表中加入。此外,将无穷大替换为固定数字可能会带来非常好的性能(在某些情况下,还可以提高准确性)。
    • 感谢您提到 pchip,这正是我进行实验数据插值所需要的 :) 但是从性能角度来看,线性插值大约是经济的 100 倍。
    【解决方案2】:

    执行此操作的典型方法是"memoization",使用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
    

    现在,每当您调用 F(x) 时,您实际上是在调用 func(x),如果 F 已经针对 x 的值进行了评估,则它直接从 cache 返回,而不是调用 @987654329再次@。

    请注意,正如 Davidmh 指出的那样,这是以速度换空间;您必须存储所有先前评估的结果。此外,您只有在多次评估完全相同的 x 值时才能获得好处。

    【讨论】:

    • 这仅在 x 的值重复时才有效,并且对于不太可能出现的浮点数。此外,您将无限增加内存。
    • 感谢在这种情况下使用装饰器的想法。
    【解决方案3】:

    根据我的经验,优化 numpy/scipy 效率的关键是尽量减少 python 代码的执行量。如果可能的话,任何循环都不应该作为python循环来完成,而应该作为矩阵向量运算或类似的;本质上,您希望将循环移动到 numpy/scipy 库中。

    在您的情况下,我会尽量避免从 g 调用其他 python 函数,并尝试将其编写为一系列简短的 numpy/scipy 调用。

    【讨论】:

      猜你喜欢
      • 2023-02-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-19
      • 2019-03-26
      • 2012-03-19
      • 1970-01-01
      相关资源
      最近更新 更多