// more complicated operations: also O(N^3) // QR decomposition write A = Q R, where Q is orthogonal (Q^T = Q^{-1}) and R is upper-right triangular. // Given a QR decomposition, it is easy to solve Ax=b by writing (QR)x=b, Q(Rx)=b, Rx=y and Qy=b. // Then y = Q^T b (an N^2 multiplication) and R x = y is a triangular system (also N^2 to solve). // To form a QR decomposition, we apply householder reflections to zero each column in turn. // XXXXX ABCDE XXXXX XXXXX XXXXX XXXXX // XXXXX 0BCDE 0ABCD 0XXXX 0XXXX 0XXXX // XXXXX 0BCDE 00BCD 00ABC 00XXX 00XXX // XXXXX -> 0BCDE -> 00BCD -> 000BC -> 000AB -> 000XX // XXXXX 0BCDE 00BCD 000BC 0000B 0000A // XXXXX 0BCDE 00BCD 000BC 0000B 00000 // XXXXX 0BCDE 00BCD 000BC 0000B 00000 // This gives us (Q_N \cdots Q_2 Q_1) A = R, so A = (Q_N \cdots Q_2 Q_1)^T R = (Q_1^T Q_2^T \cdots Q_N^T) R public static void QRDecompose(double[] store, double[] qtStore, int rows, int cols) { // loop over columns for (int k = 0; k < cols; k++) { int offset = rows * k + k; int length = rows - k; double a; VectorAlgorithms.GenerateHouseholderReflection(store, offset, 1, length, out a); // apply P to the other columns of A for (int c = k + 1; c < cols; c++) { VectorAlgorithms.ApplyHouseholderReflection(store, offset, 1, store, rows * c + k, 1, length); } // apply P to Q to accumulate the transform // since Q is rows X rows, its column count is just rows for (int c = 0; c < rows; c++) { VectorAlgorithms.ApplyHouseholderReflection(store, offset, 1, qtStore, rows * c + k, 1, length); } // we are done with the Householder vector now, so we can zero the first column store[offset] = a; for (int i = 1; i < length; i++) { store[offset + i] = 0.0; } } }
/// <summary> /// Divides a row vector by a real, scalar constant. /// </summary> /// <param name="alpha">The real, scalar constant.</param> /// <param name="v">The row vector.</param> /// <returns>The result.</returns> public static RowVector operator /(RowVector v, double alpha) { if (v == null) { throw new ArgumentNullException(nameof(v)); } double[] store = VectorAlgorithms.Multiply(1.0 / alpha, v.store, v.offset, v.stride, v.dimension); return(new RowVector(store, v.dimension)); }
/// <summary> /// Multiplies a column vector by a real, scalar constant. /// </summary> /// <param name="alpha">The real, scalar constant.</param> /// <param name="v">The column vector.</param> /// <returns>The product αv.</returns> public static ColumnVector operator *(double alpha, ColumnVector v) { if (v == null) { throw new ArgumentNullException(nameof(v)); } double[] store = VectorAlgorithms.Multiply(alpha, v.store, v.offset, v.stride, v.dimension); return(new ColumnVector(store, v.dimension)); }
public static double[] AccumulateBidiagonalV(double[] store, int rows, int cols) { // Q_1 * ... (Q_{n-1} * (Q_n * 1)) double[] result = SquareMatrixAlgorithms.CreateUnitMatrix(cols); for (int k = cols - 2; k >= 0; k--) { // apply Householder reflection to each column from the left for (int j = k + 1; j < cols; j++) { VectorAlgorithms.ApplyHouseholderReflection(store, (k + 1) * rows + k, rows, result, j * cols + (k + 1), 1, cols - k - 1); } } return(result); }
// The reduction to Hessenberg form via a similiarity transform proceeds as follows. A Householder reflection can // zero the non-Householder elements in the first column. Since this is a similiarity transform, we have to apply // it from the other side as well. The letters indicate which elements are mixed by each transform. // XXXXX XXXXX XFFFF // XXXXX ABCDE AGGGG // XXXXX -> 0BCDE -> 0HHHH // XXXXX 0BCDE 0IIII // XXXXX 0BCDE 0JJJJ // Note that if we had tried to zero all the subdiagonal elements, rather than just the (sub+1)-diagonal elements, then the // applicaion of the Householder reflection from the other side would have messed with our zeros. But because we only tried // for Hessenberg, not upper-right-triangular, form, our zeros are safe. We then do it again for the next column. // XXXXX XXXXX XXEEE // XXXXX XXXXX XXFFF // 0XXXX -> 0ABCD -> 0AGGG // 0XXXX 00BCD 00HHH // 0XXXX 00BCD 00III // And so on until we are done. public static void ReduceToHessenberg(double[] aStore, double[] vStore, int dim) { int dm2 = dim - 2; for (int k = 0; k < dm2; k++) { // determine a Householder reflection to zero the (sub+1)-diagonal elements of the current column int offset = dim * k + (k + 1); int length = dim - (k + 1); double a; VectorAlgorithms.GenerateHouseholderReflection(aStore, offset, 1, length, out a); // determine P * A for (int c = k + 1; c < dim; c++) { VectorAlgorithms.ApplyHouseholderReflection(aStore, offset, 1, aStore, dim * c + (k + 1), 1, length); } // determine A * P for (int r = 0; r < dim; r++) { VectorAlgorithms.ApplyHouseholderReflection(aStore, offset, 1, aStore, dim * (k + 1) + r, dim, length); } // if we are keeping track of the transformation, determine V * P if (vStore != null) { for (int r = 0; r < dim; r++) { VectorAlgorithms.ApplyHouseholderReflection(aStore, offset, 1, vStore, dim * (k + 1) + r, dim, length); } } // We are done with our Householder vector, so we can overwrite the space we were using for it with the values produced by // the reflection. aStore[offset] = a; for (int i = 1; i < length; i++) { aStore[offset + i] = 0.0; } //aStore[dim * k + (k + 1)] = a; //for (int i = k + 2; i < dim; i++) { // MatrixAlgorithms.SetEntry(aStore, dim, dim, i, k, 0.0); //} // Not having done this earlier is not a problem because the values in that column do not come into play when computing // the effects of the transform on the other entries, i.e. it doesn't "mix" with the other entries } }
/// <summary> /// Computes the difference of two column vectors. /// </summary> /// <param name="v1">The first column vector.</param> /// <param name="v2">The second column vector.</param> /// <returns>The difference <paramref name="v1"/> - <paramref name="v2"/>.</returns> public static RowVector operator -(RowVector v1, RowVector v2) { if (v1 == null) { throw new ArgumentNullException(nameof(v1)); } if (v2 == null) { throw new ArgumentNullException(nameof(v2)); } if (v1.dimension != v2.dimension) { throw new DimensionMismatchException(); } double[] store = VectorAlgorithms.Subtract(v1.store, v1.offset, v1.stride, v2.store, v2.offset, v2.stride, v1.dimension); return(new RowVector(store, v1.dimension)); }
public static double[] AccumulateBidiagonalU(double[] store, int rows, int cols) { // ((1 * Q_n) * Q_{n-1}) ... * Q_1 double[] result = SquareMatrixAlgorithms.CreateUnitMatrix(rows); // iterate over Householder reflections for (int k = cols - 1; k >= 0; k--) { // apply Householder reflection to each row from the right for (int j = k; j < rows; j++) { VectorAlgorithms.ApplyHouseholderReflection(store, k * rows + k, 1, result, k * rows + j, rows, rows - k); } } return(result); }
/// <summary> /// Computes the sum of two column vectors. /// </summary> /// <param name="v1">The first column vector.</param> /// <param name="v2">The second column vector.</param> /// <returns>The sum <paramref name="v1"/> + <paramref name="v2"/>.</returns> public static ColumnVector operator +(ColumnVector v1, ColumnVector v2) { if (v1 == null) { throw new ArgumentNullException("v1"); } if (v2 == null) { throw new ArgumentNullException("v2"); } if (v1.dimension != v2.dimension) { throw new DimensionMismatchException(); } double[] store = VectorAlgorithms.Add(v1.store, v1.offset, v1.stride, v2.store, v2.offset, v2.stride, v1.dimension); return(new ColumnVector(store, v1.dimension)); }
// SVD // Bidiagonalization uses seperate left and right Householder reflections to bring a matrix into a form of bandwidth two. // XXXXX ABCDE XA000 XX000 XX000 // XXXXX 0BCDE 0BBBB 0ABCD 0XA00 // XXXXX 0BCDE 0CCCC 00BCD 00BBB // XXXXX -> 0BCDE -> 0DDDD -> 00BCD -> 00CCC -> etc // XXXXX 0BCDE 0EEEE 00BCD 00DDD // XXXXX 0BCDE 0FFFF 00BCD 00EEE // XXXXX 0BCDE 0GGGG 00BCD 00FFF // The end result is B = U A V, where U and V are different (so this isn't a similiarity transform) orthogonal matrices. // Note that we can't get fully diagonal with this approach, because if the right transform tried to zero the first superdiagonal // element, it would interfere with the zeros already created. // The reslting diagonal and first superdiagonal elements are returned in a and b. The matrix itself is gradually // overwritten with the elements of the Householder reflections used. When we are done // the matrix is replaced by Householder vector components as follows: // APPPP // ABQQQ // ABCRR // ABCDS // ABCDE // ABCDE // where A, B, C, D, E are the Householder reflections applied from the left to zero columns, and P, Q, R, S are // the Householder reflections applied from the right to zero rows. public static void Bidiagonalize(double[] store, int rows, int cols, out double[] a, out double[] b) { // This logic is written assuming more rows than columns. If not, bidiagonalize the transpose. Debug.Assert(rows >= cols); // Allocate space for the diagonal and superdiagonal. a = new double[cols]; b = new double[cols - 1]; for (int k = 0; k < cols; k++) { // generate a householder reflection which, when applied from the left, will zero sub-diagonal elements in the kth column // use those elements to store the reflection vector; store the resulting diagonal element seperately VectorAlgorithms.GenerateHouseholderReflection(store, rows * k + k, 1, rows - k, out a[k]); // apply that reflection to all the columns; only subsequent ones are affected since preceeding ones // contain only zeros in the subdiagonal rows for (int c = k + 1; c < cols; c++) { VectorAlgorithms.ApplyHouseholderReflection(store, rows * k + k, 1, store, rows * c + k, 1, rows - k); } if ((k + 1) < cols) { // generate a Householder reflection which, when applied from the right, will zero (super+1)-diagonal elements in the kth row // again, store the elements of the reflection vector in the zeroed matrix elements and store the resulting superdiagonal element seperately VectorAlgorithms.GenerateHouseholderReflection(store, rows * (k + 1) + k, rows, cols - (k + 1), out b[k]); // apply the reflection to all the rows; only subsequent ones are affected since the preceeding ones contain only zeros in the // affected columns; the already-zeroed column elements are not distrubed because those columns are not affected // this restriction is why we cannot fully diagonalize using this transform for (int r = k + 1; r < rows; r++) { VectorAlgorithms.ApplyHouseholderReflection(store, rows * (k + 1) + k, rows, store, rows * (k + 1) + r, rows, cols - (k + 1)); } } } }
/// <summary> /// Returns a copy of the row vector. /// </summary> /// <returns>An independent copy of the row vector.</returns> public RowVector Copy() { double[] copy = VectorAlgorithms.Copy(store, offset, stride, dimension); return(new RowVector(copy, dimension)); }
/// <summary> /// Returns the transpose of the row vector. /// </summary> /// <returns>An independent column vector with the same components as the row vector.</returns> public ColumnVector Transpose() { double[] copy = VectorAlgorithms.Copy(store, offset, stride, dimension); return(new ColumnVector(copy, dimension)); }
/// <summary> /// Returns the vector elements in an independent array. /// </summary> /// <returns>An array containing the vector element values.</returns> public virtual new double[] ToArray() { return(VectorAlgorithms.Copy(store, offset, stride, dimension)); }
private static void FrancisTwoStep(double[] aStore, double[] qStore, int dimension, int a, int n, double sum, double product) { // to apply the implicit Q theorem, get the first column of what (A - mu I)(A - mu* I) would be double[] v = GenerateFirstColumn(aStore, dimension, a, n, sum, product); int d = n - a + 1; Debug.Assert(d >= 3); // generate a householder reflection for this column, and apply it to the matrix double e; VectorAlgorithms.GenerateHouseholderReflection(v, 0, 1, 3, out e); // determine P * A for (int c = a; c < dimension; c++) { VectorAlgorithms.ApplyHouseholderReflection(v, 0, 1, aStore, dimension * c + a, 1, 3); } // determine A * P for (int r = 0; r <= Math.Min(a + 3, n); r++) { VectorAlgorithms.ApplyHouseholderReflection(v, 0, 1, aStore, dimension * a + r, dimension, 3); } // determine Q * P if (qStore != null) { for (int r = 0; r < dimension; r++) { VectorAlgorithms.ApplyHouseholderReflection(v, 0, 1, qStore, dimension * a + r, dimension, 3); } } //Write(aStore, dimension, dimension); // The matrix now looks like this: // XXXXXX ABCDEF AAAXXX // XXXXXX ABCDEF BBBXXX // 0XXXXX -> ABCDEF -> CCCXXX // 00XXXX 00XXXX DDDXXX // 000XXX 000XXX 000XXX // 0000XX 0000XX 0000XX // Note there are three non-Hessenberg elements. These constitute a "bulge". Now "chase the bulge" down the matrix, using // Householder relfections to zero the non-Hessenberg elements of each column in turn. for (int k = a; k <= (n - 3); k++) { // determine the required Householder reflection v[0] = aStore[dimension * k + (k + 1)]; v[1] = aStore[dimension * k + (k + 2)]; v[2] = aStore[dimension * k + (k + 3)]; VectorAlgorithms.GenerateHouseholderReflection(v, 0, 1, 3, out e); //v = GenerateHouseholderReflection(aStore, dimension * k + k + 1, 1, 3); // determine P * A and A * P aStore[dimension * k + (k + 1)] = e; aStore[dimension * k + (k + 2)] = 0.0; aStore[dimension * k + (k + 3)] = 0.0; for (int c = k + 1; c < dimension; c++) { VectorAlgorithms.ApplyHouseholderReflection(v, 0, 1, aStore, dimension * c + (k + 1), 1, 3); } for (int r = 0; r < Math.Max(k + 4, dimension); r++) { VectorAlgorithms.ApplyHouseholderReflection(v, 0, 1, aStore, dimension * (k + 1) + r, dimension, 3); } // if tracking eigenvalues, determine Q * P if (qStore != null) { for (int r = 0; r < dimension; r++) { VectorAlgorithms.ApplyHouseholderReflection(v, 0, 1, qStore, dimension * (k + 1) + r, dimension, 3); } } } //Write(aStore, dimension, dimension); // restoring Householder form in the last column requires only a length-2 Householder reflection // a Givens rotation would be another possibility here int l = n - 2; v[0] = aStore[dimension * l + (l + 1)]; v[1] = aStore[dimension * l + (l + 2)]; VectorAlgorithms.GenerateHouseholderReflection(v, 0, 1, 2, out e); aStore[dimension * l + (l + 1)] = e; aStore[dimension * l + (l + 2)] = 0.0; for (int c = l + 1; c < dimension; c++) { VectorAlgorithms.ApplyHouseholderReflection(v, 0, 1, aStore, dimension * c + (l + 1), 1, 2); } for (int r = 0; r <= n; r++) { VectorAlgorithms.ApplyHouseholderReflection(v, 0, 1, aStore, dimension * (l + 1) + r, dimension, 2); } if (qStore != null) { for (int r = 0; r < dimension; r++) { VectorAlgorithms.ApplyHouseholderReflection(v, 0, 1, qStore, dimension * (l + 1) + r, dimension, 2); } } //double cos, sin; //GenerateGivensRotation(ref aStore[dimension * l + l + 1], ref aStore[dimension * l + l + 2], out cos, out sin); }