【问题标题】:Speedup sympy-lamdified and vectorized function加速 sympy-lamdified 和矢量化函数
【发布时间】:2014-11-06 08:30:30
【问题描述】:

我正在使用 sympy 生成一些用于数值计算的函数。因此,我将表达式lambdify 并对其进行矢量化以将其与numpy 数组一起使用。这是一个例子:

import numpy as np
import sympy as sp

def numpy_function():
    x, y, z = np.mgrid[0:1:40*1j, 0:1:40*1j, 0:1:40*1j]
    T   = (1 - np.cos(2*np.pi*x))*(1 - np.cos(2*np.pi*y))*np.sin(np.pi*z)*0.1
    return T

def sympy_function():
    x, y, z = sp.Symbol("x"), sp.Symbol("y"), sp.Symbol("z")
    T   = (1 - sp.cos(2*sp.pi*x))*(1 - sp.cos(2*sp.pi*y))*sp.sin(sp.pi*z)*0.1
    lambda_function = np.vectorize(sp.lambdify((x, y, z), T, "numpy"))
    x, y, z = np.mgrid[0:1:40*1j, 0:1:40*1j, 0:1:40*1j]
    T = lambda_function(x,y,z)
    return T

sympy 版本和纯 numpy 版本之间的问题是速度,即

In [3]: timeit test.numpy_function()  
100 loops, best of 3: 11.9 ms per loop

对比

In [4]: timeit test.sympy_function()
1 loops, best of 3: 634 ms per loop

那么有什么办法可以接近 numpy 版本的速度呢? 我认为 np.vectorize 很慢,但不知何故,我的代码的某些部分没有它就无法工作。感谢您的任何建议。

编辑: 所以我找到了为什么需要vectorize函数的原因,即:

In [35]: y = np.arange(10)

In [36]: f = sp.lambdify(x,sin(x),"numpy")

In [37]: f(y)
Out[37]: 
array([ 0.        ,  0.84147098,  0.90929743,  0.14112001, -0.7568025 ,
       -0.95892427, -0.2794155 ,  0.6569866 ,  0.98935825,  0.41211849])

这似乎工作正常:

In [38]: y = np.arange(10)

In [39]: f = sp.lambdify(x,1,"numpy")

In [40]: f(y)
Out[40]: 1

所以对于像1 这样的简单表达式,这个函数不会返回一个数组。 有没有办法解决这个问题,这不是某种错误或至少不一致的设计吗?

【问题讨论】:

    标签: python numpy vectorization sympy


    【解决方案1】:

    lambdify 为常量返回单个值,因为不涉及 numpy 函数。这是因为lambdify 的工作方式(参见https://stackoverflow.com/a/25514007/161801)。

    但这通常不是问题,因为在将常量用于数组的任何操作中,它都会自动广播到正确的形状。另一方面,如果您显式使用相同常量的数组,则效率会低得多,因为您将多次计算相同的操作。

    【讨论】:

    • 感谢您的回答。我的问题是我有类似 self.T = T(x,y,z) 所以在这种情况下广播不起作用。最好的方法是什么?
    • 看起来numpy.array在使用数组调用时是一个空操作,因此您可以使用np.array(T(x, y, z))来确保结果始终是一个数组。
    • 虽然我仍然不明白为什么您的用例不适用于广播。
    • @asmeurer 我不明白为什么即使是这样的东西:f = lambdify(x, np.ones_like(x)*cte, 'numpy') 它返回标量cte 不知道x 是什么......我应该打开一个关于它的问题吗?
    • lambdify 的第二个参数必须是符号表达式。 np.ones_like(Symbol('x')) 返回array(1),所以这与lambdify(x, array(1)*cte, 'numpy') 相同。请记住,只有 SymPy 函数仍然“未评估”。
    【解决方案2】:

    在这种情况下使用np.vectorize() 就像在xyz 的第一个维度上循环,这就是它变慢的原因。你不需要 np.vectorize() IF 你告诉 lambdify() 使用 NumPy 的函数,这正是你正在做的。然后,使用:

    def sympy_function():
        x, y, z = sp.Symbol("x"), sp.Symbol("y"), sp.Symbol("z")
        T   = (1 - sp.cos(2*sp.pi*x))*(1 - sp.cos(2*sp.pi*y))*sp.sin(sp.pi*z)*0.1
        lambda_function = sp.lambdify((x, y, z), T, "numpy")
        x, y, z = np.mgrid[0:1:40*1j, 0:1:40*1j, 0:1:40*1j]
        T = lambda_function(x,y,z)
        return T
    

    使性能具有可比性:

    In [26]: np.allclose(numpy_function(), sympy_function())
    Out[26]: True
    
    In [27]: timeit numpy_function()
    100 loops, best of 3: 4.08 ms per loop
    
    In [28]: timeit sympy_function()
    100 loops, best of 3: 5.52 ms per loop
    

    【讨论】:

    • @jrsm 你是对的......我什至试过f= lambdify(x, (cte*np.ones_like(x)))lambdify 总是返回cte 值......这可能是一个错误,我会在他们的问题中打开一个问题列表...希望我的回答对您有所帮助...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-31
    • 1970-01-01
    • 2022-01-17
    • 2020-04-06
    • 1970-01-01
    相关资源
    最近更新 更多