/// <summary> /// Naive generic DFT, useful e.g. to verify faster algorithms. /// </summary> /// <param name="samples">Time-space sample vector.</param> /// <param name="exponentSign">Fourier series exponent sign.</param> /// <returns>Corresponding frequency-space vector.</returns> static void Naive(Complex[] samples, int exponentSign) { var w0 = exponentSign * Constants.Pi2 / samples.Length; var spectrum = new Complex[samples.Length]; CommonParallel.For(0, samples.Length, (u, v) => { for (int i = u; i < v; i++) { var wk = w0 * i; var sum = Complex.Zero; for (var n = 0; n < samples.Length; n++) { var w = n * wk; sum += samples[n] * new Complex(Math.Cos(w), Math.Sin(w)); } spectrum[i] = sum; } }); spectrum.Copy(samples); }
/// <summary> /// Solves A*X=B for X using LU factorization. /// </summary> /// <param name="columnsOfB">The number of columns of B.</param> /// <param name="a">The square matrix A.</param> /// <param name="order">The order of the square matrix <paramref name="a"/>.</param> /// <param name="b">On entry the B matrix; on exit the X matrix.</param> /// <remarks>This is equivalent to the GETRF and GETRS LAPACK routines.</remarks> public virtual void LUSolve(int columnsOfB, Complex[] a, int order, Complex[] b) { if (a == null) { throw new ArgumentNullException("a"); } if (b == null) { throw new ArgumentNullException("b"); } if (a.Length != order*order) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); } if (b.Length != order*columnsOfB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); } if (ReferenceEquals(a, b)) { throw new ArgumentException(Resources.ArgumentReferenceDifferent); } var ipiv = new int[order]; var clone = new Complex[a.Length]; a.Copy(clone); LUFactor(clone, order, ipiv); LUSolveFactored(columnsOfB, clone, order, ipiv, b); }
/// <summary> /// Computes the inverse of a previously factored matrix. /// </summary> /// <param name="a">The LU factored N by N matrix. Contains the inverse On exit.</param> /// <param name="order">The order of the square matrix <paramref name="a"/>.</param> /// <param name="ipiv">The pivot indices of <paramref name="a"/>.</param> /// <remarks>This is equivalent to the GETRI LAPACK routine.</remarks> public virtual void LUInverseFactored(Complex[] a, int order, int[] ipiv) { if (a == null) { throw new ArgumentNullException("a"); } if (ipiv == null) { throw new ArgumentNullException("ipiv"); } if (a.Length != order*order) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); } if (ipiv.Length != order) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); } var inverse = new Complex[a.Length]; for (var i = 0; i < order; i++) { inverse[i + (order*i)] = Complex.One; } LUSolveFactored(order, a, order, ipiv, inverse); inverse.Copy(a); }
/// <summary> /// Solves A*X=B for X using Cholesky factorization. /// </summary> /// <param name="a">The square, positive definite matrix A.</param> /// <param name="orderA">The number of rows and columns in A.</param> /// <param name="b">On entry the B matrix; on exit the X matrix.</param> /// <param name="columnsB">The number of columns in the B matrix.</param> /// <remarks>This is equivalent to the POTRF add POTRS LAPACK routines.</remarks> public virtual void CholeskySolve(Complex[] a, int orderA, Complex[] b, int columnsB) { if (a == null) { throw new ArgumentNullException("a"); } if (b == null) { throw new ArgumentNullException("b"); } if (b.Length != orderA*columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); } if (ReferenceEquals(a, b)) { throw new ArgumentException(Resources.ArgumentReferenceDifferent); } var clone = new Complex[a.Length]; a.Copy(clone); CholeskyFactor(clone, orderA); CholeskySolveFactored(clone, orderA, b, columnsB); }
/// <summary> /// Solves A*X=B for X using the singular value decomposition of A. /// </summary> /// <param name="a">On entry, the M by N matrix to decompose.</param> /// <param name="rowsA">The number of rows in the A matrix.</param> /// <param name="columnsA">The number of columns in the A matrix.</param> /// <param name="b">The B matrix.</param> /// <param name="columnsB">The number of columns of B.</param> /// <param name="x">On exit, the solution matrix.</param> public virtual void SvdSolve(Complex[] a, int rowsA, int columnsA, Complex[] b, int columnsB, Complex[] x) { if (a == null) { throw new ArgumentNullException("a"); } if (b == null) { throw new ArgumentNullException("b"); } if (x == null) { throw new ArgumentNullException("x"); } if (b.Length != rowsA*columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); } if (x.Length != columnsA*columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); } var work = new Complex[rowsA]; var s = new Complex[Math.Min(rowsA, columnsA)]; var u = new Complex[rowsA*rowsA]; var vt = new Complex[columnsA*columnsA]; var clone = new Complex[a.Length]; a.Copy(clone); SingularValueDecomposition(true, clone, rowsA, columnsA, s, u, vt, work); SvdSolveFactored(rowsA, columnsA, s, u, vt, b, columnsB, x); }
/// <summary> /// Scales an array. Can be used to scale a vector and a matrix. /// </summary> /// <param name="alpha">The scalar.</param> /// <param name="x">The values to scale.</param> /// <param name="result">This result of the scaling.</param> /// <remarks>This is similar to the SCAL BLAS routine.</remarks> public virtual void ScaleArray(Complex alpha, Complex[] x, Complex[] result) { if (x == null) { throw new ArgumentNullException("x"); } if (alpha.IsZero()) { Array.Clear(result, 0, result.Length); } else if (alpha.IsOne()) { x.Copy(result); } else { if (Control.ParallelizeOperation(x.Length)) { CommonParallel.For(0, x.Length, 4096, (a, b) => { for (int i = a; i < b; i++) { result[i] = alpha*x[i]; } }); } else { for (var index = 0; index < x.Length; index++) { result[index] = alpha*x[index]; } } } }
/// <summary> /// Solves A*X=B for X using QR factorization of A. /// </summary> /// <param name="a">The A matrix.</param> /// <param name="rows">The number of rows in the A matrix.</param> /// <param name="columns">The number of columns in the A matrix.</param> /// <param name="b">The B matrix.</param> /// <param name="columnsB">The number of columns of B.</param> /// <param name="x">On exit, the solution matrix.</param> /// <param name="work">The work array. The array must have a length of at least N, /// but should be N*blocksize. The blocksize is machine dependent. On exit, work[0] contains the optimal /// work size value.</param> /// <param name="method">The type of QR factorization to perform. <seealso cref="QRMethod"/></param> /// <remarks>Rows must be greater or equal to columns.</remarks> public virtual void QRSolve(Complex[] a, int rows, int columns, Complex[] b, int columnsB, Complex[] x, Complex[] work, QRMethod method = QRMethod.Full) { if (a == null) { throw new ArgumentNullException("a"); } if (b == null) { throw new ArgumentNullException("b"); } if (x == null) { throw new ArgumentNullException("x"); } if (work == null) { throw new ArgumentNullException("work"); } if (a.Length != rows*columns) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); } if (b.Length != rows*columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); } if (rows < columns) { throw new ArgumentException(Resources.RowsLessThanColumns); } if (x.Length != columns*columnsB) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); } if (work.Length < rows*columns) { work[0] = rows*columns; throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); } var clone = new Complex[a.Length]; a.Copy(clone); if (method == QRMethod.Full) { var q = new Complex[rows*rows]; QRFactor(clone, rows, columns, q, work); QRSolveFactored(q, clone, rows, columns, null, b, columnsB, x, method); } else { var r = new Complex[columns*columns]; ThinQRFactor(clone, rows, columns, r, work); QRSolveFactored(clone, r, rows, columns, null, b, columnsB, x, method); } work[0] = rows*columns; }
/// <summary> /// Adds a scaled vector to another: <c>result = y + alpha*x</c>. /// </summary> /// <param name="y">The vector to update.</param> /// <param name="alpha">The value to scale <paramref name="x"/> by.</param> /// <param name="x">The vector to add to <paramref name="y"/>.</param> /// <param name="result">The result of the addition.</param> /// <remarks>This is similar to the AXPY BLAS routine.</remarks> public virtual void AddVectorToScaledVector(Complex[] y, Complex alpha, Complex[] x, Complex[] result) { if (y == null) { throw new ArgumentNullException("y"); } if (x == null) { throw new ArgumentNullException("x"); } if (y.Length != x.Length) { throw new ArgumentException(Resources.ArgumentVectorsSameLength); } if (y.Length != x.Length) { throw new ArgumentException(Resources.ArgumentVectorsSameLength); } if (alpha.IsZero()) { y.Copy(result); } else if (alpha.IsOne()) { if (Control.ParallelizeOperation(x.Length)) { CommonParallel.For(0, y.Length, 4096, (a, b) => { for (int i = a; i < b; i++) { result[i] = y[i] + x[i]; } }); } else { for (var index = 0; index < x.Length; index++) { result[index] = y[index] + x[index]; } } } else { if (Control.ParallelizeOperation(x.Length)) { CommonParallel.For(0, y.Length, 4096, (a, b) => { for (int i = a; i < b; i++) { result[i] = y[i] + (alpha*x[i]); } }); } else { for (var index = 0; index < x.Length; index++) { result[index] = y[index] + (alpha*x[index]); } } } }