/* matrix eigenvalues/eigenvectors via QR decomposition from Handbook of C Tools for Scientists and Engineers by L. Baker CONTENTS: eigenvv() computes eigenvalues and eigenvectors of a matrix hqr2() applies QR method to Hessenberg matrix elmhes() converts general matrix to Hessenberg form eltran() creates transform. matrix from elmhes output balance() balances a matrix balbak() inverse transform of balance(used on eigenvectors) pnormwr() normalize and print eigenvectors. largest element=1. type double versions of vector.c and matrix.c routines: printmd DEPENDENCIES: NONE */ /* #include "libc.h" */ #include /* defines below are from ftoc.h except for IU,DFFOR,DFFR,DFFRR */ #define INDEX(i,j) [j+(i)*coln] #define IU(i,j) [j+(i)*colu] #define DOFOR(i,j) for(i=0;i=to;i--) #define min(a,b) (((a)<(b))? (a): (b)) #define max(a,b) (((a)<(b))? (b): (a)) /*#define abs(x) ( ((x)>0.)?(x):-(x)) using fabs */ #define itmax 30 printmd(a,coln,rown,col,row) int rown,row,col,coln; double a[]; { int i,j,btm,top,count; printf("\n"); btm=top=0; while(btmigh) { wr[i]= h INDEX(i,i); wi[i]=0.0; } for(j=k;j=low) { its=0; na=en-1; enm2=na-1; nextit: DFFRR(l,en,low)/*algol code had one less iteration than ftn*/ {/* as in EISPAC*/ s=fabs(h INDEX(l-1,l-1) )+ fabs(h INDEX(l,l) ); if(s==0.)s=norm; if( fabs(h INDEX(l,l-1) ) <= s*macheps )goto break1; } l=low; break1: x= h INDEX(en,en); if(l==en) {/*found a root*/ /*printf(" single root its=%d\n",its);*/ wr[en]= x+t; wi[en]=0.; h INDEX(en,en)=x+t; en=na; cnt[en]=its; goto nextw; } y= h INDEX(na,na); w= h INDEX(en,na) * h INDEX(na,en); if(l!=na) { if(its==itlim) { cnt[en]=31; *ierr=en; return; } if(its==it1 || its==it2) { t+=x; DFFR(i,low,en) h INDEX(i,i) -=x; s= fabs( h INDEX(en,na) ) +fabs( h INDEX(na,en-2)); x=.75*s; y=x; w=-0.4375*s*s; } its++; /*printf(" its=%d\n",its);*/ for(m=en-2;m>=l;m--) { mm=m+1; z= h INDEX(m,m); r=x-z; s=y-z; p=(r*s-w)/(h INDEX(mm,m)) + h INDEX(m,mm); q= h INDEX(mm,mm)-z-r-s; r= h INDEX(m+2,mm); s= fabs(p)+fabs(q)+fabs(r); p/=s; q/=s; r/=s; if(m==l)break; if( fabs( h INDEX(m,m-1)) * (fabs(q)+fabs(r)) <= macheps*fabs(p)* (fabs(h INDEX(m-1,m-1))+fabs(z)+fabs(h INDEX(mm,mm))) )break; } for(i=m+2;i<=en;i++) h INDEX(i,i-2)=0.; for(i=m+3;i<=en;i++) h INDEX(i,i-3)=0.; for(k=m;k<=na;k++) { notlast= (k!=na); if(k!=m) { p= h INDEX(k,k-1); q= h INDEX(k+1,k-1); r= notlast ? h INDEX(k+2,k-1) : 0.; x=fabs(r)+fabs(q)+fabs(p); if (x==0.)break;/*exit for loop*/ p/=x;q/=x;r/=x; } s= sqrt(p*p+q*q+r*r); if (p<0.) s=-s; if (k!=m) h INDEX(k,k-1)=-s*x; else if(l!=m) h INDEX(k,k-1)=-h INDEX(k,k-1); p+=s; x=p/s;y=q/s;z=r/s;q/=p;r/=p; /*row modification*/ for(j=k;j=0.) /*q>0 in algol */ {/*real pair*/ z= p<0. ? p-z : p+z ; wr[na]=x+z; wr[en]=wr[na]; /*EISPACK version */ if (z !=0.) { s=x-w/z; wr[en]=s; } wi[en]=0.; wi[na]=0.; x=h INDEX(en,na); /* r=sqrt(x*x+z*z); p=x/r; q=z/r; algol version */ s= fabs(x)+fabs(z); p=x/s; q=z/s; r= sqrt(p*p+q*q); p/=r; q/=r; for(j=na;j=0;en--) { p=wr[en]; q=wi[en]; na=en-1; if(q==0.) { /*real vector*/ m=en; h INDEX(en,en)=1.; for(i=na;i>=0;i--) { w=h INDEX(i,i)-p; r=h INDEX(i,en); DFFR(j,m,na) { r+=h INDEX(i,j)*h INDEX(j,en); } if(wi[i]<0.0) { z=w; s=r; } else { m=i; if (wi[i]==0.) { h INDEX(i,en)= -r/( (w!=0.)? w : macheps*norm); } else { x=h INDEX(i,i+1); y=h INDEX(i+1,i); q=(wr[i]-p); yr=wi[i]; q=q*q+ yr*yr; t=(x*s-z*r)/q; h INDEX(i,en)=t; h INDEX(i+1,en)= (fabs(x)>fabs(z))? (-r-w*t)/x :(-s-y*t)/z ; }/*wi!=0*/ } /*wi>=0*/ }/*for i loop*/ }/*real vector,q=0*/ else { if(q<0.) { m=na; /* in algol, was -q, with xi=0 . EISPACK commment says this alternate choice was to make last eigenvector component imag. so that eigenvector matrix triangular*/ if(fabs(h INDEX(en,na))>fabs(h INDEX(na,en))) { h INDEX(na,en)=-(h INDEX(en,en) -p)/h INDEX(en,na); h INDEX(na,na)= q/h INDEX(en,na); } else { xr=0.; xi=- h INDEX(na,en); yr= h INDEX(na,na)-p; yi=q; if(fabs(yr)>fabs(yi)) { t=yi/yr; yr+=t*yi; zr=(xr+t*xi)/yr; zi=(xi-t*xr)/yr; } else { t=yr/yi; yi+=t*yr; zr=(t*xr+xi)/yi; zi=(t*xi-xr)/yi; } h INDEX(na,na)=zr; h INDEX(na,en)=zi; } h INDEX(en,na)=0.; h INDEX(en,en) =1.; for(i=na-1;i>=0;i--) { w= h INDEX(i,i)-p; /* caveat- in algol, the assignments to ra,sa below were switched. believe its bug fix in EISPACK */ sa= h INDEX(i,en); ra=0.; for (j=m;j<=na;j++) { ra+= h INDEX(i,j)*h INDEX(j,na); sa+= h INDEX(i,j)*h INDEX(j,en); } if(wi[i]<0.) { z=w; r=ra; s=sa; } else { m=i; if(wi[i]==0.) { /*cdiv*/ xr=-ra; xi=-sa; yr=w; yi=q; if(fabs(yr)>fabs(yi) ) { t=yi/yr; yr+=t*yi; zr=(xr+t*xi)/yr; zi=(xi-t*xr)/yr; } else { t=yr/yi; yi+=t*yr; zr=(t*xr+xi)/yi; zi=(t*xi-xr)/yi; } h INDEX(i,na)=zr; h INDEX(i,en)=zi; } else { x=h INDEX(i,i+1); y=h INDEX(i+1,i); vi=wi[i]; t=(wr[i]-p); vr=t*t+vi*vi-q*q; vi=2.0*q*t; if(vr==0. && vi==0.) { vr=macheps*norm* (fabs(w)+fabs(q)+fabs(x)+fabs(y)+fabs(z) ); }/*zero vr and vi*/ /*cdiv*/ xr= x*r-z*ra+q*sa; xi=s*x-z*sa-q*ra; yr=vr; yi=vi; if(fabs(yr)>fabs(yi) ) { t=yi/yr; yr+=t*yi; zr=(xr+t*xi)/yr; zi=(xi-t*xr)/yr; } else { t=yr/yi; yi+=t*yr; zr=(t*xr+xi)/yi; zi=(t*xi-xr)/yi; } h INDEX(i,na)=zr; h INDEX(i,en)=zi; if(fabs(x)>(fabs(z)+fabs(q))) { h INDEX(i+1,na)= (-ra-w*h INDEX(i,na)+q* h INDEX(i,en))/x; h INDEX(i+1,en)= (-sa-w*h INDEX(i,en)-q* h INDEX(i,na) )/x; } else { /*cdiv*/ xr=-r-y* h INDEX(i,na); xi=-s-y*h INDEX(i,en) ; yr=z; yi=q; if(fabs(yr)>fabs(yi)) { t=yi/yr; yr+=t*yi; zr=(xr+t*xi)/yr; zi=(xi-t*xr)/yr; } else { t=yr/yi; yi+=t*yr; zr=(t*xr+xi)/yi; zi=(t*xi-xr)/yi; } h INDEX(i+1,na)=zr; h INDEX(i+1,en)=zi; } }/* wi!=0*/ } }/*for i*/ }/*q<0. complex vector*/ }/*end else*/ }/*end for*/ /* vector of roots*/ DOFOR(i,n) { if(iigh) { for(j=i;j=low;j--) { m= min( j , igh) ; DFFR(i,low,igh) { z=0.;DFFR(k,low,m) z += vecs INDEX(i,k) * h INDEX(k,j); vecs INDEX(i,j)=z; } }/*end for j loop*/ return; } elmhes(colm,n,low,igh,a,intar) int colm,n,low,igh, intar[]; double *a; { int i,j,m,la,kp1,mm1,mp1; double x,y; int coln; coln=n; la= igh-1; kp1=low+1; if(la fabs(x ) ) { x= a INDEX(j,mm1); i=j; } } intar[m]=i; if(i!=m) { DFFOR(j,mm1,n) { y= a INDEX(i,j); a INDEX(i,j) = a INDEX(m,j); a INDEX(m,j) = y; } DFFR( j,0,igh) { y= a INDEX(j,i); a INDEX(j,i) = a INDEX(j,m); a INDEX(j,m) = y; } } if(x!=0.) { mp1=m+1; DFFR(i,mp1,igh) { y= a INDEX(i,mm1); if (y!=0.) { y/=x; a INDEX(i,mm1) =y; DFFOR(j,m,n) a INDEX(i,j) -= y* a INDEX(m,j); DFFR(j,0,igh) a INDEX(j,m) += y* a INDEX(j,i); } } } } } eltran(coln,n,low,igh,a,intar,z) int coln,low,igh,n,intar[]; double *a,*z; {/*form matrix of accumulated transforms z*/ int colu,i,j,k,l,kl,mn,mp,mp1; /* set z(n x n) to identity matrix; */ colu=n; DOFOR(i,n) { DOFOR(j,n) z IU(i,j) =0.; z IU(i,i) = 1.; } kl=igh-low-1; if(kl<1)return; DFFRR(i,(igh-1),(low+1)) { j= intar[i]; DFFR(k,i+1,igh) z IU(k,i) =a INDEX(k,i-1); if(i!=j) { DFFR(k,i,igh) { z IU(i,k) = z IU(j,k); z IU(j,k) =0.; } z IU(j,i)=1.; } } } /* not used as hqr2 does it elmbak(colu,n,m,low,igh,a,intar,z) int colu,n,low,igh,m,intar[]; double *z,*a; { int coln,i,j,la,l,mm,mp,kp1,mp1; double x; if(m<0)return; la=igh-1; kp1=low+1; coln=n; if(laigh) { /* l=0 i=low-1 l=low-1 i=0*/ if(i=g) { f*=bi;c*=b2i;goto l4; } if( (c+r)/f < .95*s) { g=1./f; scale[i]*=f; noconv=1; DFFOR(j,l,n) a INDEX(i,j) *= g; DFFR(j,0,k) a INDEX(j,i) *= f; } }/* do i */ kt++; if(noconv && kt>lim) { printf(" balance iteration limit\n"); exit(0); } if (noconv)goto iteration; } pnormwr(coln,n,z,wr,wi) double *z,wr[],wi[]; int coln,n; {/* normalize as in wilinson & Reinsch with largest element=1*/ int i,j,k; double t,xr,xi,yi,yr,zr,zi,emax,ei; printf(" un normalized matrix:\n"); printmd(z,coln,n,n,n); DOFOR(i,n) { if(wi[i]==0.) { emax=0.; DOFOR(j,n) { if ( fabs(z INDEX(j,i))>fabs(emax) ) emax=z INDEX(j,i); } if(emax!=0.) { emax=1./emax; DOFOR(j,n) { z INDEX(j,i) *=emax; } } } else /*complex eigenvector*/ { emax=0.; k=0; DOFOR(j,n) { xr= z INDEX(j,i) ; yr=z INDEX(j,i+1); xr= sqrt(xr*xr+yr*yr); if(xr>emax) { emax=xr; k=j; } } emax=z INDEX(k,i); ei=z INDEX(k,i+1); DOFOR(j,n) { yr=emax; yi=ei; xr=z INDEX(j,i); xi=z INDEX(j,i+1); if(fabs(yr)>fabs(yi) ) { t=yi/yr; yr+=t*yi; zr=(xr+t*xi)/yr; zi=(xi-t*xr)/yr; } else { t=yr/yi; yi+=t*yr; zr=(t*xr+xi)/yi; zi=(t*xi-xr)/yi; } z INDEX(j,i)=zr; z INDEX(j,i+1)=zi; } i++; } } /* DOFOR i loop*/ printf(" normalized matrix of eigenvectors:\n"); printmd(z,coln,n,n,n); } /* specify matrices by row.*/ double et[4][4]={1.,2.,3.,5.,2.,4.,1.,6.,1.,2.,-1.,3.,2.,0.,1.,3.}; double ev[4][4]={3.,1.,2.,5.,2.,1.,3.,-1.,0.,4.,1.,1.,0.,0.,2.,1.}; double ex[3][3]={2.,4.,4.,0.,3.,1.,0.,1.,3.}; double ey[3][3]={5.,-4.,-7.,-4.,2.,-4.,-7.,-4.,5.}; main(argc,argv) int argc; char **argv; {/*test driver*/ double wr[4],wi[4],z[16],d[4]; float mach; int i,j,iwork[4],cnt[4],bal; elmhes(4,4,0,3,et,iwork); printf(" elmhes test\n"); printmd(et,4,4,4,4); printf(" enter macheps ");scanf("%e",&mach); macheps=mach; printf(" macheps=%e\n",macheps); printf(" eigenv test\n"); bal=1; eigenvv(4,4,ev,iwork,d,z,wr,wi,bal,cnt); DOFOR(i,4)printf(" eigenvalue real= %e imag=%e it=%d\n",wr[i],wi[i],cnt[i]); printmd(z,4,4,4,4); pnormwr(4,4,z,wr,wi); printf(" eigenv test 2\n"); bal=0; eigenvv(3,3,ex,iwork,d,z,wr,wi,bal,cnt); DOFOR(i,3)printf(" eigenvalue real= %e imag=%e it=%d\n",wr[i],wi[i],cnt[i]); printmd(z,3,3,3,3); pnormwr(3,3,z,wr,wi); printf(" eign test 3\n"); bal=1; eigenvv(3,3,ey,iwork,d,z,wr,wi,bal,cnt); DOFOR(i,3)printf(" eigenvalue real= %e imag=%e it=%d\n",wr[i],wi[i],cnt[i]); printmd(z,3,3,3,3); pnormwr(3,3,z,wr,wi); }