【问题标题】:Converting A Fortran77 Code To Matlab Code for finding eigen values/vectors将 Fortran77 代码转换为 Matlab 代码以查找特征值/向量
【发布时间】:2012-07-29 15:16:40
【问题描述】:

我将 fortran 77 中的书面代码转换为 Matlab 代码。此函数使用 QL 算法计算矩阵的特征值和特征向量。由于某些原因,我不能在 matlab 中使用 eig 函数的结果。该方法得到的特征值与eig函数得到的特征值不同,有的相同,有的不同。我不知道问题出在哪里。感谢您的任何帮助和建议。如果需要运行和查看结果,我可以提供输入数组。

这是fortran代码:

      SUBROUTINE tqli(d,e,n,np,z)
      INTEGER n,np
      REAL d(np),e(np),z(np,np)
CU    USES pythag
      INTEGER i,iter,k,l,m
      REAL b,c,dd,f,g,p,r,s,pythag
      do 11 i=2,n
        e(i-1)=e(i)
11    continue
      e(n)=0.
      do 15 l=1,n
        iter=0
1       do 12 m=l,n-1
          dd=abs(d(m))+abs(d(m+1))
          if (abs(e(m))+dd.eq.dd) goto 2
12      continue
        m=n
2       if(m.ne.l)then
          if(iter.eq.30)pause 'too many iterations in tqli'
          iter=iter+1
          g=(d(l+1)-d(l))/(2.*e(l))
          r=pythag(g,1.)
          g=d(m)-d(l)+e(l)/(g+sign(r,g))
          s=1.
          c=1.
          p=0.
          do 14 i=m-1,l,-1
            f=s*e(i)
            b=c*e(i)
            r=pythag(f,g)
            e(i+1)=r
            if(r.eq.0.)then
              d(i+1)=d(i+1)-p
              e(m)=0.
              goto 1
            endif
            s=f/r
            c=g/r
            g=d(i+1)-p
            r=(d(i)-g)*s+2.*c*b
            p=s*r
            d(i+1)=g+p
            g=c*r-b
C     Omit lines from here ...
            do 13 k=1,n
              f=z(k,i+1)
              z(k,i+1)=s*z(k,i)+c*f
              z(k,i)=c*z(k,i)-s*f
13          continue
C     ... to here when finding only eigenvalues.
14        continue
          d(l)=d(l)-p
          e(l)=g
          e(m)=0.
          goto 1
        endif
15    continue
      return
      END

以下是matlab代码:

function [d,z]=tqli(d,e,n,np,z)

for i=2:n
    e(i-1)=e(i);
end
e(n)=0.;
for l=1:n
    iter=0;
    for m=l:(n-1)
        dd=abs(d(m))+abs(d(m+1));
        if ((abs(e(m))+dd)==dd)
            break
        end
    end
    m=n;
    if (m~=l)
        if (iter==30)
            disp('too many iteration in tqli')
        end
        iter=iter+1;
        g=(d(l+1)-d(l))/(2.*e(l));
        r=pythag(g,1.);
        g=d(m)-d(l)+e(l)/(g+r*sign(g));
        s=1.;
        c=1.;
        p=0.;
        for i=(m-1):-1:l
            f=s*e(i);
            b=c*e(i);
            r=pythag(f,g);
            e(i+1)=r;
            if(r==0.)
                d(i+1)=d(i+1)-p;
                e(m)=0.;
                break
            end
            s=f/r;
            c=g/r;
            g=d(i+1)-p;
            r=(d(i)-g)*s+2.*c*b;
            p=s*r;
            d(i+1)=g+p;
            g=c*r-b;
            for k=1:n
                f=z(k,i+1);
                z(k,i+1)=s*z(k,i)+c*f;
                z(k,i)=c*z(k,i)-s*f;
            end
        end
        d(l)=d(l)-p;
        e(l)=g;
        e(m)=0.;
    end
end
end

【问题讨论】:

  • 请详细说明“某些原因”是什么。 Matlb 例程通常非常健壮,所以我非常怀疑是否需要重新发明轮子来进行特征值计算。

标签: algorithm matlab fortran linear-algebra eigenvalue


【解决方案1】:

我看到了一些可能会导致您的 matlab 翻译出现问题的情况。一个是您对 fortran 符号的转换。您需要使用 abs(r) 而不仅仅是 r。

我看到的另一个更严重的问题是您尝试重构 goto 的流程结构。当我使用 f2matlab(以及我编写的一个未发布的工具 remgoto)转换它时,它提出了以下流程结构。我希望这对您有所帮助。请注意,这都是未经测试的!

function [d,e,n,np,z]=tqli(d,e,n,np,z);

remg([1:2])=true;

for i = 2 : n;
 e(i-1) = e(i);
end
e(n) = 0.;
for l = 1 : n;
 while (1);
  if(remg(2))
   if(remg(1))
    iter = 0;
   end;
   remg(1)=true;
   for m = l : n - 1;
    dd = abs(d(m)) + abs(d(m+1));
    if( abs(e(m))+dd==dd )
     remg(2)=false;
     break;
    end;
   end;
   if(~(remg(2)))
    continue;
   end;
   m = fix(n);
  end;
  remg(2)=true;
  if( m~=l )
   if( iter==30 )
    disp(['too many iterations in tqli',' -- Hit Return to continue']);
    pause ;
   end;
   iter = fix(iter + 1);
   g =(d(l+1)-d(l))./(2..*e(l));
   r = pythag(g,1.);
   g = d(m) - d(l) + e(l)./(g+(abs(r).*sign(g)));
   s = 1.;
   c = 1.;
   p = 0.;
   for i = m - 1 : -1: l ;
    f = s.*e(i);
    b = c.*e(i);
    r = pythag(f,g);
    e(i+1) = r;
    if( r==0. )
     d(i+1) = d(i+1) - p;
     e(m) = 0.;
     remg(1)=false;
     break;
    end;
    s = f./r;
    c = g./r;
    g = d(i+1) - p;
    r =(d(i)-g).*s + 2..*c.*b;
    p = s.*r;
    d(i+1) = g + p;
    g = c.*r - b;
    %        Omit lines from here ...
    for k = 1 : n;
     f = z(k,i+1);
     z(k,i+1) = s.*z(k,i) + c.*f;
     z(k,i) = c.*z(k,i) - s.*f;
    end; k = fix(n+1);
    %        ... to here when finding only eigenvalues.
   end;
   if(~(remg(1)))
    continue;
   end;
   d(l) = d(l) - p;
   e(l) = g;
   e(m) = 0.;
   remg(1)=false;
   continue;
  end;
  break;
 end;
end;
end %subroutine tqli

【讨论】:

    【解决方案2】:

    在您的 Fortran 代码中,您将一组变量声明为 REAL。大多数编译器默认将这些实现为 32 位浮点数。默认情况下,您的 Matlab 版本中的相应变量是 64 位浮点数。

    在没有看到您的输入或输出的情况下,很难判断这是否部分或全部是两个版本输出差异的原因。但是,更改浮点数的精度通常是导致您报告的这类问题的原因,尤其是在特征值计算等棘手的数值方法中。

    在撰写本文时,我还注意到您将比较浮点值是否相等的不良做法从 Fortran 转换为 Matlab。在 Fortran 中这是不好的做法,在 Matlab 中也是不好的做法。

    在 Matlab 中写表达式时要小心

    2.*c
    

    在 Fortran 中,将标量或数组变量 c 的每个元素乘以 REAL2.0。在 Matlab 中,它将标量或向量 c 的每个元素乘以整数 2.* 在 Matlab 中是一个运算符(或运算符的组合,如果您愿意的话),意思是“逐个元素执行乘法” '。您已经巧妙地更改了程序的语义,可能还更改了您计算的某些数字的精确值。

    这引出了我的最后一条评论:您所说的 Matlab 版本只不过是 Fortran 代码的音译,是从一种语言到另一种语言的逐行复制。有时你可以侥幸逃脱,但这种幼稚的方法迟早会绊倒你。也许它已经有了。您真的应该将您的 Matlab 重写为 Matlab,就好像您打算编写好的 Matlab 一样。

    【讨论】:

    • 感谢您的考虑,但我认为问题出在其他地方。因为它正确地给出了一些特征值。我不得不说你的评论是对的,我知道这不是一个很好的理由,但我找不到一个清晰的算法来自己编写 matlab 代码。谢谢。
    猜你喜欢
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-15
    • 1970-01-01
    • 2022-12-30
    • 1970-01-01
    相关资源
    最近更新 更多