Python NumPy与Octave/MATLAB精度对比
这个问题是关于使用NumPy和Octave/MATLAB进行计算时的精度差异(下面的MATLAB代码只在Octave上测试过)。我知道StackOverflow上有一个类似的问题,链接是这个,但似乎和我下面要问的内容不太一样。
环境设置
一切都在Ubuntu 14.04上运行。
Python版本是3.4.0。
NumPy版本是1.8.1,使用OpenBLAS编译。
Octave版本是3.8.1,使用OpenBLAS编译。
示例代码
下面是一些Python代码示例。
import numpy as np
from scipy import linalg as la
def build_laplacian(n):
lap=np.zeros([n,n])
for j in range(n-1):
lap[j+1][j]=1
lap[j][j+1]=1
lap[n-1][n-2]=1
lap[n-2][n-1]=1
return lap
def evolve(s, lap):
wave=la.expm(-1j*s*lap).dot([1]+[0]*(lap.shape[0]-1))
for i in range(len(wave)):
wave[i]=np.linalg.norm(wave[i])**2
return wave
我们现在运行以下代码。
np.min(evolve(2, build_laplacian(500)))
结果大约是e-34
。
我们可以在Octave/MATLAB中写出类似的代码:
function lap=build_laplacian(n)
lap=zeros(n,n);
for i=1:(n-1)
lap(i+1,i)=1;
lap(i,i+1)=1;
end
lap(n,n-1)=1;
lap(n-1,n)=1;
end
function result=evolve(s, lap)
d=zeros(length(lap(:,1)),1); d(1)=1;
result=expm(-1i*s*lap)*d;
for i=1:length(result)
result(i)=norm(result(i))^2;
end
end
然后我们运行
min(evolve(2, build_laplacian(500)))
得到的结果是0
。实际上,evolve(2, build_laplacian(500)))(60)
的结果大约在e-100
或更小(这是预期的结果)。
问题
有没有人知道为什么NumPy和Octave之间会有这么大的差异?(再次说明,我没有在MATLAB上测试代码,但我预计结果会类似)。
当然,我们也可以通过先对矩阵进行对角化来计算矩阵的指数。我尝试过这样做,但在NumPy中得到的结果相似或更差。
编辑
我的scipy
版本是0.14.0
。我知道Octave/MATLAB使用Pade近似方案,并且对这个算法有所了解。我不太确定scipy
是怎么做的,但我们可以尝试以下步骤。
用
numpy
的eig
或eigh
对矩阵进行对角化(在我们的例子中,后者效果很好,因为矩阵是厄米矩阵)。结果我们得到两个矩阵:一个对角矩阵D
和一个矩阵U
,其中D
的对角线是原始矩阵的特征值,U
的列是对应的特征向量;所以原始矩阵可以表示为U.T.dot(D).dot(U)
。对
D
进行指数运算(这现在很简单,因为D
是对角矩阵)。现在,如果
M
是原始矩阵,d
是原始向量d=[1]+[0]*n
,我们得到scipy.linalg.expm(-1j*s*M).dot(d)=U.T.dot(numpy.exp(-1j*s*D).dot(U.dot(d))
。
不幸的是,这样做的结果和之前一样。因此,这可能与numpy.linalg.eig
和numpy.linalg.eigh
的工作方式有关,或者与numpy
内部的算术运算有关。
所以问题是:我们如何提高numpy
的精度?确实,如上所述,Octave在这种情况下似乎做得更好。
1 个回答
下面的代码
import numpy as np from scipy import linalg as la import scipy print np.__version__ print scipy.__version__ def build_laplacian(n): lap=np.zeros([n,n]) for j in range(n-1): lap[j+1][j]=1 lap[j][j+1]=1 lap[n-1][n-2]=1 lap[n-2][n-1]=1 return lap def evolve(s, lap): wave=la.expm(-1j*s*lap).dot([1]+[0]*(lap.shape[0]-1)) for i in range(len(wave)): wave[i]=la.norm(wave[i])**2 return wave r = evolve(2, build_laplacian(500)) print np.min(abs(r)) print r[59]
输出结果是
1.8.1 0.14.0 0 (2.77560227344e-101+0j)
这是我在使用 OpenBLAS 0.2.8-6ubuntu1 时得到的。
所以看起来你的问题没有立刻复现。你上面的代码示例有一些小错误,不能直接运行。
正如在scipy.linalg.expm 的文档中提到的,这个算法来自 Al-Mohy 和 Higham(2009),和 Octave 中的简单缩放平方 Pade 方法是不同的。
因此,我从 Octave 得到的结果也稍有不同,尽管在矩阵范数(1, 2, 无穷大)上结果是非常接近的。MATLAB 使用的是 Higham(2005)提出的 Pade 方法,似乎和上面的 Scipy 得到的结果是一样的。