public static double[] Subtract(double[] aStore, int aOffset, int aStride, double[] bStore, int bOffset, int bStride, int dimension) { double[] store = new double[dimension]; Blas1.dCopy(aStore, aOffset, aStride, store, 0, 1, dimension); Blas1.dAxpy(-1.0, bStore, bOffset, bStride, store, 0, 1, dimension); return(store); }
public static void SortValues(double[] values, double[] utStore, double[] vStore, int rows, int cols) { // this is a selection sort, an O(N^2) sort which requires fewer swaps than an insertion sort or an O(N ln N) sort // loop over ranks for (int i = 0; i < cols; i++) { // find the next largest value int j = i; double t = values[i]; for (int k = i + 1; k < cols; k++) { if (values[k] > t) { j = k; t = values[k]; } } // if necessary swap it with the current element if (j != i) { Global.Swap(ref values[i], ref values[j]); if (utStore != null) { Blas1.dSwap(utStore, i, rows, utStore, j, rows, rows); } if (vStore != null) { Blas1.dSwap(vStore, i * cols, 1, vStore, j * cols, 1, cols); } } } }
public static void SolveLowerLeftTriangular(double[] tStore, double[] x, int xOffset, int dimension) { for (int r = 0; r < dimension; r++) { double t = Blas1.dDot(tStore, r, dimension, x, xOffset, 1, r); x[xOffset + r] -= t; } }
// ** 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(); }
public static void SolveUpperRightTriangular(double[] tStore, double[] x, int xOffset, int dimension) { for (int r = dimension - 1; r >= 0; r--) { int i0 = dimension * r + r; double t = Blas1.dDot(tStore, i0 + dimension, dimension, x, xOffset + r + 1, 1, dimension - r - 1); x[xOffset + r] = (x[xOffset + r] - t) / tStore[i0]; } }
/// <summary> /// Gets the U factor. /// </summary> /// <returns>The upper-right triangular factor U of the LU decomposition.</returns> public SquareMatrix UMatrix() { double[] uStore = new double[dimension * dimension]; for (int c = 0; c < dimension; c++) { Blas1.dCopy(luStore, dimension * c, 1, uStore, dimension * c, 1, c + 1); } return(new SquareMatrix(uStore, dimension)); }
/// <inheritdoc /> public override ColumnVector Column(int c) { if ((c < 0) || (c >= cols)) { throw new ArgumentOutOfRangeException(nameof(c)); } double[] cStore = new double[rows]; Blas1.dCopy(store, offset + colStride * c, rowStride, cStore, 0, 1, rows); return(new ColumnVector(cStore, rows)); }
/// <inheritdoc /> public override RowVector Row(int r) { if ((r < 0) || (r >= rows)) { throw new ArgumentOutOfRangeException(nameof(r)); } double[] rStore = new double[cols]; Blas1.dCopy(store, offset + rowStride * r, colStride, rStore, 0, 1, cols); return(new RowVector(rStore, cols)); }
// Apply a Householder transfrom defined by v to the vector x (which may be // a column of a matrix, if H is applied from the left, or a row of a matrix // if H is applied from the right). On exit v is unchanged, x is changed. // We have H = I - \beta v v^T, so H x = x - (\beta v^T x) v. public static void ApplyHouseholderReflection( double[] uStore, int uOffset, int uStride, double[] yStore, int yOffset, int yStride, int count ) { double s = Blas1.dDot(uStore, uOffset, uStride, yStore, yOffset, yStride, count); Blas1.dAxpy(-s, uStore, uOffset, uStride, yStore, yOffset, yStride, count); }
/// <summary> /// Multiplies a complex column vector by a complex constant. /// </summary> /// <param name="alpha">The complex constant.</param> /// <param name="v">The complex column vector.</param> /// <returns>The product αv.</returns> /// <exception cref="ArgumentNullException"><paramref name="v"/> is null.</exception> public static ComplexColumnVector operator *(Complex alpha, ComplexColumnVector v) { if (v == null) { throw new ArgumentNullException(nameof(v)); } Complex[] product = new Complex[v.dimension]; Blas1.zAxpy(alpha, v.store, v.offset, 1, product, 0, 1, v.dimension); return(new ComplexColumnVector(product, 0, v.dimension, false)); }
/// <summary> /// Gets the L factor. /// </summary> /// <returns>The lower-left triangular factor L of the LU decomposition.</returns> /// <remarks> /// <para>The pivoted LU decomposition algorithm guarantees that the diagonal entries of this matrix are all one, and /// that the magnitudes of the sub-diagonal entries are all less than or equal to one.</para> /// </remarks> public SquareMatrix LMatrix() { double[] lStore = new double[dimension * dimension]; for (int c = 0; c < dimension; c++) { int i0 = dimension * c + c; lStore[i0] = 1.0; Blas1.dCopy(luStore, i0 + 1, 1, lStore, i0 + 1, 1, dimension - c - 1); } return(new SquareMatrix(lStore, dimension)); }
private static void SwapIndexes(double[] aStore, int[] perm, int dimension, int p, int q) { if (p == q) { return; } Blas1.dSwap(aStore, p, dimension, aStore, q, dimension, dimension); Blas1.dSwap(aStore, dimension * p, 1, aStore, dimension * q, 1, dimension); if (perm != null) { Global.Swap <int>(ref perm[p], ref perm[q]); } }
public static double OneNorm(double[] store, int offset, int rowStride, int colStride, int nRows, int nColumns) { double norm = 0.0; for (int c = 0; c < nColumns; c++) { double csum = Blas1.dNrm1(store, offset + colStride * c, rowStride, nRows); if (csum > norm) { norm = csum; } } return(norm); }
public static double InfinityNorm(double[] store, int offset, int rowStride, int colStride, int nRows, int nColumns) { double norm = 0.0; for (int r = 0; r < nRows; r++) { double rsum = Blas1.dNrm1(store, offset + rowStride * r, colStride, nColumns); if (rsum > norm) { norm = rsum; } } return(norm); }
// Solve a triangular system // aIsUpper indicates whether A is upper/right or lower/left // aIsUnit indicates whether A's diagonal elements are all 1 or not // The relevent elements of xStore are overwritten by the solution vector public static void dTrsv( bool aIsUpper, bool aIsUnit, double[] aStore, int aOffset, int aRowStride, int aColStride, double[] xStore, int xOffset, int xStride, int count ) { int aDiagonalStride = aColStride + aRowStride; // If A is upper triangular, start at the bottom/right and reverse the direction of progression. // This does not affect the passed in variables because integers are pass-by-value. if (aIsUpper) { aOffset = aOffset + (count - 1) * aDiagonalStride; aRowStride = -aRowStride; aColStride = -aColStride; aDiagonalStride = -aDiagonalStride; xOffset = xOffset + (count - 1) * xStride; xStride = -xStride; } // The index of the first row element to be subtracted in the numerator. int aRowIndex = aOffset; // The index of the x element to be solved for. int xIndex = xOffset; // We hoist the aIsUnit test outside the loop and pay the cost of a little repeated code in // order to avoid an unnecessary per-loop test. It's possible the compiler could do this for us. if (aIsUnit) { for (int n = 0; n < count; n++) { xStore[xIndex] -= Blas1.dDot(aStore, aRowIndex, aColStride, xStore, xOffset, xStride, n); aRowIndex += aRowStride; xIndex += xStride; } } else { int aDiagonalIndex = aOffset; for (int n = 0; n < count; n++) { xStore[xIndex] -= Blas1.dDot(aStore, aRowIndex, aColStride, xStore, xOffset, xStride, n); xStore[xIndex] /= aStore[aDiagonalIndex]; aRowIndex += aRowStride; aDiagonalIndex += aDiagonalStride; xIndex += xStride; } } }
/// <summary> /// Sort the eigenvalues as specified. /// </summary> /// <param name="order">The desired ordering.</param> public void Sort(OrderBy order) { // Create an auxiluary array of indexes to sort int[] ranks = new int[dimension]; for (int i = 0; i < ranks.Length; i++) { ranks[i] = i; } // Sort indexes in the requested order Comparison <int> comparison; switch (order) { case OrderBy.ValueAscending: comparison = (int a, int b) => eigenvalues[a].CompareTo(eigenvalues[b]); break; case OrderBy.ValueDescending: comparison = (int a, int b) => eigenvalues[b].CompareTo(eigenvalues[a]); break; case OrderBy.MagnitudeAscending: comparison = (int a, int b) => Math.Abs(eigenvalues[a]).CompareTo(Math.Abs(eigenvalues[b])); break; case OrderBy.MagnitudeDescending: comparison = (int a, int b) => Math.Abs(eigenvalues[b]).CompareTo(Math.Abs(eigenvalues[a])); break; default: throw new NotSupportedException(); } Array.Sort(ranks, comparison); // Create new storage in the desired order and discard the old storage // This is faster than moving within existing storage since we can move each column just once. double[] newEigenvalues = new double[dimension]; double[] newEigenvectorStorage = new double[dimension * dimension]; for (int i = 0; i < ranks.Length; i++) { newEigenvalues[i] = eigenvalues[ranks[i]]; Blas1.dCopy(eigenvectorStorage, dimension * ranks[i], 1, newEigenvectorStorage, dimension * i, 1, dimension); } eigenvalues = newEigenvalues; eigenvectorStorage = newEigenvectorStorage; }
// y <- A x + y public static void dGemv( double[] aStore, int aOffset, int aRowStride, int aColStride, double[] xStore, int xOffset, int xStride, double[] yStore, int yOffset, int yStride, int rows, int cols ) { int aIndex = aOffset; int yIndex = yOffset; for (int n = 0; n < rows; n++) { yStore[yIndex] += Blas1.dDot(aStore, aIndex, aColStride, xStore, xOffset, xStride, cols); aIndex += aRowStride; yIndex += yStride; } }
/// <summary> /// Computes the inner (scalar or dot) product of a row and a column vector. /// </summary> /// <param name="v">The row vector.</param> /// <param name="u">The column vector.</param> /// <returns>The value of the scalar product.</returns> public static double operator *(RowVector v, ColumnVector u) { if (v == null) { throw new ArgumentNullException(nameof(v)); } if (u == null) { throw new ArgumentNullException(nameof(u)); } if (v.dimension != u.dimension) { throw new DimensionMismatchException(); } double p = Blas1.dDot(v.store, v.offset, v.stride, u.store, u.offset, u.stride, v.dimension); return(p); }
public static double[] Copy(double[] store, int offset, int rowStride, int colStride, int nRows, int nCols) { double[] copyStore = AllocateStorage(nRows, nCols); if ((rowStride == 1) && (colStride == nRows)) { Array.Copy(store, offset, copyStore, 0, copyStore.Length); } else { for (int c = 0; c < nCols; c++) { int sourceIndex = offset + colStride * c; int copyIndex = nRows * c; Blas1.dCopy(store, sourceIndex, rowStride, copyStore, copyIndex, 1, nRows); } } return(copyStore); }
// 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; } } }
// 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); } }
public static void SolveUpperRightTriangular(double[] tStore, int tRows, int tCols, double[] xStore, int xOffset) { Debug.Assert(tRows >= tCols); // pre-calculate some useful quantities int trp1 = tRows + 1; int tcm1 = tCols - 1; // initialize the x index (to its highest value) the the diagonal t index (to its highest value) int xIndex = xOffset + tcm1; int tIndex = trp1 * tcm1; // do the first, trivial back-substitution xStore[xIndex] /= tStore[tIndex]; // do the remaining back-substitutions, each of which requires subtracting n previously computed x-values for (int n = 1; n <= tcm1; n++) { xIndex -= 1; tIndex -= trp1; double t = Blas1.dDot(tStore, tIndex + tRows, tRows, xStore, xIndex + 1, 1, n); xStore[xIndex] = (xStore[xIndex] - t) / tStore[tIndex]; } }
public static double[] Copy(double[] store, int offset, int stride, int dimension) { double[] copy = new double[dimension]; Blas1.dCopy(store, offset, stride, copy, 0, 1, dimension); return(copy); }
// 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); } } }
void ICollection <double> .CopyTo(double[] array, int arrayIndex) { Blas1.dCopy(store, offset, stride, array, arrayIndex, 1, dimension); }
/// <summary> /// Computes the magnitude of the vector. /// </summary> /// <returns>The Euclidean norm of the vector.</returns> public virtual double Norm() { return(Blas1.dNrm2(store, offset, stride, dimension)); }
/// <summary> /// Returns a copy of the complex column vector. /// </summary> /// <returns>An independent copy of the complex column vector.</returns> public ComplexColumnVector Copy() { Complex[] copy = new Complex[dimension]; Blas1.Copy(store, offset, 1, copy, 0, 1, dimension); return(new ComplexColumnVector(copy, 0, dimension, false)); }
public static double[] Multiply(double alpha, double[] store, int offset, int stride, int dimension) { double[] product = new double[dimension]; Blas1.dAxpy(alpha, store, offset, stride, product, 0, 1, dimension); return(product); }