【问题标题】:Wrong result for the Inverse Matrix逆矩阵的错误结果
【发布时间】:2019-11-08 15:03:15
【问题描述】:

我想通过 Fortran 代码计算复杂矩阵的逆矩阵以及矩阵 52 乘 52 的大小。我使用 MKL 库中的 ZGETRF 和 ZGETRI 函数来运行测试。如果复矩阵的大小是 2×2,这两个函数效果很好,因为原矩阵和逆矩阵的乘积是单位矩阵;但是,我发现如果复数矩阵的大小是 3 x 3,则结果是错误的,因为原矩阵和逆矩阵的乘法不再是单位矩阵。

我把我的测试代码贴在这里,你可以参考一下。

我在集群上用shell脚本运行测试程序,脚本也粘贴在这里。

PROGRAM TE
IMPLICIT NONE
INTEGER :: i, j, dime
DOUBLE COMPLEX, ALLOCATABLE :: a(:,:)
DOUBLE COMPLEX, ALLOCATABLE :: b(:,:)
DOUBLE COMPLEX, ALLOCATABLE :: c(:,:)

!Variables used in LU decomposition subroutine (ZGETRF)
INTEGER                       :: nu_r                 !Number of row in target matrix
INTEGER                       :: nu_c                 !Number of column in target matrix
INTEGER                       :: lu_lda               !Leading dimension of target matrix
INTEGER, ALLOCATABLE          :: lu_ipiv(:)           !Array with dimension of .GE. MIN(nu_r,nu_c)
INTEGER                       :: lu_info              !Judgement (0-successful;<0-error and infor = number, then
                                                      !'1-number'th argument has illegal value;>0 and info = 
                                                      !number - LU matrix(number,number) is zero)
!

!Variables used in inverse subroutine (ZGETRI)
INTEGER                       :: nu_o                 !Order of LU matrix
DOUBLE COMPLEX, ALLOCATABLE   :: in_work(:)           !Workspace array with dimension of .GE. MAX(1,in_lwork)
INTEGER                       :: in_lda               !Leading dimension of LU decomposed matrix .GE. MAX(1,nu_o)
INTEGER                       :: in_lwork             !Size of in_work array with value .GE. nu_o
INTEGER                       :: in_info              !Judgement (0-successful;<0-error and infor = number, then
                                                      !'1-number'th argument has illegal value;>0 and info =
                                                      !number; then, LU matrix(number,number) is zero and inversion
                                                      !cannot be accomplished)

dime = 3
ALLOCATE (a(dime,dime))
ALLOCATE (b(dime,dime))
ALLOCATE (c(dime,dime))

DO i = 1, dime, 1
   DO j = 1, dime, 1
      a(i,j) = CMPLX(DBLE(i), DBLE(j))
   END DO
END DO

b = a

OPEN (UNIT=3, FILE='te_in.dat', STATUS='UNKNOWN')

WRITE (UNIT=3, FMT='(A1)') 'a'
DO i = 1, dime, 1
   DO j = 1, dime, 1
      WRITE (UNIT=3, FMT=*) a(i,j)
   END DO
END DO
WRITE (UNIT=3, FMT=*)

!Initialising parameters for LU decomposition subroutine (ZGETRF)
nu_r = dime
nu_c = dime
lu_lda = dime
ALLOCATE (lu_ipiv(dime))
lu_ipiv = 0
!

!Initialising parameters for inverse subroutine (ZGETRI)
nu_o = dime
in_lwork = dime
ALLOCATE (in_work(in_lwork))
in_work = (0.0d0, 0.0d0)
in_lda = dime
!

CALL ZGETRF(nu_r,nu_c,b,lu_lda,lu_ipiv,lu_info)
CALL ZGETRI(nu_o,b,in_lda,lu_ipiv,in_work,in_lwork,in_info)

WRITE (UNIT=3, FMT='(A1)') 'b'
DO i = 1, dime, 1
   DO j = 1, dime, 1
      WRITE (UNIT=3, FMT=*) b(i,j)
   END DO
END DO
WRITE (UNIT=3, FMT=*)

c = MATMUL(a,b)

WRITE (UNIT=3, FMT='(A1)') 'c'
DO i = 1, dime, 1
   DO j = 1, dime, 1
      WRITE (UNIT=3, FMT=*) c(i,j)
   END DO
END DO
WRITE (UNIT=3, FMT=*)

DEALLOCATE (lu_ipiv)
DEALLOCATE (a)
DEALLOCATE (b)
DEALLOCATE (c)

CLOSE (UNIT=3)
STOP
END PROGRAM TE

下面是输出文件2×2复数矩阵的内容,正确。

a
(1.00000000000000,1.00000000000000)
(1.00000000000000,2.00000000000000)
(2.00000000000000,1.00000000000000)
(2.00000000000000,2.00000000000000)

b
(-2.00000000000000,2.00000000000000)
(2.00000000000000,-1.00000000000000)
(1.00000000000000,-2.00000000000000)
(-1.00000000000000,1.00000000000000)

c
(1.00000000000000,-2.220446049250313E-016)
(0.000000000000000E+000,2.220446049250313E-016)
(0.000000000000000E+000,4.440892098500626E-016)
(1.00000000000000,0.000000000000000E+000)

但是,程序不会为 3 x 3 复数矩阵生成正确的输出内容,如下所示。

a
(1.00000000000000,1.00000000000000)
(1.00000000000000,2.00000000000000)
(1.00000000000000,3.00000000000000)
(2.00000000000000,1.00000000000000)
(2.00000000000000,2.00000000000000)
(2.00000000000000,3.00000000000000)
(3.00000000000000,1.00000000000000)
(3.00000000000000,2.00000000000000)
(3.00000000000000,3.00000000000000)

b
(723205779577744.,262983919846453.)
(-1.446411559155488E+015,-525967839692903.)
(723205779577744.,262983919846451.)
(-1.446411559155489E+015,-525967839692904.)
(2.892823118310976E+015,1.051935679385808E+015)
(-1.446411559155487E+015,-525967839692903.)
(723205779577745.,262983919846452.)
(-1.446411559155488E+015,-525967839692905.)
(723205779577743.,262983919846452.)

c
(0.437500000000000,0.000000000000000E+000)
(1.25000000000000,0.000000000000000E+000)
(-0.250000000000000,0.000000000000000E+000)
(0.125000000000000,0.000000000000000E+000)
(0.500000000000000,0.000000000000000E+000)
(0.750000000000000,0.000000000000000E+000)
(-0.500000000000000,0.000000000000000E+000)
(0.000000000000000E+000,-1.00000000000000)
(1.50000000000000,0.500000000000000)

我还用其他更高阶的复杂矩阵测试我的代码,结果都是错误的。我在下面粘贴了我用来提交作业以运行此测试程序的 shell 脚本。

#!/bin/bash
module switch PrgEnv-cray PrgEnv-intel
#module load intel
#----------------------------------------------------------#
ifort -O3 te.f90 \
  -Wl,--start-group${MKLROOT}/lib/intel64/libmkl_intel_lp64.a \
                   ${MKLROOT}/lib/intel64/libmkl_core.a \
                   ${MKLROOT}/lib/intel64/libmkl_sequential.a \
  -Wl,--end-group-lpthread -lm –ldl

谁能告诉我我的代码有什么问题? ZGETRF 和 ZGETRI 函数有问题吗?谁能给我一些关于如何整理它的建议?

【问题讨论】:

  • 对于所有 Fortran 问题,请使用标签 fortran

标签: fortran intel-mkl matrix-inverse


【解决方案1】:

(使用 Mathematica 的插图)

该问题与 MKL 无关。对于dime&gt;2,您的矩阵是单数的。 MKL 不应该为没有逆矩阵的矩阵提供正确的逆矩阵。

In[1]:= mat[n_]:=Table[i+I j,{i,1,n},{j,1,n}]
In[2]:= Table[Det[mat[n]],{n,1,20}]
Out[2]= {1+I,-I,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}

其实不难看出他们在dime&gt;1排名第二。

In[3]:= col[n_]:=Table[i+I j,{i,1,n},{j,1,2}]
In[4]:= row[n_]:={Table[2-j,{j,1,n}],Table[j-1,{j,1,n}]}
In[5]:= Table[Norm[mat[n]-col[n].row[n]],{n,2,20}]
Out[5]= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}

也就是说,您的矩阵可能的rank factorization 是 C*R :

C=[1+I 1+2I]
  [2+I 2+2I]
  [...  ...]
  [n+I n+2I]

R=[1 0 -1 ... 2-n]
  [0 1  2 ... n-1]

【讨论】:

  • @kieran :另外请参阅zgetri / zgetrfinfo 参数关于可能的返回值(正如您在信息值的声明中已经提到的那样)。
  • @albert 实际上 info=0 两个电话。 documentation 表示 INFO > 0:如果 INFO = i,U(i,i) 正好为零;矩阵是奇异的,无法计算其逆矩阵。 但由于数值误差,U(i,i) 通常不完全为零,它只是一个非常小的值,因此输出中的值非常大.
  • 所以实际上问题是因为矩阵在逻辑上/被人类视为奇异但由于计算机的数值分辨率这一事实(有一些很好的文章,特别是:什么每个计算机科学家都应该了解浮点运算,David Goldberg 着)它不会被 MKL 检测到。
  • @albert @ Jean-Claude Arbaut 尊敬的 Albert 和 Jean, 非常感谢您提出的宝贵建议。如果我希望 ZGETRF 或 ZGETRI 函数返回错误信息(info=i),在处理奇异矩阵时,我需要通过更改分辨率使集群将小值识别为零。我的理解正确吗?如果是这样,有什么办法可以修改集群上的这个数值分辨率设置;例如,通过修改编译文件?非常感谢。
  • @Kieran 如果降低精度,仍然会有数值错误。由于 LU 代码没有检测到这一点,因此它不起作用。您可以在之后检查行列式(或者最好是行列式的对数)或条件数:使用 SVD 并检查最大奇异值与最小奇异值之间的比率。请记住,使用数值软件您无法真正检查矩阵是否为单数,您只能检查它是否numerically 单数(即接近单数)。
【解决方案2】:

你能用最新的 MKL 2019 版本检查这个案例吗?我记得很清楚,MKL 在 getri 进入 2018 版时遇到了一些问题。

【讨论】:

  • 亲爱的根纳迪, 非常感谢您的建议。我会查一下。另一方面,你能推荐我一些其他的解决方案吗?是否有其他稳定的 FORTRAN 函数或当前稳定的 FORTRAN 代码,计算复矩阵的逆?
  • @Kieran xGETRF 和 xGETRI 子例程是 LAPACK 的一部分。 LAPACK 的实现有很多,MKL 包含其中之一。但还有其他的,包括大多数 Linux 发行版中存在并与-llapack-lblas 链接的参考netlib.org/lapack。但是,请参阅其他答案。
  • @Vladimir F 非常感谢您的建议。我会寻找更多的解决方案。
猜你喜欢
  • 2021-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多