这个问题是关于使用NumPy与Octave / MATLAB的计算精度(下面的MATLAB代码仅用Octave测试过)。我在Stackoverflow上发现了一个类似的问题,即this,但这似乎与我在下面提到的内容有些不同。
设置
一切都在Ubuntu 14.04上运行。
Python 3.4.0版。
针对OpenBLAS编译的NumPy版本1.8.1。
针对OpenBLAS编译的Octave版本3.8.1。
示例代码
示例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)。
EDITS
我的scipy
版本为0.14.0
。我知道Octave / MATLAB使用Pade近似方案,并熟悉这个算法。我不确定scipy
是做什么的,但我们可以尝试以下方法。
使用numpy
' s eig
或eigh
对矩阵进行对角化(在我们的例子中,后者工作正常,因为矩阵是Hermitian)。结果我们得到两个矩阵:对角矩阵D
和矩阵U
,其中D
由对角线上原始矩阵的特征值组成,U
组成相应的特征向量作为列;这样原始矩阵由U.T.dot(D).dot(U)
给出。
Exponentiate 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似乎做得更好。
答案 0 :(得分:5)
以下代码
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 documentation所述,该算法来自Al-Mohy和Higham(2009),它与Octave中更简单的scale-and-square-Pade不同。
因此,我从Octave得到的结果略有不同,尽管结果在矩阵范数(1,2,inf)中接近eps。 MATLAB使用Higham(2005)的Pade方法,它似乎与上面的Scipy给出了相同的结果。