/// <summary> /// Solves a system of linear equations, <b>Ax = b</b>, with A SVD factorized. /// </summary> /// <param name="input">The right hand side vector, <b>b</b>.</param> /// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>x</b>.</param> public override void Solve(Vector <Complex32> input, Vector <Complex32> result) { if (input == null) { throw new ArgumentNullException("input"); } if (result == null) { throw new ArgumentNullException("result"); } if (!ComputeVectors) { throw new InvalidOperationException(Resources.SingularVectorsNotComputed); } // Ax=b where A is an m x n matrix // Check that b is a column vector with m entries if (MatrixU.RowCount != input.Count) { throw new ArgumentException(Resources.ArgumentVectorsSameLength); } // Check that x is a column vector with n entries if (MatrixVT.ColumnCount != result.Count) { throw Matrix.DimensionsDontMatch <ArgumentException>(MatrixVT, result); } var mn = Math.Min(MatrixU.RowCount, MatrixVT.ColumnCount); var tmp = new Complex32[MatrixVT.ColumnCount]; for (var j = 0; j < MatrixVT.ColumnCount; j++) { var value = Complex32.Zero; if (j < mn) { for (var i = 0; i < MatrixU.RowCount; i++) { value += MatrixU.At(i, j).Conjugate() * input[i]; } value /= VectorS[j]; } tmp[j] = value; } for (var j = 0; j < MatrixVT.ColumnCount; j++) { var value = Complex32.Zero; for (var i = 0; i < MatrixVT.ColumnCount; i++) { value += MatrixVT.At(i, j).Conjugate() * tmp[i]; } result[j] = value; } }
/// <summary> /// Solves a system of linear equations, <b>Ax = b</b>, with A SVD factorized. /// </summary> /// <param name="input">The right hand side vector, <b>b</b>.</param> /// <returns>The left hand side <see cref="Vector{T}"/>, <b>x</b>.</returns> public virtual Vector <T> Solve(Vector <T> input) { // Check for proper arguments. if (input == null) { throw new ArgumentNullException("input"); } if (!ComputeVectors) { throw new InvalidOperationException(Resources.SingularVectorsNotComputed); } var x = MatrixU.CreateVector(MatrixVT.ColumnCount); Solve(input, x); return(x); }
/// <summary>Returns the singular values as a diagonal <see cref="Matrix{T}"/>.</summary> /// <returns>The singular values as a diagonal <see cref="Matrix{T}"/>.</returns> public Matrix <T> W() { var rows = MatrixU.RowCount; var columns = MatrixVT.ColumnCount; var result = MatrixU.CreateMatrix(rows, columns); for (var i = 0; i < rows; i++) { for (var j = 0; j < columns; j++) { if (i == j) { result.At(i, i, VectorS[i]); } } } return(result); }
/// <summary> /// Solves a system of linear equations, <b>AX = B</b>, with A SVD factorized. /// </summary> /// <param name="input">The right hand side <see cref="Matrix{T}"/>, <b>B</b>.</param> /// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>X</b>.</param> public override void Solve(Matrix <float> input, Matrix <float> result) { // Check for proper arguments. if (input == null) { throw new ArgumentNullException("input"); } if (result == null) { throw new ArgumentNullException("result"); } if (!ComputeVectors) { throw new InvalidOperationException(Resources.SingularVectorsNotComputed); } // The solution X should have the same number of columns as B if (input.ColumnCount != result.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); } // The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows if (MatrixU.RowCount != input.RowCount) { throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension); } // The solution X row dimension is equal to the column dimension of A if (MatrixVT.ColumnCount != result.RowCount) { throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); } var mn = Math.Min(MatrixU.RowCount, MatrixVT.ColumnCount); var bn = input.ColumnCount; var tmp = new float[MatrixVT.ColumnCount]; for (var k = 0; k < bn; k++) { for (var j = 0; j < MatrixVT.ColumnCount; j++) { float value = 0; if (j < mn) { for (var i = 0; i < MatrixU.RowCount; i++) { value += MatrixU.At(i, j) * input.At(i, k); } value /= VectorS[j]; } tmp[j] = value; } for (var j = 0; j < MatrixVT.ColumnCount; j++) { float value = 0; for (var i = 0; i < MatrixVT.ColumnCount; i++) { value += MatrixVT.At(i, j) * tmp[i]; } result[j, k] = value; } } }
/// <summary> /// Initializes a new instance of the <see cref="UserSvd"/> class. This object will compute the /// the singular value decomposition when the constructor is called and cache it's decomposition. /// </summary> /// <param name="matrix">The matrix to factor.</param> /// <param name="computeVectors">Compute the singular U and VT vectors or not.</param> /// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception> /// <exception cref="ArgumentException">If SVD algorithm failed to converge with matrix <paramref name="matrix"/>.</exception> public UserSvd(Matrix <float> matrix, bool computeVectors) { if (matrix == null) { throw new ArgumentNullException("matrix"); } ComputeVectors = computeVectors; var nm = Math.Min(matrix.RowCount + 1, matrix.ColumnCount); var matrixCopy = matrix.Clone(); VectorS = matrixCopy.CreateVector(nm); MatrixU = matrixCopy.CreateMatrix(matrixCopy.RowCount, matrixCopy.RowCount); MatrixVT = matrixCopy.CreateMatrix(matrixCopy.ColumnCount, matrixCopy.ColumnCount); const int Maxiter = 1000; var e = new float[matrixCopy.ColumnCount]; var work = new float[matrixCopy.RowCount]; int i, j; int l, lp1; var cs = 0.0f; var sn = 0.0f; float t; var ncu = matrixCopy.RowCount; // Reduce matrixCopy to bidiagonal form, storing the diagonal elements // In s and the super-diagonal elements in e. var nct = Math.Min(matrixCopy.RowCount - 1, matrixCopy.ColumnCount); var nrt = Math.Max(0, Math.Min(matrixCopy.ColumnCount - 2, matrixCopy.RowCount)); var lu = Math.Max(nct, nrt); for (l = 0; l < lu; l++) { lp1 = l + 1; if (l < nct) { // Compute the transformation for the l-th column and place the l-th diagonal in VectorS[l]. var xnorm = Dnrm2Column(matrixCopy, matrixCopy.RowCount, l, l); VectorS[l] = xnorm; if (VectorS[l] != 0.0) { if (matrixCopy.At(l, l) != 0.0) { VectorS[l] = Dsign(VectorS[l], matrixCopy.At(l, l)); } DscalColumn(matrixCopy, matrixCopy.RowCount, l, l, 1.0f / VectorS[l]); matrixCopy.At(l, l, (1.0f + matrixCopy.At(l, l))); } VectorS[l] = -VectorS[l]; } for (j = lp1; j < matrixCopy.ColumnCount; j++) { if (l < nct) { if (VectorS[l] != 0.0) { // Apply the transformation. t = -Ddot(matrixCopy, matrixCopy.RowCount, l, j, l) / matrixCopy.At(l, l); for (var ii = l; ii < matrixCopy.RowCount; ii++) { matrixCopy.At(ii, j, matrixCopy.At(ii, j) + (t * matrixCopy.At(ii, l))); } } } // Place the l-th row of matrixCopy into e for the // Subsequent calculation of the row transformation. e[j] = matrixCopy.At(l, j); } if (ComputeVectors && l < nct) { // Place the transformation in u for subsequent back multiplication. for (i = l; i < matrixCopy.RowCount; i++) { MatrixU.At(i, l, matrixCopy.At(i, l)); } } if (l >= nrt) { continue; } // Compute the l-th row transformation and place the l-th super-diagonal in e(l). var enorm = Dnrm2Vector(e, lp1); e[l] = enorm; if (e[l] != 0.0) { if (e[lp1] != 0.0) { e[l] = Dsign(e[l], e[lp1]); } DscalVector(e, lp1, 1.0f / e[l]); e[lp1] = 1.0f + e[lp1]; } e[l] = -e[l]; if (lp1 < matrixCopy.RowCount && e[l] != 0.0) { // Apply the transformation. for (i = lp1; i < matrixCopy.RowCount; i++) { work[i] = 0.0f; } for (j = lp1; j < matrixCopy.ColumnCount; j++) { for (var ii = lp1; ii < matrixCopy.RowCount; ii++) { work[ii] += e[j] * matrixCopy.At(ii, j); } } for (j = lp1; j < matrixCopy.ColumnCount; j++) { var ww = -e[j] / e[lp1]; for (var ii = lp1; ii < matrixCopy.RowCount; ii++) { matrixCopy.At(ii, j, matrixCopy.At(ii, j) + (ww * work[ii])); } } } if (ComputeVectors) { // Place the transformation in v for subsequent back multiplication. for (i = lp1; i < matrixCopy.ColumnCount; i++) { MatrixVT.At(i, l, e[i]); } } } // Set up the final bidiagonal matrixCopy or order m. var m = Math.Min(matrixCopy.ColumnCount, matrixCopy.RowCount + 1); var nctp1 = nct + 1; var nrtp1 = nrt + 1; if (nct < matrixCopy.ColumnCount) { VectorS[nctp1 - 1] = matrixCopy.At((nctp1 - 1), (nctp1 - 1)); } if (matrixCopy.RowCount < m) { VectorS[m - 1] = 0.0f; } if (nrtp1 < m) { e[nrtp1 - 1] = matrixCopy.At((nrtp1 - 1), (m - 1)); } e[m - 1] = 0.0f; // If required, generate u. if (ComputeVectors) { for (j = nctp1 - 1; j < ncu; j++) { for (i = 0; i < matrixCopy.RowCount; i++) { MatrixU.At(i, j, 0.0f); } MatrixU.At(j, j, 1.0f); } for (l = nct - 1; l >= 0; l--) { if (VectorS[l] != 0.0) { for (j = l + 1; j < ncu; j++) { t = -Ddot(MatrixU, matrixCopy.RowCount, l, j, l) / MatrixU.At(l, l); for (var ii = l; ii < matrixCopy.RowCount; ii++) { MatrixU.At(ii, j, MatrixU.At(ii, j) + (t * MatrixU.At(ii, l))); } } DscalColumn(MatrixU, matrixCopy.RowCount, l, l, -1.0f); MatrixU.At(l, l, 1.0f + MatrixU.At(l, l)); for (i = 0; i < l; i++) { MatrixU.At(i, l, 0.0f); } } else { for (i = 0; i < matrixCopy.RowCount; i++) { MatrixU.At(i, l, 0.0f); } MatrixU.At(l, l, 1.0f); } } } // If it is required, generate v. if (ComputeVectors) { for (l = matrixCopy.ColumnCount - 1; l >= 0; l--) { lp1 = l + 1; if (l < nrt) { if (e[l] != 0.0) { for (j = lp1; j < matrixCopy.ColumnCount; j++) { t = -Ddot(MatrixVT, matrixCopy.ColumnCount, l, j, lp1) / MatrixVT.At(lp1, l); for (var ii = l; ii < matrixCopy.ColumnCount; ii++) { MatrixVT.At(ii, j, MatrixVT.At(ii, j) + (t * MatrixVT.At(ii, l))); } } } } for (i = 0; i < matrixCopy.ColumnCount; i++) { MatrixVT.At(i, l, 0.0f); } MatrixVT.At(l, l, 1.0f); } } // Transform s and e so that they are float . for (i = 0; i < m; i++) { float r; if (VectorS[i] != 0.0) { t = VectorS[i]; r = VectorS[i] / t; VectorS[i] = t; if (i < m - 1) { e[i] = e[i] / r; } if (ComputeVectors) { DscalColumn(MatrixU, matrixCopy.RowCount, i, 0, r); } } // Exit if (i == m - 1) { break; } if (e[i] != 0.0) { t = e[i]; r = t / e[i]; e[i] = t; VectorS[i + 1] = VectorS[i + 1] * r; if (ComputeVectors) { DscalColumn(MatrixVT, matrixCopy.ColumnCount, i + 1, 0, r); } } } // Main iteration loop for the singular values. var mn = m; var iter = 0; while (m > 0) { // Quit if all the singular values have been found. If too many iterations have been performed, // throw exception that Convergence Failed if (iter >= Maxiter) { throw new ArgumentException(Resources.ConvergenceFailed); } // This section of the program inspects for negligible elements in the s and e arrays. On // completion the variables kase and l are set as follows. // Kase = 1 if VectorS[m] and e[l-1] are negligible and l < m // Kase = 2 if VectorS[l] is negligible and l < m // Kase = 3 if e[l-1] is negligible, l < m, and VectorS[l, ..., VectorS[m] are not negligible (qr step). // Лase = 4 if e[m-1] is negligible (convergence). float ztest; float test; for (l = m - 2; l >= 0; l--) { test = Math.Abs(VectorS[l]) + Math.Abs(VectorS[l + 1]); ztest = test + Math.Abs(e[l]); if (ztest.AlmostEqualInDecimalPlaces(test, 7)) { e[l] = 0.0f; break; } } int kase; if (l == m - 2) { kase = 4; } else { int ls; for (ls = m - 1; ls > l; ls--) { test = 0.0f; if (ls != m - 1) { test = test + Math.Abs(e[ls]); } if (ls != l + 1) { test = test + Math.Abs(e[ls - 1]); } ztest = test + Math.Abs(VectorS[ls]); if (ztest.AlmostEqualInDecimalPlaces(test, 7)) { VectorS[ls] = 0.0f; break; } } if (ls == l) { kase = 3; } else if (ls == m - 1) { kase = 1; } else { kase = 2; l = ls; } } l = l + 1; // Perform the task indicated by kase. int k; float f; switch (kase) { // Deflate negligible VectorS[m]. case 1: f = e[m - 2]; e[m - 2] = 0.0f; float t1; for (var kk = l; kk < m - 1; kk++) { k = m - 2 - kk + l; t1 = VectorS[k]; Drotg(ref t1, ref f, ref cs, ref sn); VectorS[k] = t1; if (k != l) { f = -sn * e[k - 1]; e[k - 1] = cs * e[k - 1]; } if (ComputeVectors) { Drot(MatrixVT, matrixCopy.ColumnCount, k, m - 1, cs, sn); } } break; // Split at negligible VectorS[l]. case 2: f = e[l - 1]; e[l - 1] = 0.0f; for (k = l; k < m; k++) { t1 = VectorS[k]; Drotg(ref t1, ref f, ref cs, ref sn); VectorS[k] = t1; f = -sn * e[k]; e[k] = cs * e[k]; if (ComputeVectors) { Drot(MatrixU, matrixCopy.RowCount, k, l - 1, cs, sn); } } break; // Perform one qr step. case 3: // Calculate the shift. var scale = 0.0f; scale = Math.Max(scale, Math.Abs(VectorS[m - 1])); scale = Math.Max(scale, Math.Abs(VectorS[m - 2])); scale = Math.Max(scale, Math.Abs(e[m - 2])); scale = Math.Max(scale, Math.Abs(VectorS[l])); scale = Math.Max(scale, Math.Abs(e[l])); var sm = VectorS[m - 1] / scale; var smm1 = VectorS[m - 2] / scale; var emm1 = e[m - 2] / scale; var sl = VectorS[l] / scale; var el = e[l] / scale; var b = (((smm1 + sm) * (smm1 - sm)) + (emm1 * emm1)) / 2.0f; var c = (sm * emm1) * (sm * emm1); var shift = 0.0f; if (b != 0.0 || c != 0.0) { shift = (float)Math.Sqrt((b * b) + c); if (b < 0.0) { shift = -shift; } shift = c / (b + shift); } f = ((sl + sm) * (sl - sm)) + shift; var g = sl * el; // Chase zeros. for (k = l; k < m - 1; k++) { Drotg(ref f, ref g, ref cs, ref sn); if (k != l) { e[k - 1] = f; } f = (cs * VectorS[k]) + (sn * e[k]); e[k] = (cs * e[k]) - (sn * VectorS[k]); g = sn * VectorS[k + 1]; VectorS[k + 1] = cs * VectorS[k + 1]; if (ComputeVectors) { Drot(MatrixVT, matrixCopy.ColumnCount, k, k + 1, cs, sn); } Drotg(ref f, ref g, ref cs, ref sn); VectorS[k] = f; f = (cs * e[k]) + (sn * VectorS[k + 1]); VectorS[k + 1] = (-sn * e[k]) + (cs * VectorS[k + 1]); g = sn * e[k + 1]; e[k + 1] = cs * e[k + 1]; if (ComputeVectors && k < matrixCopy.RowCount) { Drot(MatrixU, matrixCopy.RowCount, k, k + 1, cs, sn); } } e[m - 2] = f; iter = iter + 1; break; // Convergence. case 4: // Make the singular value positive if (VectorS[l] < 0.0) { VectorS[l] = -VectorS[l]; if (ComputeVectors) { DscalColumn(MatrixVT, matrixCopy.ColumnCount, l, 0, -1.0f); } } // Order the singular value. while (l != mn - 1) { if (VectorS[l] >= VectorS[l + 1]) { break; } t = VectorS[l]; VectorS[l] = VectorS[l + 1]; VectorS[l + 1] = t; if (ComputeVectors && l < matrixCopy.ColumnCount) { Dswap(MatrixVT, matrixCopy.ColumnCount, l, l + 1); } if (ComputeVectors && l < matrixCopy.RowCount) { Dswap(MatrixU, matrixCopy.RowCount, l, l + 1); } l = l + 1; } iter = 0; m = m - 1; break; } } if (ComputeVectors) { MatrixVT = MatrixVT.Transpose(); } // Adjust the size of s if rows < columns. We are using ported copy of linpack's svd code and it uses // a singular vector of length mRows+1 when mRows < mColumns. The last element is not used and needs to be removed. // we should port lapack's svd routine to remove this problem. if (matrixCopy.RowCount < matrixCopy.ColumnCount) { nm--; var tmp = matrixCopy.CreateVector(nm); for (i = 0; i < nm; i++) { tmp[i] = VectorS[i]; } VectorS = tmp; } }
/// <summary>Returns the left singular vectors as a <see cref="Matrix{T}"/>.</summary> /// <returns>The left singular vectors. The matrix will be <c>null</c>, if <b>computeVectors</b> in the constructor is set to <c>false</c>.</returns> public Matrix <T> U() { return(ComputeVectors ? MatrixU.Clone() : null); }