internal static int ComputeSVD(GMatrix mat, GMatrix U, GMatrix W, GMatrix V) { int i; int j; int k; int nr; int nc; int si; int converged; int rank; double cs; double sn; double r; double mag; double scale; double t; int eLength; int sLength; int vecLength; GMatrix tmp = new GMatrix(mat.nRow, mat.nCol); GMatrix u = new GMatrix(mat.nRow, mat.nCol); GMatrix v = new GMatrix(mat.nRow, mat.nCol); GMatrix m = new GMatrix(mat); // compute the number of singular values if (m.nRow >= m.nCol) { sLength = m.nCol; eLength = m.nCol - 1; } else { sLength = m.nRow; eLength = m.nRow; } if (m.nRow > m.nCol) { vecLength = m.nRow; } else { vecLength = m.nCol; } double[] vec = new double[vecLength]; double[] single_values = new double[sLength]; double[] e = new double[eLength]; rank = 0; U.SetIdentity(); V.SetIdentity(); nr = m.nRow; nc = m.nCol; // householder reduction for (si = 0; si < sLength; si++) { // for each singular value if (nr > 1) { // zero out column // compute reflector mag = 0.0; for (i = 0; i < nr; i++) { mag += m.values[i + si][si] * m.values[i + si][si]; } mag = Math.Sqrt(mag); if (m.values[si][si] == 0.0) { vec[0] = mag; } else { vec[0] = m.values[si][si] + D_sign(mag, m.values[si][si]); } for (i = 1; i < nr; i++) { vec[i] = m.values[si + i][si]; } scale = 0.0; for (i = 0; i < nr; i++) { scale += vec[i] * vec[i]; } scale = 2.0 / scale; for (j = si; j < m.nRow; j++) { for (k = si; k < m.nRow; k++) { u.values[j][k] = -scale * vec[j - si] * vec[k - si]; } } for (i = si; i < m.nRow; i++) { u.values[i][i] += 1.0; } // compute s t = 0.0; for (i = si; i < m.nRow; i++) { t += u.values[si][i] * m.values[i][si]; } m.values[si][si] = t; // apply reflector for (j = si; j < m.nRow; j++) { for (k = si + 1; k < m.nCol; k++) { tmp.values[j][k] = 0.0; for (i = si; i < m.nCol; i++) { tmp.values[j][k] += u.values[j][i] * m.values[i][k]; } } } for (j = si; j < m.nRow; j++) { for (k = si + 1; k < m.nCol; k++) { m.values[j][k] = tmp.values[j][k]; } } // update U matrix for (j = si; j < m.nRow; j++) { for (k = 0; k < m.nCol; k++) { tmp.values[j][k] = 0.0; for (i = si; i < m.nCol; i++) { tmp.values[j][k] += u.values[j][i] * U.values[i][k]; } } } for (j = si; j < m.nRow; j++) { for (k = 0; k < m.nCol; k++) { U.values[j][k] = tmp.values[j][k]; } } nr--; } if (nc > 2) { // zero out row mag = 0.0; for (i = 1; i < nc; i++) { mag += m.values[si][si + i] * m.values[si][si + i]; } // generate the reflection vector, compute the first entry and // copy the rest from the row to be zeroed mag = Math.Sqrt(mag); if (m.values[si][si + 1] == 0.0) { vec[0] = mag; } else { vec[0] = m.values[si][si + 1] + D_sign(mag, m.values[si][si + 1]); } for (i = 1; i < nc - 1; i++) { vec[i] = m.values[si][si + i + 1]; } // use reflection vector to compute v matrix scale = 0.0; for (i = 0; i < nc - 1; i++) { scale += vec[i] * vec[i]; } scale = 2.0 / scale; for (j = si + 1; j < nc; j++) { for (k = si + 1; k < m.nCol; k++) { v.values[j][k] = -scale * vec[j - si - 1] * vec[k - si - 1]; } } for (i = si + 1; i < m.nCol; i++) { v.values[i][i] += 1.0; } t = 0.0; for (i = si; i < m.nCol; i++) { t += v.values[i][si + 1] * m.values[si][i]; } m.values[si][si + 1] = t; // apply reflector for (j = si + 1; j < m.nRow; j++) { for (k = si + 1; k < m.nCol; k++) { tmp.values[j][k] = 0.0; for (i = si + 1; i < m.nCol; i++) { tmp.values[j][k] += v.values[i][k] * m.values[j][i]; } } } for (j = si + 1; j < m.nRow; j++) { for (k = si + 1; k < m.nCol; k++) { m.values[j][k] = tmp.values[j][k]; } } // update V matrix for (j = 0; j < m.nRow; j++) { for (k = si + 1; k < m.nCol; k++) { tmp.values[j][k] = 0.0; for (i = si + 1; i < m.nCol; i++) { tmp.values[j][k] += v.values[i][k] * V.values[j][i]; } } } for (j = 0; j < m.nRow; j++) { for (k = si + 1; k < m.nCol; k++) { V.values[j][k] = tmp.values[j][k]; } } nc--; } } for (i = 0; i < sLength; i++) { single_values[i] = m.values[i][i]; } for (i = 0; i < eLength; i++) { e[i] = m.values[i][i + 1]; } // Fix ArrayIndexOutOfBounds for 2x2 matrices, which partially // addresses bug 4348562 for J3D 1.2.1. // // Does *not* fix the following problems reported in 4348562, // which will wait for J3D 1.3: // // 1) no output of W // 2) wrong transposition of U // 3) wrong results for 4x4 matrices // 4) slow performance if (m.nRow == 2 && m.nCol == 2) { double[] cosl = new double[1]; double[] cosr = new double[1]; double[] sinl = new double[1]; double[] sinr = new double[1]; Compute_2X2(single_values[0], e[0], single_values[1], single_values, sinl, cosl, sinr, cosr, 0); Update_u(0, U, cosl, sinl); Update_v(0, V, cosr, sinr); return 2; } // compute_qr causes ArrayIndexOutOfBounds for 2x2 matrices Compute_qr(0, e.Length - 1, single_values, e, U, V); // compute rank = number of non zero singular values rank = single_values.Length; // sort by order of size of single values // and check for zero's return rank; }
private static void Print_svd(double[] s, double[] e, GMatrix u, GMatrix v) { int i; GMatrix mtmp = new GMatrix(u.nCol, v.nRow); System.Console.Out.WriteLine(" \ns = "); for (i = 0; i < s.Length; i++) { System.Console.Out.WriteLine(" " + s[i]); } System.Console.Out.WriteLine(" \ne = "); for (i = 0; i < e.Length; i++) { System.Console.Out.WriteLine(" " + e[i]); } System.Console.Out.WriteLine(" \nu = \n" + u.ToString()); System.Console.Out.WriteLine(" \nv = \n" + v.ToString()); mtmp.SetIdentity(); for (i = 0; i < s.Length; i++) { mtmp.values[i][i] = s[i]; } for (i = 0; i < e.Length; i++) { mtmp.values[i][i + 1] = e[i]; } System.Console.Out.WriteLine(" \nm = \n" + mtmp.ToString()); mtmp.MulTransposeLeft(u, mtmp); mtmp.MulTransposeRight(mtmp, v); System.Console.Out.WriteLine(" \n u.transpose*m*v.transpose = \n" + mtmp.ToString ()); }
/// <summary> /// Finds the singular value decomposition (SVD) of this matrix /// such that this = U*W*transpose(V); and returns the rank of /// this matrix; the values of U,W,V are all overwritten. /// </summary> /// <remarks> /// Finds the singular value decomposition (SVD) of this matrix /// such that this = U*W*transpose(V); and returns the rank of /// this matrix; the values of U,W,V are all overwritten. Note /// that the matrix V is output as V, and /// not transpose(V). If this matrix is mxn, then U is mxm, W /// is a diagonal matrix that is mxn, and V is nxn. Using the /// notation W = diag(w), then the inverse of this matrix is: /// inverse(this) = V*diag(1/w)*tranpose(U), where diag(1/w) /// is the same matrix as W except that the reciprocal of each /// of the diagonal components is used. /// </remarks> /// <param name="U">The computed U matrix in the equation this = U*W*transpose(V)</param> /// <param name="W">The computed W matrix in the equation this = U*W*transpose(V)</param> /// <param name="V">The computed V matrix in the equation this = U*W*transpose(V)</param> /// <returns>The rank of this matrix.</returns> public int Svd(GMatrix U, GMatrix W, GMatrix V) { // check for consistancy in dimensions if (nCol != V.nCol || nCol != V.nRow) { throw new MismatchedSizeException("GMatrix.SVD: dimension mismatch with V matrix"); } if (nRow != U.nRow || nRow != U.nCol) { throw new MismatchedSizeException("GMatrix.SVD: dimension mismatch with U matrix"); } if (nRow != W.nRow || nCol != W.nCol) { throw new MismatchedSizeException("GMatrix.SVD: dimension mismatch with W matrix"); } // Fix ArrayIndexOutOfBounds for 2x2 matrices, which partially // addresses bug 4348562 for J3D 1.2.1. // // Does *not* fix the following problems reported in 4348562, // which will wait for J3D 1.3: // // 1) no output of W // 2) wrong transposition of U // 3) wrong results for 4x4 matrices // 4) slow performance if (nRow == 2 && nCol == 2) { if (values[1][0] == 0.0) { U.SetIdentity(); V.SetIdentity(); if (values[0][1] == 0.0) { return 2; } double[] sinl = new double[1]; double[] sinr = new double[1]; double[] cosl = new double[1]; double[] cosr = new double[1]; double[] single_values = new double[2]; single_values[0] = values[0][0]; single_values[1] = values[1][1]; Compute_2X2(values[0][0], values[0][1], values[1][1], single_values, sinl, cosl, sinr, cosr, 0); Update_u(0, U, cosl, sinl); Update_v(0, V, cosr, sinr); return 2; } } // else call computeSVD() and check for 2x2 there return ComputeSVD(this, U, W, V); }