/*## ## class FFull ## ## This class extends FMatrix to represent all full matrices. ## It contains the subroutine for singular value decomposition, the ## QR decompostion routines, the updating routines, and the Gaussian ## Elimination factoring routines. ## ##*/ package edu.rice.linpack.Matrix.FMatrix; import edu.rice.linpack.LNumber.*; import edu.rice.linpack.util.*; import edu.rice.linpack.Vector.*; public class FFull extends FMatrix { public FFull() { super(); cons(); } public FFull(int i, int j) { cons(i,j); } public FFull(float[][] f) { cons(f); } public FFull(FFull F) { cons(F); } public FFull(int ni, int nj, float[][] f, int strow, int stcol) { rows = ni; cols = nj; Mat = new float[rows][cols]; for(int i=0;i= n. ## Contains the FFull whose singular value ## decomposition is to be computed. This FFull ## is destroyed by svDecompose ## ## n int ## n is the number of columns of the section of this ## FFull that is to be used. ## ## p int ## p is the number of rows of the section of this ## FFull that is to be used. ## ## job int ## job controls the computation of the singular ## vectors. It has the decimal expansion AB ## with the following meaning: ## ## A == 0 Do not compute the left singular ## vectors. ## A == 1 Return the n left singular vectors ## in U. ## A >= 2 Return the first min(n,p) singluar ## vectors in U. ## B == 0 Do not compute the right singular ## vectors. ## B == 1 Return the right singular vectors ## in V. ## ## On Return: ## ## S float[MM], where MM = Math.min(n+1,p). ## the first min(n,p) entries of S contain the ## singular values of this FFull arranged in ## descending order of magnitude. ## ## E float[p] ## E ordinarily contains zeros. However see the ## discussion of info for exceptions. ## ## U float[n][k], where if job(A) == 1 then k == n ## else k == Min(n,p) ## U contains the FFull of right singular vectors. ## U is not referenced if job(A) == 0. ## ## V float[p][p] ## V contains the FFull of right singular vectors. ## V is not referenced if job == 0. ## ## info int ## The singular values (and their corresponding ## singular vectors) S(info),S(info+1),...,S(M-1) ## are correct (here M=min(n,p)). Thus if ## info == 0, all the singular values and their ## vectors are correct. In any event, the matrix ## B = U.transpose()*this*V is the bidiagonal matrix ## with the elements of S on its diagonal and the ## elements of E on its super-diagonal. Thus the ## singular values of this and B are the same. ##*/ public int svDecompose(float[] S, float[] E, FFull U, FFull V, int job) { float[] W = new float[rows]; int info = 0; /*## ## This is the maximum number of iterations. ##*/ int MAXIT = 30; /*## ## Determine what is to be computed ##*/ boolean wantU = false; boolean wantV = false; int jobu = (job%100)/10; int NCU = rows; if(jobu > 1) NCU = Math.min(cols,rows); if(jobu != 0) wantU = true; if(job%10 != 0) wantV = true; int NCT = Math.min(rows-1,cols); int NRT = Math.max(0,Math.min(cols-2,rows)); int lu = Math.max(NCT,NRT); for(int q=0;q0) { if(iter >= MAXIT) return M; else { int q = qgetter(M-2,S,E); if(q == M-2) { q++; if(S[q] < 0) { S[q] = -S[q]; if(wantV) V.scal(cols,-1,1,0,q); } boolean notDone = true; while(q < MM-1 && notDone) { if(S[q] < S[q+1]) { FUtil.swapElems(S,q,q+1); if(wantV && q < cols-1) V.swap(cols,1,0,q,V,1,0,q+1); if(wantU && q < rows-1) U.swap(rows,1,0,q,U,1,0,q+1); q++; } else notDone = false; } iter = 0; M--; } else { int qs = this.findNextStep(q,M,S,E); if(qs == q) { q++; FTrig Tg = this.fggetter(q,M,S,E); for(int k=q;k=q;k--) { Tg.setA(S[k]); FUtil.rotg(Tg); S[k] = Tg.getA(); if(k != q) { Tg.setB(-Tg.getSin()*E[k-1]); E[k-1] *= Tg.getCos(); } if(wantV) V.rot(cols,1,0,k,V,1,0,MMOne+1,Tg); } } else { q = qs; FTrig Tg = new FTrig(); Tg.setB(E[q]); E[q] = 0; q++; for(int k=q;k 0) { for(int q=NCT-1;q>=0;q--) { if(S[q] != 0) { for(int j=q+1;j=0;q--) { int qp = q+1; if((q < NRT) && (E[q] != 0)) { for(int j=qp;j= 0;q--) { float Test = Math.abs(S[q]) + Math.abs(S[q+1]); float ZTest = Test + Math.abs(E[q]); if(ZTest == Test) { E[q] = 0; return q; } } return -1; } private int findNextStep(int q, int M, float[] S, float[] E) { for(int qs=M-1;qs>q;qs--) { float Test = 0; if(qs != M-1) Test += Math.abs(E[qs]); if(qs != q+1) Test += Math.abs(E[qs-1]); float Ztest = Test + Math.abs(S[qs]); if(Ztest == Test) { S[qs] = 0; return qs; } } return q; } private FTrig fggetter(int q, int M, float[] S, float[] E) { FTrig Tg = new FTrig(); float scale = FUtil.aMax(S[M-1],S[M-2],E[M-2],S[q],E[q]); float SM = S[M-1]/scale; float SMM = S[M-2]/scale; float EMM = E[M-2]/scale; float Sq = S[q]/scale; float Eq = E[q]/scale; float B = ((SMM + SM)*(SMM - SM) + (float)Math.pow(EMM,2))/2; float C = (float)Math.pow(SM*EMM,2); float shift = 0; if(B != 0 || C != 0) { shift = (float)Math.sqrt(Math.pow(B,2) + C); if(B < 0) shift = -shift; shift = C/(B+shift); } Tg.setA((Sq + SM)*(Sq - SM) - shift); Tg.setB(Sq*Eq); return Tg; } /*## ## qrDecompose uses Householder transformations to compute the ## QR factorization of an n by p FFull. Column pivoting ## based on the 2-norms of the reduced columns may be ## performed at the user's option. ## ## On Entry: ## ## this FFull - Mat[L][p], where L >= n ## FFull contains the FFull whose decomposition is to ## be computed. ## ## ## n int ## n is the number of rows of FFull.Mat to be used in ## the calculation ## ## p int ## p is the number of columns of FFull.Mat to be used ## in the calculation ## ## pivot int[p] ## pivot contains ints that control the selection ## of the pivot columns. The k-th column FFull.Mat[0][k] ## is placed in one of three classes according to the ## value of pivot[k]: ## ## if pivot[k] > 0, then FFull.getColumn(k,0) is an ## initial column. ## ## if pivot[k] == 0, then FFull.getColumn(k,0) is a ## free column. ## ## if pivot[k] < 0, then FFull.getColumn(k,0) is a ## final column. ## ## Before the decomposition is computed, initial columns ## are moved to the beginning of FFull.Mat and final ## columns to the end. Both initial and final columns ## are frozen in place during the computation and only ## free columns are moved. At the k-th stage of the ## reduction, if FFull.getColumn(k,0) is occupied by a ## free column it is interchanged with the free column of ## largest reduced norm. pivot is not referenced if ## job == 0. ## ## ## On Return ## ## this FFull.Mat contains in its upper triangle the upper ## triangular matrix R of the QR factorization. ## Below its diagonal FFull.Mat contains information ## from which the orthogonal part of the decomposition ## can be recovered. Note that if pivoting has ## been requested, the decomposition is not that ## of the original FFull.Mat, but that of FFull.Mat ## with its columns permuted as described by pivot. ## ## QRaux float[p] ## QRaux contains further information required to recover ## the orthogonal part of the decomposition. ## ## pivot pivot[k-1] contains the index of the column of the ## original matrix that has been interchanged into ## the k-th column, if pivoting was requested. ##*/ public void qrPDecompose(float[] QRaux) { Pul P = new Pul(0,-1); this.qrPivot(P); this.qrDecom(P, QRaux); } private void qrPivot(Pul P) { this.swaperSki(P); P.pu = cols-1; for(int j=P.pu;j>=0;j--) { if(pivot[j] < 0) { pivot[j] = -pivot[j]; if(j != P.pu) { this.swap(rows,1,0,P.pu,this,1,0,j); FUtil.swapElems(pivot,P.pu,j); } P.pu--; } } } void swaperSki(Pul P) { for(int j=0;j 0; if(pivot[j] < 0) pivot[j] = -j-1; else pivot[j] = j+1; if(swapj) this.swap2(j,P); } } void swap2(int j, Pul P) { if(j != P.pl) this.swap(rows,1,0,P.pl,this,1,0,j); pivot[j] = pivot[P.pl]; pivot[P.pl] = j+1; P.pl++; } public void qrDecompose(float[] QRaux) { Pul P = new Pul(0,-1); this.qrDecom(P,QRaux); } private void qrDecom(Pul P, float[] QRaux) { float[] Work = new float[cols]; for(int j=P.pl;j<=P.pu;j++) { QRaux[j] = this.nrm2(rows,1,0,j); Work[j] = QRaux[j]; } int qup = Math.min(rows,cols); for(int q=0;q= P.pl && q= maxnorm) { maxnorm = QRaux[j]; maxj = j; } } if(maxj != q) { this.swap(rows,1,0,q,this,1,0,maxj); QRaux[maxj] = QRaux[q]; Work[maxj] = Work[q]; FUtil.swapElems(pivot,maxj,q); } } QRaux[q] = 0; if(q < rows-1) { float normxq = this.nrm2(rows-q,1,q,q); if(normxq != 0) { if(this.Mat[q][q] != 0) normxq = FUtil.signOf(normxq,this.Mat[q][q]); this.scal(rows-q,1/normxq,1,q,q); this.Mat[q][q]+=1; for(int j=q+1;j= P.pl && j <= P.pu) { if(QRaux[j] != 0) { float TT = 1 - (float)Math.pow(Math.abs(this.Mat[q][j])/ QRaux[j],2); TT = Math.max(TT,0); T = TT; TT = 1 + (float).5*TT*(float)Math.pow(QRaux[j]/Work[j],2); if(TT != 1) QRaux[j] *= (float)Math.sqrt(T); else { QRaux[j] = this.nrm2(rows-q-1,1,q+1,j); Work[j] = QRaux[j]; } } } } QRaux[q] = this.Mat[q][q]; this.Mat[q][q] = -normxq; } } } } /*## ## The following qr*** routines apply the output of qrDecompose to ## compute coordinate transformations, projections, and least squares ## solutions. ## for k < Min(n,p), let XK be the matrix ## ## XK = (this.getColumn(pivot(0)-1,0), ## this.getColumn(pivot(1)-1,0), ## ... ,this.getColumn(pivot(k)-1,0)); ## ## formed from columns pivot[0], ... , pivot[k] of the original ## n X p matrix FFull.Mat that was input to qrDecompose (if no ## pivoting was done, XK consists of the first k+1 columns of ## FFull.Mat in their original order). qrDecompose produces a ## factored orthogonal matrix Q and an upper triang. matrix R s.t. ## ## XK = Q * (R) ## (0) ## ## this information is contained in coded form in FFull.Mat and QRaux ## ## On Entry ## ## this FFull.Mat[n][p] ## FFull.Mat contains the output of qrDecompose ## ## n int ## n is the number of rows of the matrix XK. It must ## have the same value as n in qrDecompose. ## ## k int ## k is the number of columns of the matrix XK. k ## must not be greater than min(n,p), where p is the ## same as in qrDecompose. ## ## QRaux float[p] ## QRaux contains the auxiliary output from qrDecompose. ## ## Y float[n] ## Y contains the vector that is to be manipulated ## by qr***. ## ## On Return from its respective function ## ## qrQy: ## Qy float[n] ## Qy contains Q*Y ## ## qrQty: ## Qty float[n] ## Qty contains Q.transpose()*Y ## ## qrQB: ## B float[k] ## B contains the solution of the least squares problem ## ## minimize norm2(Y - XK*B), ## ## (Note that if pivoting was requested in qrDecompose, ## the j-th component of B will be associated with column ## pivot[j-1]-1 of the original FFull.Mat that was input ## into qrDecompose.) ## ## info int ## info is zero unless R is exactly singular in the ## computation of B. In this case, info is the index plus ## one of the first zero diagonal element of R and B is ## left unaltered. ## ## qrQRSD: ## Rsd float[n] ## Rsd contains the least squares residual Y - XK*B, ## Rsd is also the orthogonal projection of Y onto the ## orthogonal complement of the column space of XK. ## ## qrXB: ## Xb float[n] ## Xb contains the least squares approximation XK*B, ## Xb is also the orthogonal projection of Y onto the ## column space of FFull.Mat. ##*/ public void qrQy(float[] QRaux, float[] Y, float[] Qy) { int k = Math.min(rows,cols); int ju = Math.min(k,rows-1) - 1; if(ju < 0) Qy[0] = Y[0]; else { FUtil.copy(rows,Y,1,0,Qy,1,0); for(int j=ju;j>=0;j--) this.qrsolver(j,Qy,QRaux); } } public void qrQty(float[] QRaux, float[] Y,float[] Qty) { int k = Math.min(rows,cols); int ju = Math.min(k,rows-1) - 1; if(ju < 0) Qty[0] = Y[0]; else { FUtil.copy(rows,Y,1,0,Qty,1,0); for(int j=0;j<=ju;j++) this.qrsolver(j,Qty,QRaux); } } public int qrB(float[] QRaux, float[] Y, float[] Qty, float[] B) { int k = Math.min(cols,rows); int ju = Math.min(k,rows-1) - 1; qrQty(QRaux,Y,Qty); if(ju < 0) { if(this.Mat[0][0] == 0) return 1; else B[0] = Y[0]/this.Mat[0][0]; } else { FUtil.copy(k,Qty,1,0,B,1,0); for(int j=k-1;j>=0;j--) { if(this.Mat[j][j] == 0) return j+1; else { B[j] /= this.Mat[j][j]; if(j > 0) this.axpy(j,-B[j],1,0,j,B,1,0); } } } return 0; } public void qrXB(float[] QRaux, float[] Y, float[] Qty, float[] Xb) { int k = Math.min(cols,rows); int ju = Math.min(k,rows-1) - 1; qrQty(QRaux,Y,Qty); if(ju < 0) Xb[0] = Y[0]; else { FUtil.copy(k,Qty,1,0,Xb,1,0); for(int i=k;i=0;j--) this.qrsolver(j,Xb,QRaux); } } public void qrRSD(float[] QRaux, float[] Y, float[] Qty, float[] Rsd) { int k = Math.min(cols,rows); int ju = Math.min(k,rows-1) - 1; qrQty(QRaux,Y,Qty); if(ju < 0) Rsd[0] = 0; else { int kp = k+1; if(k < rows) FUtil.copy(rows-k,Qty,1,k,Rsd,1,k); for(int i=0;i=0;j--) this.qrsolver(j,Rsd,QRaux); } } private void qrsolver(int j, float[] R, float[] QRaux) { if(QRaux[j] != 0) { float Temp = this.Mat[j][j]; this.Mat[j][j] = QRaux[j]; float T = -this.dot(rows-j,1,j,j,R,1,j)/this.Mat[j][j]; this.axpy(rows-j,T,1,j,j,R,1,j); this.Mat[j][j] = Temp; } } public void chUpdate(float[] X,float[][] Z, float[] Y, float[] Rho, float[] C, float[] S) { // Update the matrix // for(int j=0;j= 0) { float scale = azeta*Rho[j]; Rho[j] = scale*(float)Math.sqrt(Math.pow(azeta/scale,2)+ Math.pow(Rho[j]/scale,2)); } } } public int chDowndate(float[] X,float[][] Z, float[] Y,float[] Rho,float[] C,float[] S) { int info = 0; float Zeta; S[0] = X[0]/this.Mat[0][0]; for(int j=1;j= 1) info = -1; else { float alpha = (float)Math.sqrt(1 - (float)Math.pow(norm,2)); for(int i=cols-1;i>=0;i--) { float scale = alpha + Math.abs(S[i]); float A = alpha/scale; float B = S[i]/scale; norm = (float)Math.sqrt(Math.pow(A,2) + Math.pow(B,2)); C[i] = A/norm; S[i] = B/norm; alpha = scale*norm; } for(int j=0;j=0;i--) { float T = C[i]*XX + S[i]*this.Mat[i][j]; this.Mat[i][j] = C[i]*this.Mat[i][j] - S[i]*XX; XX = T; } } for(int j=0;j Rho[j]) { info = 1; Rho[j] = -1; } else Rho[j] *= (float)Math.sqrt(1-(float)Math.pow(azeta/Rho[j],2)); } } return info; } public void chExchange(int k, int q, FFull Z, float[] C, float[] S, int job) { /* k and l are java (0 based) indices */ int km = k-1; int kp = k+1; int qmkp = q-k; int qm = q-1; if(job == 1) { for(int i=0;i<=q;i++) { int ii = q-i; S[i] = this.Mat[ii][q]; } for(int j=qm;j>=k;j--) { for(int i=0;i<=j;i++) this.Mat[i][j+1] = this.Mat[i][j]; this.Mat[j+1][j+1] = 0; } for(int i=0;i 1) { for(int k=0;k < nm; k++) { int kp = k+1; /*## ## Find L the pivot index ##*/ int L = this.i_amax(cols-k,1,k,k) + k; pivot[k] = L+1; /*## ## Zero pivot implies this column already triangularized ##*/ if(this.Mat[L][k] != 0) { /*## ## Interchange if necessary ##*/ if(L != k) this.swapElem(L,k,k,k); /*## ## Compute multipliers ##*/ T = (float)-1.0/this.Mat[k][k]; this.scal(nm-k,T,1,kp,k); /*## ## Row elimination with column indexing ##*/ for(int j=kp;j Math.abs(this.Mat[k][k])) { float S = Math.abs(this.Mat[k][k])/Math.abs(ek-Z[k]); FUtil.scal(cols,S,Z,1); ek *= S; } float wk = ek - Z[k]; float wkm = -ek - Z[k]; float S = Math.abs(wk); float SM = Math.abs(wkm); if(this.Mat[k][k] != 0) { wk /= this.Mat[k][k]; wkm /= this.Mat[k][k]; } else { wk = 1; wkm = 1; } int kp = k + 1; if(kp < cols) { for(int j=kp;j 1) { float S = (float)1.0/Math.abs(Z[k]); FUtil.scal(cols,S,Z,1); ynorm *= S; } } float S = (float)1.0/FUtil.asum(cols,Z,1); FUtil.scal(cols,S,Z,1); ynorm *= S; return ynorm; } private void solveTransLY(float[] Z) { for(int k=cols-1;k>=0;k--) { if(k < cols-1) { Z[k] += this.dot(cols-k-1,1,k+1,k,Z,1,k+1); } if(Math.abs(Z[k]) > 1) { float S = (float)1.0/Math.abs(Z[k]); FUtil.scal(cols,S,Z,1); } int L = pivot[k] - 1; FUtil.swapElems(Z,L,k); } float S = (float)1.0/FUtil.asum(cols,Z,1); FUtil.scal(cols,S,Z,1); } float solveUZ(float[] Z, float ynorm) { for(int k=cols-1;k>=0;k--) { if(Math.abs(Z[k]) > Math.abs(this.Mat[k][k])) { float S = Math.abs(this.Mat[k][k])/Math.abs(Z[k]); FUtil.scal(cols,S,Z,1); ynorm *= S; } if(this.Mat[k][k] != 0) Z[k] = Z[k]/this.Mat[k][k]; else Z[k] = 1; this.axpy(k,-Z[k],1,0,k,Z,1,0); } float S = (float)1.0/FUtil.asum(cols,Z,1); FUtil.scal(cols,S,Z,1); ynorm *= S; return ynorm; } /*## ## this.solve solves the single precision system ## A * X = B or A.transpose() * X = B ## using the factors computed by this.condition or this.factor. ## ## On Entry ## ## this.Mat float[n][n] ## The output from this.condition or this.factor. ## ## n int ## The order of this.Mat. ## ## pivot int(n) ## The pivot vector from this.condition or this.factor. ## ## B float[n] ## The right hand side vector. ## ## Job int ## = 0 To solve A*X = B , ## = non-zero To solve A.transpose() * X = B ## ## On Return ## ## B The solution vector X. ## ## Error condition ## ## A division by zero will occur if the input factor contains a ## zero on the diagonal. Technically this indicates singularity ## but it is often caused by improper arguments or improper ## setting of n. It will not occur if the subroutines are ## called correctly and if this.condition has set ## LFloat.getFloat > 0 or this.factor threw an exception ## ## To compute A.inverse() * C where C is a FMatrix ## with p columns ## ## this.condition(); ## if (FRcond.Rcond > threshold) { ## for(int j=0;j=0;k--) { B[k] += this.dot(nm-k,1,k+1,k,B,1,k+1); int L = pivot[k] - 1; if(L != k) FUtil.swapElems(B,L,k); } } return; } /*## ## Solve U*X = Y ## This function is used by FPoFull ##*/ void solveUX(float[] B) { for(int k=cols-1;k>=0;k--) { B[k] /= this.Mat[k][k]; this.axpy(k,-B[k],1,0,k,B,1,0); } } /*## ## Solve U.transpose() * Y = B ## This function is used by FPoFull ##*/ void solveTransAX (float[] B){ for(int k=0;k 0 or this.factor has set info == 0. ##*/ public void inverse() { /*## ## Compute Inverse of U ##*/ this.invertU(); /*## ## Form inverse(U)*inverse(L) ##*/ if(cols > 1) this.invUinvL(); } void invertU () { for(int k=0;k=0;k--) { int kp = k+1; for(int i=kp;i