DIRECTORY LinearSystem, RealFns, Rope, SVD; SVDImpl: CEDAR PROGRAM IMPORTS RealFns EXPORTS SVD ~ BEGIN RowN: TYPE ~ LinearSystem.RowN; ColumnN: TYPE ~ LinearSystem.ColumnN; MatrixN: TYPE ~ LinearSystem.MatrixN; badShape: PUBLIC ERROR [msg: Rope.ROPE] ~ CODE; noConvergence: PUBLIC SIGNAL [msg: Rope.ROPE] ~ CODE; SVDecomp: PUBLIC PROC [a: MatrixN, m, n: INT, w: ColumnN, v: MatrixN] ~ { Pythag: PROC [a, b: REAL] RETURNS [REAL] ~ { at: REAL ~ ABS[a]; bt: REAL ~ ABS[b]; SELECT bt FROM < at => {ct: REAL ~ bt/at; RETURN[at*RealFns.SqRt[1.0 + ct*ct]]}; # 0.0 => {ct: REAL ~ at/bt; RETURN[bt*RealFns.SqRt[1.0 + ct*ct]]}; ENDCASE => RETURN[0.0]; }; Sign: PROC [a, b: REAL] RETURNS [REAL] ~ {IF b >= 0.0 THEN RETURN[ABS[a]] ELSE RETURN[-ABS[a]]}; nm, el, k, j, its, i, jj: INT; z, y, x, scale, s, h, g, f, c, aNorm: REAL; rv1: ColumnN ¬ NEW[LinearSystem.VecSeq[n]]; -- w, rv1 are diag, superdiag of reduced A IF m < n THEN ERROR badShape["You must add enough zero rows to make A square."]; g ¬ scale ¬ aNorm ¬ 0.0; FOR i IN [0..n) DO el ¬ i+1; -- Invariant until start diagonalizing reduction rv1[i] ¬ scale*g; -- Finish row reduction from bottom of this loop g ¬ s ¬ scale ¬ 0.0; IF i < m THEN { FOR k IN [i..m) DO scale ¬ scale + ABS[a[k][i]]; ENDLOOP; IF scale # 0.0 THEN { FOR k IN [i..m) DO a[k][i] ¬ a[k][i]/scale; s ¬ s + a[k][i]*a[k][i]; ENDLOOP; f ¬ a[i][i]; g ¬ -Sign[RealFns.SqRt[s], f]; h ¬ f*g - s; a[i][i] ¬ f - g; FOR j IN [el..n) DO s ¬ 0.0; FOR k IN [i..m) DO s ¬ s + a[k][i]*a[k][j]; ENDLOOP; f ¬ s/h; FOR k IN [i..m) DO a[k][j] ¬ a[k][j] + f*a[k][i]; ENDLOOP; ENDLOOP; FOR k IN [i..m) DO a[k][i] ¬ scale*a[k][i]; ENDLOOP; }; }; w[i] ¬ scale*g; g ¬ s ¬ scale ¬ 0.0; IF i < m AND i < n-1 THEN { FOR k IN [el..n) DO scale ¬ scale + ABS[a[i][k]] ENDLOOP; IF scale # 0.0 THEN { FOR k IN [el..n) DO a[i][k] ¬ a[i][k]/scale; s ¬ s + a[i][k]*a[i][k]; ENDLOOP; f ¬ a[i][el]; g ¬ -Sign[RealFns.SqRt[s], f]; h ¬ f*g - s; a[i][el] ¬ f - g; FOR k IN [el..n) DO rv1[k] ¬ a[i][k]/h; ENDLOOP; IF i < m-1 THEN { FOR j IN [el..m) DO s ¬ 0.0; FOR k IN [el..n) DO s ¬ s + a[j][k]*a[i][k]; ENDLOOP; FOR k IN [el..n) DO a[j][k] ¬ a[j][k] + s*rv1[k]; ENDLOOP; ENDLOOP; }; FOR k IN [el..n) DO a[i][k] ¬ scale*a[i][k]; ENDLOOP; }; }; aNorm ¬ MAX[aNorm, ABS[w[i]] + ABS[rv1[i]]]; ENDLOOP; FOR i DECREASING IN [0..n) DO IF i < n-1 THEN { IF g # 0.0 THEN { -- Double division to avoid possible underflow: FOR j IN [el..n) DO v[j][i] ¬ (a[i][j]/a[i][el])/g; ENDLOOP; FOR j IN [el..n) DO s ¬ 0.0; FOR k IN [el..n) DO s ¬ s + a[i][k]*v[k][j]; ENDLOOP; FOR k IN [el..n) DO v[k][j] ¬ v[k][j] + s*v[k][i]; ENDLOOP; ENDLOOP; }; FOR j IN [el..n) DO v[i][j] ¬ v[j][i] ¬ 0.0; ENDLOOP; }; v[i][i] ¬ 1.0; g ¬ rv1[i]; el ¬ i; ENDLOOP; FOR i DECREASING IN [0..n) DO el ¬ i+1; g ¬ w[i]; FOR j IN [el..n) DO a[i][j] ¬ 0.0; ENDLOOP; IF g # 0.0 THEN { g ¬ 1.0/g; FOR j IN [el..n) DO s ¬ 0.0; FOR k IN [el..m) DO s ¬ s + a[k][i]*a[k][j]; ENDLOOP; f ¬ (s/a[i][i])*g; FOR k IN [i..m) DO a[k][j] ¬ a[k][j] + f*a[k][i]; ENDLOOP; ENDLOOP; FOR j IN [i..m) DO a[j][i] ¬ a[j][i]*g; ENDLOOP; } ELSE {FOR j IN [i..m) DO a[j][i] ¬ 0.0; ENDLOOP}; a[i][i] ¬ a[i][i] + 1.0; ENDLOOP; FOR k DECREASING IN [0..n) DO -- Loop over singular values. FOR its IN [1..30] DO -- Loop over allowed iterations. FOR el DECREASING IN [0..k] DO -- Test for splitting: nm ¬ el-1; -- Note that rv1[0] is always zero. IF (ABS[rv1[el]] + aNorm) = aNorm THEN EXIT; -- Thus exit here, never fall through IF (ABS[w[nm]] + aNorm) = aNorm THEN GO TO SVNegligible; REPEAT SVNegligible => { -- Cancellation of rv1[el], if el > 0 : c ¬ 0.0; s ¬ 1.0; FOR i IN [el..k] DO f ¬ s*rv1[i]; IF (ABS[f] + aNorm) # aNorm THEN { g ¬ w[i]; h ¬ Pythag[f, g]; w[i] ¬ h; h ¬ 1.0/h; c ¬ g*h; s ¬ -(f*h); FOR j IN [0..m) DO y ¬ a[j][nm]; z ¬ a[j][i]; a[j][nm] ¬ y*c + z*s; a[j][i] ¬ z*c - y*s; ENDLOOP; }; ENDLOOP; }; ENDLOOP; z ¬ w[k]; IF el = k THEN { -- Convergence. IF z < 0.0 THEN { -- Singular value is made nonnegative. w[k] ¬ -z; FOR j IN [0..n) DO v[j][k] ¬ -v[j][k]; ENDLOOP; }; EXIT; -- No more iterations needed for this singular value. }; IF its = 30 THEN {SIGNAL noConvergence["No convergence in 30 SVDecomp iterations"]}; x ¬ w[el]; -- Shift from bottom 2-by-2 minor: nm ¬ k-1; y ¬ w[nm]; g ¬ rv1[nm]; h ¬ rv1[k]; f ¬ ((y-z)*(y+z) + (g-h)*(g+h))/(2.0*h*y); g ¬ Pythag[f, 1.0]; f ¬ ((x-z)*(x+z) + h*((y/(f+Sign[g, f])) - h))/x; c ¬ 1.0; s ¬ 1.0; FOR j IN [el..nm] DO i ¬ j+1; g ¬ rv1[i]; y ¬ w[i]; h ¬ s*g; g ¬ c*g; z ¬ Pythag[f, h]; rv1[j] ¬ z; c ¬ f/z; s ¬ h/z; f ¬ x*c + g*s; g ¬ g*c - x*s; h ¬ y*s; y ¬ y*c; FOR jj IN [0..n) DO x ¬ v[jj][j]; z ¬ v[jj][i]; v[jj][j] ¬ x*c + z*s; v[jj][i] ¬ z*c - x*s; ENDLOOP; z ¬ Pythag[f, h]; w[j] ¬ z; -- Rotation can be arbitrary if z = 0. IF z # 0.0 THEN {z ¬ 1.0/z; c ¬ f*z; s ¬ h*z}; f ¬ c*g + s*y; x ¬ c*y - s*g; FOR jj IN [0..m) DO y ¬ a[jj][j]; z ¬ a[jj][i]; a[jj][j] ¬ y*c + z*s; a[jj][i] ¬ z*c - y*s; ENDLOOP; ENDLOOP; rv1[el] ¬ 0.0; rv1[k] ¬ f; w[k] ¬ x; ENDLOOP; ENDLOOP; }; SVBackSub: PUBLIC PROC [u: MatrixN, w: ColumnN, v: MatrixN, m, n: INT, b: ColumnN, x: ColumnN] ~ { jj, j, i: INT; s: REAL; tmp: ColumnN ¬ NEW[LinearSystem.VecSeq[n]]; FOR j IN [0..n) DO -- Calculate UT B. s ¬ 0.0; IF w[j] # 0.0 THEN { -- Nonzero result only if wj is nonzero. FOR i IN [0..m) DO s ¬ s + u[i][j]*b[i]; ENDLOOP; s ¬ s/w[j]; -- This is the divide by wj. }; tmp[j] ¬ s; ENDLOOP; FOR j IN [0..n) DO -- Matrix multiply by V to get answer. s ¬ 0.0; FOR jj IN [0..n) DO s ¬ s + v[j][jj]*tmp[jj]; ENDLOOP; x[j] ¬ s; ENDLOOP; }; END.. DIMENSION A[np][np], U[np][np], W[np], V[np][np], B[np], X[np]; . . . FOR i IN [0..n) DO -- Copy A into U if you don't want it to be destroyed. FOR j IN [0..n) DO U[i][j] ¬ A[i][j]; ENDLOOP; ENDLOOP; SVDecomp[U, n, n, W, V]; -- SVD the square matrix A. wMax ¬ 0.0; -- Will be the maximum singular value obtained. FOR j IN [0..n) DO IF W[j] > wMax THEN wMax ¬ W[j]; ENDLOOP; wMin ¬ wMax*1.0E-6; -- This is where we set the threshold for singular values FOR j IN [0..n) DO -- allowed to be nonzero. The constant is typical, but not IF W[j] < wMin THEN W[j] ¬ 0.0; -- universal. You have to experiment with your own ENDLOOP; -- application. SVBackSub[U, W, V, n, n, B, X]; -- Now we can backsubstitute. ς SVDImpl.mesa Copyright Σ 1989, 1992 by Xerox Corporation. All rights reserved. Ken Shoemake, June 30, 1989 2:33:21 am PDT This code is derived from that in Numerical Recipes by Press, Flannery, Teukolsky, and Vetterling, Cambridge University Press, 1986, third 1987 printing. Chauser, January 28, 1992 12:03 pm PST Given matrix A[0..m)[0..n), this routine computes its singular value decomposition, A = U W VT . The matrix U replaces A on output. The diagonal matrix of singular values W is output as a vector W[0..n). The matrix V (not the transpose VT) is output as V[0..n)[0..n). The number of rows, m, must be greater or equal to the number of columns, n; if it is smaller, then a should be filled up to square with zero rows. Pythag computes SqRt[a*a + b*b] without destructive overflow or underflow. Householder reduction to bidiagonal form. Zero column i below diagonal Find norm of column i, avoiding underflow and overflow, for Householder reflection Apply reflection I-2uuT to remaining columns Zero row i beyond superdiagonal Find norm of row i, avoiding underflow and overflow, for Householder reflection Apply reflection I-2uuT to remaining rows Accumulation of right-hand transformations. Accumulation of left-hand transformations. Diagonalization of the bidiagonal form. Based on implicit QR reduction of symmetric tridiagonal BTB, for upper bidiagonal B Use Givens rotations to chase superdiag out of row Accumulate rotation in left-hand transformations Accumulate reflection in right-hand transformations Next QR transformation: Use alternating right- and left-hand Givens rotations to do implicit shift QR Rotate first two rows, then chase new off-bidiag non-zero back and forth 'til off matrix Rotate rows to zero below diagonal Accumulate Givens rotation in right-hand transformations Rotate columns to zero above superdiagonal Accumulate Givens rotation in left-hand transformations Solves A x = B for a vector X, where A is specified by the arrays U, W, V, as returned by SVDecomp. M and N are the logical dimensions of A, and will be equal for square matrices. B[0..m) is the input right-hand side. X[0..n) is the output solution vector. No input quantities are destroyed, so the routine may be called sequentially with different B's. Example of use Κ "•NewlineDelimiter –(cedarcode) style™codešœ ™ Kšœ Οeœ7™BK™*Kšœ#Οzœf™šK™&—K˜šΟk ˜ Kšœ!˜!—K˜šΠnxœŸœŸ˜KšŸœ˜KšŸœŸ˜ —KšœŸ˜˜KšœŸœ˜Kšœ Ÿœ˜%Kšœ Ÿœ˜%K˜Kš œ ŸœŸœ ŸœŸœ˜/Kš œŸœŸœ ŸœŸœ˜5K˜KšΟnœŸœŸœŸœ˜Ešœ]Οuœ’’œ²™£K˜š ‘œŸœŸœŸœŸœ˜(K™JK˜KšœŸœŸœ˜KšœŸœŸœ˜šŸœŸ˜Kšœ Ÿœ Ÿœ ˜AKšœŸœ Ÿœ ˜BKšŸœŸœ˜—K˜—š ‘œŸœŸœŸœŸœ˜&KšœŸœ ŸœŸœŸœŸœŸœŸœ˜9—KšœŸœ˜Kšœ&Ÿœ˜+KšœŸœΟc*˜VK˜KšŸœŸœŸœ=˜PK˜Kšœ)™)K˜šŸœŸœŸ˜Kšœ £0˜:Kšœ£0˜BKšœ™K˜šŸœŸœ˜Kš ŸœŸœŸœŸœ Ÿ œ˜;šŸœ Ÿœ˜KšœR™RKšŸœŸœŸœ6Ÿœ˜PK˜ K˜K˜ KšœΠouœ™,K˜šŸœŸœ Ÿ˜K˜KšŸœŸœŸœŸ œ˜6K˜KšŸœŸœŸœŸ œ˜šŸœŸœ Ÿ˜K˜KšŸœŸœ ŸœŸ œ˜7KšŸœŸœ ŸœŸ œ˜=KšŸœ˜—K˜—KšŸœŸœ ŸœŸ œ˜7K˜—K˜K˜ K˜KšŸœ˜—Kšœ*™*šŸœŸ œŸœŸ˜K˜ K˜ KšŸœŸœ ŸœŸ œ˜-šŸœ˜ šŸœ˜K˜ šŸœŸœ Ÿ˜K˜KšŸœŸœ ŸœŸ œ˜7K˜KšŸœŸœŸœŸ œ˜K™2K˜K˜šŸœŸœ Ÿ˜K˜ šŸœŸœŸœ˜"K˜ K˜K˜ K˜ K˜K˜ K™0šŸœŸœŸ˜K˜ K˜ K˜K˜KšŸœ˜—K˜—KšŸœ˜—K˜——KšŸœ˜—K˜ šŸœŸœ £˜&šŸœ Ÿœ £&˜>K˜ K™3KšŸœŸœŸœŸ œ˜1K˜—KšŸœ£5˜