加速sympy-lamodified和向量化函数

5 投票
2 回答
1888 浏览
提问于 2025-04-21 06:25

我正在使用sympy来生成一些用于数值计算的函数。因此,我把一个表达式转化为可以和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

相比。

那么,有没有办法让sympy的速度更接近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这样的简单表达式,这个函数不会返回一个数组。有没有办法解决这个问题?这难道不是某种bug,或者至少是设计不一致吗?

2 个回答

3

在这个情况下,使用 np.vectorize() 就像是在对 xyz 的第一维进行循环,所以这会导致速度变慢。如果你告诉 lambdify() 使用 NumPy 的函数,那么就不需要 np.vectorize() 了,而这正是你现在所做的。这样使用:

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
3

lambdify 函数在处理常量时只返回一个值,这是因为它没有使用到 numpy 的函数。这是 lambdify 的工作方式所决定的(具体可以参考 这个链接)。

不过,这通常不是个问题,因为常量在与数组进行任何操作时,会自动调整为合适的形状。换句话说,你可以直接使用常量,它会自动适应。而如果你特意用一个包含相同常量的数组,那样效率就会低很多,因为你会重复计算同样的操作多次。

撰写回答