// ** SVD ** public static void ExtractSingularValues(double[] a, double[] b, double[] uStore, double[] vStore, int rows, int cols) { // n is the upper limit index int n = cols - 1; int count = 0; while (count < 32) { // move the lower boundary up as far as we can while ((n > 0) && (Math.Abs(b[n - 1]) <= (2.0E-16) * Math.Abs(a[n - 1] + a[n]))) { b[n - 1] = 0.0; if (a[n] < 0.0) { a[n] = -a[n]; if (vStore != null) { Blas1.dScal(-1.0, vStore, n * cols, 1, cols); } } n--; count = 0; } if (n == 0) { return; } // p is the upper limit index // find any zeros on the diagonal and reduce the problem int p = n; while (p > 0) { if (Math.Abs(b[p - 1]) <= (2.0E-16) * (Math.Abs(a[p]) + Math.Abs(a[p - 1]))) { break; } p--; // do we need a better zero test? if (a[p] == 0.0) { // ChaseBidiagonalZero(a, b, p, n, uStore, rows); p++; break; } } // diagonal zeros will cause the Golulb-Kahn step to reproduce the same matrix, // and eventually we will fail with non-convergence GolubKahn(a, b, p, n, uStore, vStore, rows, cols); count++; } throw new NonconvergenceException(); }
// A Householder reflection matrix is a rank-1 update to the identity. // P = I - b v v^T // Unitarity requires b = 2 / |v|^2. To anihilate all but the first components of vector x, // P x = a e_1 // we must have // v = x +/- |x| e_1 // that is, all elements the same except the first, from which we have either added or subtracted |x|. This makes // a = -/+ |x| // There are two way to handle the sign. One is to simply choose the sign that avoids cancelation when calculating v_1, // i.e. + for positive x_1 and - for negative x_1. This works fine, but makes a negative for positive x_1, which is // weird-looking (1 0 0 gets turned into -1 0 0). An alternative is to choose the negative sign even for positive x_1, // but to avoid cancelation write // v_1 = x_1 - |x| = ( x_1 - |x| ) ( x_1 + |x|) / ( x_1 + |x|) = ( x_1^2 - |x|^2 ) / ( x_1 + |x| ) // = - ( x_2^2 + \cdots + x_n^2 ) / ( x_1 + |x| ) // We have now moved to the second method. Note that v is the same as x except for the first element. public static void GenerateHouseholderReflection(double[] store, int offset, int stride, int count, out double a) { double x0 = store[offset]; // Compute |x| and u_0. double xm, u0; if (x0 < 0.0) { xm = Blas1.dNrm2(store, offset, stride, count); u0 = x0 - xm; } else { // This method of computing ym and xm does incur and extra square root compared to naively computing x_2 + \cdots + x_n^2, // but doing it this way allows us to use dNrm2's over/under-flow prevention when we have large/small elements. double ym = Blas1.dNrm2(store, offset + stride, stride, count - 1); xm = MoreMath.Hypot(x0, ym); // Writing ym / (x0 + xm) * ym instead of ym * ym / (x0 + xm) prevents over/under-flow for large/small ym. Note this will // be 0 / 0 = NaN when xm = 0. u0 = -ym / (x0 + xm) * ym; } // Set result element. a = xm; // If |x| = 0 there is nothing to do; we could have done this a little earlier but the compiler requires us to set a before returning. if (xm == 0.0) { return; } // Set the new value of u_0 store[offset] = u0; // Rescale to make b = 1. double um = Math.Sqrt(xm * Math.Abs(u0)); if (um > 0.0) { Blas1.dScal(1.0 / um, store, offset, stride, count); } }
// on input: // store contains the matrix in column-major order (must have the length dimension^2) // permutation contains the row permutation (typically 0, 1, 2, ..., dimension - 1; must have the length dimension^2) // parity contains the parity of the row permutation (typically 1; must be 1 or -1) // dimension contains the dimension of the matrix (must be non-negative) // on output: // store is replaced by the L and U matrices, L in the lower-left triangle (with 1s along the diagonal), U in the upper-right triangle // permutation is replaced by the row permutation, and parity be the parity of that permutation // A = PLU public static void LUDecompose(double[] store, int[] permutation, ref int parity, int dimension) { for (int d = 0; d < dimension; d++) { int pivotRow = -1; double pivotValue = 0.0; for (int r = d; r < dimension; r++) { int a0 = dimension * d + r; double t = store[a0] - Blas1.dDot(store, r, dimension, store, dimension * d, 1, d); store[a0] = t; if (Math.Abs(t) > Math.Abs(pivotValue)) { pivotRow = r; pivotValue = t; } } if (pivotValue == 0.0) { throw new DivideByZeroException(); } if (pivotRow != d) { // switch rows Blas1.dSwap(store, d, dimension, store, pivotRow, dimension, dimension); int t = permutation[pivotRow]; permutation[pivotRow] = permutation[d]; permutation[d] = t; parity = -parity; } Blas1.dScal(1.0 / pivotValue, store, dimension * d + d + 1, 1, dimension - d - 1); for (int c = d + 1; c < dimension; c++) { double t = Blas1.dDot(store, d, dimension, store, dimension * c, 1, d); store[dimension * c + d] -= t; } } }
// inverts the matrix in place // the in-place-ness makes this a bit confusing public static void GaussJordanInvert(double[] store, int dimension) { // keep track of row exchanges int[] ps = new int[dimension]; // iterate over dimensions for (int k = 0; k < dimension; k++) { // look for a pivot in the kth column on any lower row int p = k; double q = MatrixAlgorithms.GetEntry(store, dimension, dimension, k, k); for (int r = k + 1; r < dimension; r++) { double s = MatrixAlgorithms.GetEntry(store, dimension, dimension, r, k); if (Math.Abs(s) > Math.Abs(q)) { p = r; q = s; } } ps[k] = p; // if no non-zero pivot is found, the matrix is singular and cannot be inverted if (q == 0.0) { throw new DivideByZeroException(); } // if the best pivot was on a lower row, swap it into the kth row if (p != k) { Blas1.dSwap(store, k, dimension, store, p, dimension, dimension); } // divide the pivot row by the pivot element, so the diagonal element becomes unity MatrixAlgorithms.SetEntry(store, dimension, dimension, k, k, 1.0); Blas1.dScal(1.0 / q, store, k, dimension, dimension); // add factors to the pivot row to zero all off-diagonal elements in the kth column for (int r = 0; r < dimension; r++) { if (r == k) { continue; } double a = MatrixAlgorithms.GetEntry(store, dimension, dimension, r, k); MatrixAlgorithms.SetEntry(store, dimension, dimension, r, k, 0.0); Blas1.dAxpy(-a, store, k, dimension, store, r, dimension, dimension); } } // unscramble exchanges for (int k = dimension - 1; k >= 0; k--) { int p = ps[k]; if (p != k) { Blas1.dSwap(store, dimension * p, 1, store, dimension * k, 1, dimension); } } }