diff --git a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex.cs b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex.cs new file mode 100644 index 00000000..6ee85a96 --- /dev/null +++ b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex.cs @@ -0,0 +1,2865 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// http://mathnetnumerics.codeplex.com +// Copyright (c) 2009-2010 Math.NET +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +namespace MathNet.Numerics.Algorithms.LinearAlgebra +{ + using System; + using System.Numerics; + using Properties; + using Threading; + + /// + /// The managed linear algebra provider. + /// + public partial class ManagedLinearAlgebraProvider : ILinearAlgebraProvider + { + /// + /// Adds a scaled vector to another: y += alpha*x. + /// + /// The vector to update. + /// The value to scale by. + /// The vector to add to . + /// This equivalent to the AXPY BLAS routine. + public void AddVectorToScaledVector(Complex[] y, Complex alpha, Complex[] x) + { + 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 (alpha == 0.0) + { + return; + } + + if (alpha == 1.0) + { + CommonParallel.For(0, y.Length, i => y[i] += x[i]); + } + else + { + CommonParallel.For(0, y.Length, i => y[i] += alpha * x[i]); + } + } + + /// + /// Scales an array. Can be used to scale a vector and a matrix. + /// + /// The scalar. + /// The values to scale. + /// This is equivalent to the SCAL BLAS routine. + public void ScaleArray(Complex alpha, Complex[] x) + { + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (alpha.IsOne()) + { + return; + } + + CommonParallel.For(0, x.Length, i => x[i] = alpha * x[i]); + } + + /// + /// Computes the dot product of x and y. + /// + /// The vector x. + /// The vector y. + /// The dot product of x and y. + /// This is equivalent to the DOT BLAS routine. + public Complex DotProduct(Complex[] x, Complex[] y) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (y.Length != x.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + return CommonParallel.Aggregate(0, y.Length, index => y[index] * x[index]); + } + + /// + /// Does a point wise add of two arrays z = x + y. This can be used + /// to add vectors or matrices. + /// + /// The array x. + /// The array y. + /// The result of the addition. + /// There is no equivalent BLAS routine, but many libraries + /// provide optimized (parallel and/or vectorized) versions of this + /// routine. + public void AddArrays(Complex[] x, Complex[] y, Complex[] result) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (y.Length != x.Length || y.Length != result.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + CommonParallel.For(0, y.Length, i => result[i] = x[i] + y[i]); + } + + /// + /// Does a point wise subtraction of two arrays z = x - y. This can be used + /// to subtract vectors or matrices. + /// + /// The array x. + /// The array y. + /// The result of the subtraction. + /// There is no equivalent BLAS routine, but many libraries + /// provide optimized (parallel and/or vectorized) versions of this + /// routine. + public void SubtractArrays(Complex[] x, Complex[] y, Complex[] result) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (y.Length != x.Length || y.Length != result.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + CommonParallel.For(0, y.Length, i => result[i] = x[i] - y[i]); + } + + /// + /// Does a point wise multiplication of two arrays z = x * y. This can be used + /// to multiple elements of vectors or matrices. + /// + /// The array x. + /// The array y. + /// The result of the point wise multiplication. + /// There is no equivalent BLAS routine, but many libraries + /// provide optimized (parallel and/or vectorized) versions of this + /// routine. + public void PointWiseMultiplyArrays(Complex[] x, Complex[] y, Complex[] result) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (y.Length != x.Length || y.Length != result.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + CommonParallel.For(0, y.Length, i => result[i] = x[i] * y[i]); + } + + /// + /// Does a point wise division of two arrays z = x / y. This can be used + /// to divide elements of vectors or matrices. + /// + /// The array x. + /// The array y. + /// The result of the point wise division. + /// There is no equivalent BLAS routine, but many libraries + /// provide optimized (parallel and/or vectorized) versions of this + /// routine. + public void PointWiseDivideArrays(Complex[] x, Complex[] y, Complex[] result) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (y.Length != x.Length || y.Length != result.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + CommonParallel.For(0, y.Length, index => { result[index] = x[index] / y[index]; }); + } + + /// + /// Computes the requested of the matrix. + /// + /// The type of norm to compute. + /// The number of rows. + /// The number of columns. + /// The matrix to compute the norm from. + /// + /// The requested of the matrix. + /// + public Complex MatrixNorm(Norm norm, int rows, int columns, Complex[] matrix) + { + var ret = 0.0; + switch (norm) + { + case Norm.OneNorm: + for (var j = 0; j < columns; j++) + { + var s = 0.0; + for (var i = 0; i < rows; i++) + { + s += matrix[(j * rows) + i].Magnitude; + } + + ret = Math.Max(ret, s); + } + + break; + case Norm.LargestAbsoluteValue: + + for (var i = 0; i < rows; i++) + { + for (var j = 0; j < columns; j++) + { + ret = Math.Max(matrix[(j * rows) + i].Magnitude, ret); + } + } + + break; + case Norm.InfinityNorm: + for (var i = 0; i < rows; i++) + { + var s = 0.0; + for (var j = 0; j < columns; j++) + { + s += matrix[(j * rows) + i].Magnitude; + } + + ret = Math.Max(ret, s); + } + + break; + case Norm.FrobeniusNorm: + var aat = new Complex[rows * rows]; + MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.Transpose, 1.0, matrix, rows, columns, matrix, rows, columns, 0.0, aat); + + for (var i = 0; i < rows; i++) + { + ret += aat[(i * rows) + i].Magnitude; + } + + ret = Math.Sqrt(ret); + break; + } + + return ret; + } + + /// + /// Computes the requested of the matrix. + /// + /// The type of norm to compute. + /// The number of rows. + /// The number of columns. + /// The matrix to compute the norm from. + /// The work array. Only used when + /// and needs to be have a length of at least M (number of rows of . + /// + /// The requested of the matrix. + /// + public Complex MatrixNorm(Norm norm, int rows, int columns, Complex[] matrix, Complex[] work) + { + return MatrixNorm(norm, rows, columns, matrix); + } + + /// + /// Multiples two matrices. result = x * y + /// + /// The x matrix. + /// The number of rows in the x matrix. + /// The number of columns in the x matrix. + /// The y matrix. + /// The number of rows in the y matrix. + /// The number of columns in the y matrix. + /// Where to store the result of the multiplication. + /// This is a simplified version of the BLAS GEMM routine with alpha + /// set to 1.0 and beta set to 0.0, and x and y are not transposed. + public void MatrixMultiply(Complex[] x, int rowsX, int columnsX, Complex[] y, int rowsY, int columnsY, Complex[] result) + { + // First check some basic requirement on the parameters of the matrix multiplication. + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (rowsX * columnsX != x.Length) + { + throw new ArgumentException("x.Length != xRows * xColumns"); + } + + if (rowsY * columnsY != y.Length) + { + throw new ArgumentException("y.Length != yRows * yColumns"); + } + + if (columnsX != rowsY) + { + throw new ArgumentException("xColumns != yRows"); + } + + if (rowsX * columnsY != result.Length) + { + throw new ArgumentException("xRows * yColumns != result.Length"); + } + + // Check whether we will be overwriting any of our inputs and make copies if necessary. + // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory + // as result, we can do it on a row wise basis. We should investigate this. + Complex[] xdata; + if (ReferenceEquals(x, result)) + { + xdata = (Complex[])x.Clone(); + } + else + { + xdata = x; + } + + Complex[] ydata; + if (ReferenceEquals(y, result)) + { + ydata = (Complex[])y.Clone(); + } + else + { + ydata = y; + } + + // Start the actual matrix multiplication. + // TODO - For small matrices we should get rid of the parallelism because of startup costs. + // Perhaps the following implementations would be a good one + // http://blog.feradz.com/2009/01/cache-efficient-matrix-multiplication/ + MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, Complex.One, xdata, rowsX, columnsX, ydata, rowsY, columnsY, Complex.Zero, result); + } + + /// + /// Multiplies two matrices and updates another with the result. c = alpha*op(a)*op(b) + beta*c + /// + /// How to transpose the matrix. + /// How to transpose the matrix. + /// The value to scale matrix. + /// The a matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. + /// The b matrix + /// The number of rows in the matrix. + /// The number of columns in the matrix. + /// The value to scale the matrix. + /// The c matrix. + public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, Complex alpha, Complex[] a, int rowsA, int columnsA, Complex[] b, int rowsB, int columnsB, Complex beta, Complex[] c) + { + // Choose nonsensical values for the number of rows in c; fill them in depending + // on the operations on a and b. + int rowsC; + + // First check some basic requirement on the parameters of the matrix multiplication. + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if ((int)transposeA > 111 && (int)transposeB > 111) + { + if (rowsA != columnsB) + { + throw new ArgumentOutOfRangeException(); + } + + if (columnsA * rowsB != c.Length) + { + throw new ArgumentOutOfRangeException(); + } + + rowsC = columnsA; + } + else if ((int)transposeA > 111) + { + if (rowsA != rowsB) + { + throw new ArgumentOutOfRangeException(); + } + + if (columnsA * columnsB != c.Length) + { + throw new ArgumentOutOfRangeException(); + } + + rowsC = columnsA; + } + else if ((int)transposeB > 111) + { + if (columnsA != columnsB) + { + throw new ArgumentOutOfRangeException(); + } + + if (rowsA * rowsB != c.Length) + { + throw new ArgumentOutOfRangeException(); + } + + rowsC = rowsA; + } + else + { + if (columnsA != rowsB) + { + throw new ArgumentOutOfRangeException(); + } + + if (rowsA * columnsB != c.Length) + { + throw new ArgumentOutOfRangeException(); + } + + rowsC = rowsA; + } + + if (alpha.IsZero() && beta.IsZero()) + { + Array.Clear(c, 0, c.Length); + return; + } + + // Check whether we will be overwriting any of our inputs and make copies if necessary. + // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory + // as result, we can do it on a row wise basis. We should investigate this. + Complex[] adata; + if (ReferenceEquals(a, c)) + { + adata = (Complex[])a.Clone(); + } + else + { + adata = a; + } + + Complex[] bdata; + if (ReferenceEquals(b, c)) + { + bdata = (Complex[])b.Clone(); + } + else + { + bdata = b; + } + + if (alpha.IsOne()) + { + if (beta.IsZero()) + { + if ((int)transposeA > 111 && (int)transposeB > 111) + { + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + Complex s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s; + } + }); + } + else if ((int)transposeA > 111) + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + Complex s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s; + } + }); + } + else if ((int)transposeB > 111) + { + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + Complex s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s; + } + }); + } + else + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + Complex s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s; + } + }); + } + } + else + { + if ((int)transposeA > 111 && (int)transposeB > 111) + { + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + Complex s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (c[jIndex + i] * beta) + s; + } + }); + } + else if ((int)transposeA > 111) + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + Complex s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s + (c[jcIndex + i] * beta); + } + }); + } + else if ((int)transposeB > 111) + { + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + Complex s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s + (c[jIndex + i] * beta); + } + }); + } + else + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + Complex s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s + (c[jcIndex + i] * beta); + } + }); + } + } + } + else + { + if ((int)transposeA > 111 && (int)transposeB > 111) + { + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + Complex s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (c[jIndex + i] * beta) + (alpha * s); + } + }); + } + else if ((int)transposeA > 111) + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + Complex s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); + } + }); + } + else if ((int)transposeB > 111) + { + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + Complex s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (alpha * s) + (c[jIndex + i] * beta); + } + }); + } + else + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + Complex s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); + } + }); + } + } + } + + /// + /// Computes the LUP factorization of A. P*A = L*U. + /// + /// An by matrix. The matrix is overwritten with the + /// the LU factorization on exit. The lower triangular factor L is stored in under the diagonal of (the diagonal is always 1.0 + /// for the L factor). The upper triangular factor U is stored on and above the diagonal of . + /// The order of the square matrix . + /// On exit, it contains the pivot indices. The size of the array must be . + /// This is equivalent to the GETRF LAPACK routine. + public void LUFactor(Complex[] data, int order, int[] ipiv) + { + if (data == null) + { + throw new ArgumentNullException("data"); + } + + if (ipiv == null) + { + throw new ArgumentNullException("ipiv"); + } + + if (data.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "data"); + } + + if (ipiv.Length != order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); + } + + // Initialize the pivot matrix to the identity permutation. + for (var i = 0; i < order; i++) + { + ipiv[i] = i; + } + + var vecLUcolj = new Complex[order]; + + // Outer loop. + for (var j = 0; j < order; j++) + { + var indexj = j * order; + var indexjj = indexj + j; + + // Make a copy of the j-th column to localize references. + for (var i = 0; i < order; i++) + { + vecLUcolj[i] = data[indexj + i]; + } + + // Apply previous transformations. + for (var i = 0; i < order; i++) + { + // Most of the time is spent in the following dot product. + var kmax = Math.Min(i, j); + var s = Complex.Zero; + for (var k = 0; k < kmax; k++) + { + s += data[(k * order) + i] * vecLUcolj[k]; + } + + data[indexj + i] = vecLUcolj[i] -= s; + } + + // Find pivot and exchange if necessary. + var p = j; + for (var i = j + 1; i < order; i++) + { + if (vecLUcolj[i].Magnitude > vecLUcolj[p].Magnitude) + { + p = i; + } + } + + if (p != j) + { + for (var k = 0; k < order; k++) + { + var indexk = k * order; + var indexkp = indexk + p; + var indexkj = indexk + j; + var temp = data[indexkp]; + data[indexkp] = data[indexkj]; + data[indexkj] = temp; + } + + ipiv[j] = p; + } + + // Compute multipliers. + if (j < order & data[indexjj] != 0.0) + { + for (var i = j + 1; i < order; i++) + { + data[indexj + i] /= data[indexjj]; + } + } + } + } + + /// + /// Computes the inverse of matrix using LU factorization. + /// + /// The N by N matrix to invert. Contains the inverse On exit. + /// The order of the square matrix . + /// This is equivalent to the GETRF and GETRI LAPACK routines. + public void LUInverse(Complex[] a, int order) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (a.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); + } + + var ipiv = new int[order]; + LUFactor(a, order, ipiv); + LUInverseFactored(a, order, ipiv); + } + + /// + /// Computes the inverse of a previously factored matrix. + /// + /// The LU factored N by N matrix. Contains the inverse On exit. + /// The order of the square matrix . + /// The pivot indices of . + /// This is equivalent to the GETRI LAPACK routine. + public 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); + CommonParallel.For(0, a.Length, index => a[index] = inverse[index]); + } + + /// + /// Computes the inverse of matrix using LU factorization. + /// + /// The N by N matrix to invert. Contains the inverse On exit. + /// The order of the square matrix . + /// 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. + /// This is equivalent to the GETRF and GETRI LAPACK routines. + public void LUInverse(Complex[] a, int order, Complex[] work) + { + LUInverse(a, order); + } + + /// + /// Computes the inverse of a previously factored matrix. + /// + /// The LU factored N by N matrix. Contains the inverse On exit. + /// The order of the square matrix . + /// The pivot indices of . + /// 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. + /// This is equivalent to the GETRI LAPACK routine. + public void LUInverseFactored(Complex[] a, int order, int[] ipiv, Complex[] work) + { + LUInverseFactored(a, order, ipiv); + } + + /// + /// Solves A*X=B for X using LU factorization. + /// + /// The number of columns of B. + /// The square matrix A. + /// The order of the square matrix . + /// The B matrix. + /// This is equivalent to the GETRF and GETRS LAPACK routines. + public 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"); + } + + var ipiv = new int[order]; + LUFactor(a, order, ipiv); + LUSolveFactored(columnsOfB, a, order, ipiv, b); + } + + /// + /// Solves A*X=B for X using a previously factored A matrix. + /// + /// The number of columns of B. + /// The factored A matrix. + /// The order of the square matrix . + /// The pivot indices of . + /// The B matrix. + /// This is equivalent to the GETRS LAPACK routine. + public void LUSolveFactored(int columnsOfB, Complex[] a, int order, int[] ipiv, Complex[] b) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (ipiv == null) + { + throw new ArgumentNullException("ipiv"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (a.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); + } + + if (ipiv.Length != order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); + } + + if (b.Length != order * columnsOfB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + // Compute the column vector P*B + for (var i = 0; i < ipiv.Length; i++) + { + if (ipiv[i] == i) + { + continue; + } + + var p = ipiv[i]; + for (var j = 0; j < columnsOfB; j++) + { + var indexk = j * order; + var indexkp = indexk + p; + var indexkj = indexk + i; + var temp = b[indexkp]; + b[indexkp] = b[indexkj]; + b[indexkj] = temp; + } + } + + // Solve L*Y = P*B + for (var k = 0; k < order; k++) + { + var korder = k * order; + for (var i = k + 1; i < order; i++) + { + for (var j = 0; j < columnsOfB; j++) + { + var index = j * order; + b[i + index] -= b[k + index] * a[i + korder]; + } + } + } + + // Solve U*X = Y; + for (var k = order - 1; k >= 0; k--) + { + var korder = k + (k * order); + for (var j = 0; j < columnsOfB; j++) + { + b[k + (j * order)] /= a[korder]; + } + + korder = k * order; + for (var i = 0; i < k; i++) + { + for (var j = 0; j < columnsOfB; j++) + { + var index = j * order; + b[i + index] -= b[k + index] * a[i + korder]; + } + } + } + } + + /// + /// Solves A*X=B for X using LU factorization. + /// + /// How to transpose the matrix. + /// The number of columns of B. + /// The square matrix A. + /// The order of the square matrix . + /// The B matrix. + /// This is equivalent to the GETRF and GETRS LAPACK routines. + public void LUSolve(Transpose transposeA, 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"); + } + + var ipiv = new int[order]; + LUFactor(a, order, ipiv); + LUSolveFactored(transposeA, columnsOfB, a, order, ipiv, b); + } + + /// + /// Solves A*X=B for X using a previously factored A matrix. + /// + /// How to transpose the matrix. + /// The number of columns of B. + /// The factored A matrix. + /// The order of the square matrix . + /// The pivot indices of . + /// The B matrix. + /// This is equivalent to the GETRS LAPACK routine. + public void LUSolveFactored(Transpose transposeA, int columnsOfB, Complex[] a, int order, int[] ipiv, Complex[] b) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (ipiv == null) + { + throw new ArgumentNullException("ipiv"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (a.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); + } + + if (ipiv.Length != order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); + } + + if (b.Length != order * columnsOfB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (transposeA == Transpose.Transpose) + { + var aT = new Complex[a.Length]; + for (var i = 0; i < order; i++) + { + for (var j = 0; j < order; j++) + { + aT[(j * order) + i] = a[(i * order) + j]; + } + } + + LUSolveFactored(columnsOfB, aT, order, ipiv, b); + } + else if (transposeA == Transpose.ConjugateTranspose) + { + var acT = new Complex[a.Length]; + for (var i = 0; i < order; i++) + { + for (var j = 0; j < order; j++) + { + acT[(j * order) + i] = a[(i * order) + j].Conjugate(); + } + } + + LUSolveFactored(columnsOfB, acT, order, ipiv, b); + } + else + { + LUSolveFactored(columnsOfB, a, order, ipiv, b); + } + } + + /// + /// Computes the Cholesky factorization of A. + /// + /// On entry, a square, positive definite matrix. On exit, the matrix is overwritten with the + /// the Cholesky factorization. + /// The number of rows or columns in the matrix. + /// This is equivalent to the POTRF LAPACK routine. + public void CholeskyFactor(Complex[] a, int order) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + var tmpColumn = new Complex[order]; + + // Main loop - along the diagonal + for (var ij = 0; ij < order; ij++) + { + // "Pivot" element + var tmpVal = a[(ij * order) + ij]; + + if (tmpVal.Real > 0.0) + { + tmpVal = tmpVal.SquareRoot(); + a[(ij * order) + ij] = tmpVal; + tmpColumn[ij] = tmpVal; + + // Calculate multipliers and copy to local column + // Current column, below the diagonal + for (var i = ij + 1; i < order; i++) + { + a[(ij * order) + i] /= tmpVal; + tmpColumn[i] = a[(ij * order) + i]; + } + + // Remaining columns, below the diagonal + DoCholeskyStep(a, order, ij + 1, order, tmpColumn, Control.NumberOfParallelWorkerThreads); + } + else + { + throw new ArgumentException(Resources.ArgumentMatrixPositiveDefinite); + } + + for (var i = ij + 1; i < order; i++) + { + a[(i * order) + ij] = 0.0; + } + } + } + + /// + /// Calculate Cholesky step + /// + /// Factor matrix + /// Number of rows + /// Column start + /// Total columns + /// Multipliears calculated previously + /// Number of available processors + private static void DoCholeskyStep(Complex[] data, int rowDim, int firstCol, int colLimit, Complex[] multipliers, int availableCores) + { + var tmpColCount = colLimit - firstCol; + + if ((availableCores > 1) && (tmpColCount > 200)) + { + var tmpSplit = firstCol + (tmpColCount / 3); + var tmpCores = availableCores / 2; + + CommonParallel.Invoke( + () => DoCholeskyStep(data, rowDim, firstCol, tmpSplit, multipliers, tmpCores), + () => DoCholeskyStep(data, rowDim, tmpSplit, colLimit, multipliers, tmpCores)); + } + else + { + for (var j = firstCol; j < colLimit; j++) + { + var tmpVal = multipliers[j]; + for (var i = j; i < rowDim; i++) + { + data[(j * rowDim) + i] -= multipliers[i] * tmpVal.Conjugate(); + } + } + } + } + + /// + /// Solves A*X=B for X using Cholesky factorization. + /// + /// The square, positive definite matrix A. + /// The number of rows and columns in A. + /// The B matrix. + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. + /// This is equivalent to the POTRF add POTRS LAPACK routines. + public void CholeskySolve(Complex[] a, int orderA, Complex[] b, int rowsB, int columnsB) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (orderA != rowsB) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions); + } + + if (ReferenceEquals(a, b)) + { + throw new ArgumentException(Resources.ArgumentReferenceDifferent); + } + + CholeskyFactor(a, orderA); + CholeskySolveFactored(a, orderA, b, rowsB, columnsB); + } + + /// + /// Solves A*X=B for X using a previously factored A matrix. + /// + /// The square, positive definite matrix A. + /// The number of rows and columns in A. + /// The B matrix. + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. + /// This is equivalent to the POTRS LAPACK routine. + public void CholeskySolveFactored(Complex[] a, int orderA, Complex[] b, int rowsB, int columnsB) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (orderA != rowsB) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions); + } + + if (ReferenceEquals(a, b)) + { + throw new ArgumentException(Resources.ArgumentReferenceDifferent); + } + + CommonParallel.For( + 0, + columnsB, + c => + { + var cindex = c * orderA; + + // Solve L*Y = B; + Complex sum; + for (var i = 0; i < orderA; i++) + { + sum = b[cindex + i]; + for (var k = i - 1; k >= 0; k--) + { + sum -= a[(k * orderA) + i] * b[cindex + k]; + } + + b[cindex + i] = sum / a[(i * orderA) + i]; + } + + // Solve L'*X = Y; + for (var i = orderA - 1; i >= 0; i--) + { + sum = b[cindex + i]; + var iindex = i * orderA; + for (var k = i + 1; k < orderA; k++) + { + sum -= a[iindex + k].Conjugate() * b[cindex + k]; + } + + b[cindex + i] = sum / a[iindex + i]; + } + }); + } + + /// + /// Computes the QR factorization of A. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the R matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A M by M matrix that holds the Q matrix of the + /// QR factorization. + /// This is similar to the GEQRF and ORGQR LAPACK routines. + public void QRFactor(Complex[] r, int rowsR, int columnsR, Complex[] q) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + var work = new Complex[rowsR * rowsR]; + QRFactor(r, rowsR, columnsR, q, work); + } + + /// + /// Computes the QR factorization of A. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the R matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A M by M matrix that holds the Q matrix of the + /// QR factorization. + /// 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. + /// This is similar to the GEQRF and ORGQR LAPACK routines. + public void QRFactor(Complex[] r, int rowsR, int columnsR, Complex[] q, Complex[] work) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (work == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + if (work.Length < rowsR * rowsR) + { + work[0] = rowsR * rowsR; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + CommonParallel.For(0, rowsR, i => q[(i * rowsR) + i] = Complex.One); + + var minmn = Math.Min(rowsR, columnsR); + for (var i = 0; i < minmn; i++) + { + GenerateColumn(work, r, rowsR, i, i); + ComputeQR(work, i, r, i, rowsR, i + 1, columnsR, Control.NumberOfParallelWorkerThreads); + } + + for (var i = minmn - 1; i >= 0; i--) + { + ComputeQR(work, i, q, i, rowsR, i, rowsR, Control.NumberOfParallelWorkerThreads); + } + + work[0] = rowsR * rowsR; + } + + #region QR Factor Helper functions + + /// + /// Perform calculation of Q or R + /// + /// Work array + /// Index of colunn in work array + /// Q or R matrices + /// The first row in + /// The last row + /// The first column + /// The last column + /// Number of available CPUs + private static void ComputeQR(Complex[] work, int workIndex, Complex[] a, int rowStart, int rowCount, int columnStart, int columnCount, int availableCores) + { + if (rowStart > rowCount || columnStart > columnCount) + { + return; + } + + var tmpColCount = columnCount - columnStart; + + if ((availableCores > 1) && (tmpColCount > 200)) + { + var tmpSplit = columnStart + (tmpColCount / 2); + var tmpCores = availableCores / 2; + + CommonParallel.Invoke( + () => ComputeQR(work, workIndex, a, rowStart, rowCount, columnStart, tmpSplit, tmpCores), + () => ComputeQR(work, workIndex, a, rowStart, rowCount, tmpSplit, columnCount, tmpCores)); + } + else + { + for (var j = columnStart; j < columnCount; j++) + { + var scale = Complex.Zero; + for (var i = rowStart; i < rowCount; i++) + { + scale += work[(workIndex * rowCount) + i - rowStart] * a[(j * rowCount) + i]; + } + + for (var i = rowStart; i < rowCount; i++) + { + a[(j * rowCount) + i] -= work[(workIndex * rowCount) + i - rowStart].Conjugate() * scale; + } + } + } + } + + /// + /// Generate column from initial matrix to work array + /// + /// Work array + /// Initial matrix + /// The number of rows in matrix + /// The firts row + /// Column index + private static void GenerateColumn(Complex[] work, Complex[] a, int rowCount, int row, int column) + { + var tmp = column * rowCount; + var index = tmp + row; + + CommonParallel.For( + row, + rowCount, + i => + { + var iIndex = tmp + i; + work[iIndex - row] = a[iIndex]; + a[iIndex] = Complex.Zero; + }); + + var norm = Complex.Zero; + for (var i = 0; i < rowCount - row; ++i) + { + var index1 = tmp + i; + norm += work[index1].Magnitude * work[index1].Magnitude; + } + + norm = norm.SquareRoot(); + if (row == rowCount - 1 || norm.Magnitude == 0) + { + a[index] = -work[tmp]; + work[tmp] = new Complex(2.0, 0).SquareRoot(); + return; + } + + if (work[tmp].Magnitude != 0.0) + { + norm = norm.Magnitude * (work[tmp] / work[tmp].Magnitude); + } + + a[index] = -norm; + CommonParallel.For(0, rowCount - row, i => work[tmp + i] /= norm); + work[tmp] += 1.0; + + var s = (1.0 / work[tmp]).SquareRoot(); + CommonParallel.For(0, rowCount - row, i => work[tmp + i] = work[tmp + i].Conjugate() * s); + } + + #endregion + + /// + /// Solves A*X=B for X using QR factorization of A. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the R matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A M by M matrix that holds the Q matrix of the + /// QR factorization. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + public void QRSolve(Complex[] r, int rowsR, int columnsR, Complex[] q, Complex[] b, int columnsB, Complex[] x) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (b == null) + { + throw new ArgumentNullException("q"); + } + + if (x == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + if (b.Length != rowsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + } + + var work = new Complex[rowsR * rowsR]; + QRSolve(r, rowsR, columnsR, q, b, columnsB, x, work); + } + + /// + /// Solves A*X=B for X using QR factorization of A. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the R matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A M by M matrix that holds the Q matrix of the + /// QR factorization. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + /// 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. + public void QRSolve(Complex[] r, int rowsR, int columnsR, Complex[] q, Complex[] b, int columnsB, Complex[] x, Complex[] work) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (b == null) + { + throw new ArgumentNullException("q"); + } + + if (x == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + if (b.Length != rowsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + } + + if (work.Length < rowsR * rowsR) + { + work[0] = rowsR * rowsR; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + QRFactor(r, rowsR, columnsR, q, work); + QRSolveFactored(q, r, rowsR, columnsR, b, columnsB, x); + + work[0] = rowsR * rowsR; + } + + /// + /// Solves A*X=B for X using a previously QR factored matrix. + /// + /// The Q matrix obtained by calling . + /// The R matrix obtained by calling . + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + public void QRSolveFactored(Complex[] q, Complex[] r, int rowsR, int columnsR, Complex[] b, int columnsB, Complex[] x) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (b == null) + { + throw new ArgumentNullException("q"); + } + + if (x == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + if (b.Length != rowsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + } + + var sol = new Complex[b.Length]; + + // Copy B matrix to "sol", so B data will not be changed + CommonParallel.For(0, b.Length, index => sol[index] = b[index]); + + // Compute Y = transpose(Q)*B + var column = new Complex[rowsR]; + for (var j = 0; j < columnsB; j++) + { + var jm = j * rowsR; + CommonParallel.For(0, rowsR, k => column[k] = sol[jm + k]); + CommonParallel.For( + 0, + rowsR, + i => + { + var im = i * rowsR; + sol[jm + i] = CommonParallel.Aggregate(0, rowsR, k => q[im + k].Conjugate() * column[k]); + }); + } + + // Solve R*X = Y; + for (var k = columnsR - 1; k >= 0; k--) + { + var km = k * rowsR; + for (var j = 0; j < columnsB; j++) + { + sol[(j * rowsR) + k] /= r[km + k]; + } + + for (var i = 0; i < k; i++) + { + for (var j = 0; j < columnsB; j++) + { + var jm = j * rowsR; + sol[jm + i] -= sol[jm + k] * r[km + i]; + } + } + } + + // Fill result matrix + CommonParallel.For( + 0, + columnsR, + row => + { + for (var col = 0; col < columnsB; col++) + { + x[(col * columnsR) + row] = sol[row + (col * rowsR)]; + } + }); + } + + /// + /// Computes the singular value decomposition of A. + /// + /// Compute the singular U and VT vectors or not. + /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The singular values of A in ascending value. + /// If is true, on exit U contains the left + /// singular vectors. + /// If is true, on exit VT contains the transposed + /// right singular vectors. + /// This is equivalent to the GESVD LAPACK routine. + public void SingularValueDecomposition(bool computeVectors, Complex[] a, int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (s == null) + { + throw new ArgumentNullException("s"); + } + + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (vt == null) + { + throw new ArgumentNullException("vt"); + } + + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } + + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } + + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } + + // Actually "work = new Complex[aRows]" is acceptable size of work array. I set size proposed in method description + var work = new Complex[(2 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA)]; + SingularValueDecomposition(computeVectors, a, rowsA, columnsA, s, u, vt, work); + } + + /// + /// Computes the singular value decomposition of A. + /// + /// Compute the singular U and VT vectors or not. + /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The singular values of A in ascending value. + /// If is true, on exit U contains the left + /// singular vectors. + /// If is true, on exit VT contains the transposed + /// right singular vectors. + /// The work array. For real matrices, the work array should be at least + /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). + /// On exit, work[0] contains the optimal work size value. + /// This is equivalent to the GESVD LAPACK routine. + public void SingularValueDecomposition(bool computeVectors, Complex[] a, int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt, Complex[] work) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (s == null) + { + throw new ArgumentNullException("s"); + } + + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (vt == null) + { + throw new ArgumentNullException("vt"); + } + + if (work == null) + { + throw new ArgumentNullException("work"); + } + + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } + + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } + + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } + + if (work.Length == 0) + { + throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); + } + + if (work.Length < rowsA) + { + work[0] = rowsA; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + const int Maxiter = 1000; + + var e = new Complex[columnsA]; + var v = new Complex[vt.Length]; + var stemp = new Complex[Math.Min(rowsA + 1, columnsA)]; + + int i, j, l, lp1; + + var cs = 0.0; + var sn = 0.0; + Complex t; + + var ncu = rowsA; + + // Reduce matrix to bidiagonal form, storing the diagonal elements + // in "s" and the super-diagonal elements in "e". + var nct = Math.Min(rowsA - 1, columnsA); + var nrt = Math.Max(0, Math.Min(columnsA - 2, rowsA)); + 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 vector s[l]. + var sum = 0.0; + for (i = l; i < rowsA; i++) + { + sum += a[(l * rowsA) + i].Magnitude * a[(l * rowsA) + i].Magnitude; + } + + stemp[l] = Math.Sqrt(sum); + if (stemp[l] != 0.0) + { + if (a[(l * rowsA) + l] != 0.0) + { + stemp[l] = stemp[l].Magnitude * (a[(l * rowsA) + l] / a[(l * rowsA) + l].Magnitude); + } + + // A part of column "l" of Matrix A from row "l" to end multiply by 1.0 / s[l] + for (i = l; i < rowsA; i++) + { + a[(l * rowsA) + i] = a[(l * rowsA) + i] * (1.0 / stemp[l]); + } + + a[(l * rowsA) + l] = 1.0 + a[(l * rowsA) + l]; + } + + stemp[l] = -stemp[l]; + } + + for (j = lp1; j < columnsA; j++) + { + if (l < nct) + { + if (stemp[l] != 0.0) + { + // Apply the transformation. + t = 0.0; + for (i = l; i < rowsA; i++) + { + t += a[(l * rowsA) + i].Conjugate() * a[(j * rowsA) + i]; + } + + t = -t / a[(l * rowsA) + l]; + + for (var ii = l; ii < rowsA; ii++) + { + a[(j * rowsA) + ii] += t * a[(l * rowsA) + ii]; + } + } + } + + // Place the l-th row of matrix into "e" for the + // subsequent calculation of the row transformation. + e[j] = a[(j * rowsA) + l].Conjugate(); + } + + if (computeVectors && l < nct) + { + // Place the transformation in "u" for subsequent back multiplication. + for (i = l; i < rowsA; i++) + { + u[(l * rowsA) + i] = a[(l * rowsA) + i]; + } + } + + if (l >= nrt) + { + continue; + } + + // Compute the l-th row transformation and place the l-th super-diagonal in e(l). + var enorm = 0.0; + for (i = lp1; i < e.Length; i++) + { + enorm += e[i].Magnitude * e[i].Magnitude; + } + + e[l] = Math.Sqrt(enorm); + if (e[l] != 0.0) + { + if (e[lp1] != 0.0) + { + e[l] = e[l].Magnitude * (e[lp1] / e[lp1].Magnitude); + } + + // Scale vector "e" from "lp1" by 1.0 / e[l] + for (i = lp1; i < e.Length; i++) + { + e[i] = e[i] * (1.0 / e[l]); + } + + e[lp1] = 1.0 + e[lp1]; + } + + e[l] = -e[l].Conjugate(); + + if (lp1 < rowsA && e[l] != 0.0) + { + // Apply the transformation. + for (i = lp1; i < rowsA; i++) + { + work[i] = 0.0; + } + + for (j = lp1; j < columnsA; j++) + { + for (var ii = lp1; ii < rowsA; ii++) + { + work[ii] += e[j] * a[(j * rowsA) + ii]; + } + } + + for (j = lp1; j < columnsA; j++) + { + var ww = (-e[j] / e[lp1]).Conjugate(); + for (var ii = lp1; ii < rowsA; ii++) + { + a[(j * rowsA) + ii] += ww * work[ii]; + } + } + } + + if (!computeVectors) + { + continue; + } + + // Place the transformation in v for subsequent back multiplication. + for (i = lp1; i < columnsA; i++) + { + v[(l * columnsA) + i] = e[i]; + } + } + + // Set up the final bidiagonal matrix or order m. + var m = Math.Min(columnsA, rowsA + 1); + var nctp1 = nct + 1; + var nrtp1 = nrt + 1; + if (nct < columnsA) + { + stemp[nctp1 - 1] = a[((nctp1 - 1) * rowsA) + (nctp1 - 1)]; + } + + if (rowsA < m) + { + stemp[m - 1] = 0.0; + } + + if (nrtp1 < m) + { + e[nrtp1 - 1] = a[((m - 1) * rowsA) + (nrtp1 - 1)]; + } + + e[m - 1] = 0.0; + + // If required, generate "u". + if (computeVectors) + { + for (j = nctp1 - 1; j < ncu; j++) + { + for (i = 0; i < rowsA; i++) + { + u[(j * rowsA) + i] = 0.0; + } + + u[(j * rowsA) + j] = 1.0; + } + + for (l = nct - 1; l >= 0; l--) + { + if (stemp[l] != 0.0) + { + for (j = l + 1; j < ncu; j++) + { + t = 0.0; + for (i = l; i < rowsA; i++) + { + t += u[(l * rowsA) + i].Conjugate() * u[(j * rowsA) + i]; + } + + t = -t / u[(l * rowsA) + l]; + for (var ii = l; ii < rowsA; ii++) + { + u[(j * rowsA) + ii] += t * u[(l * rowsA) + ii]; + } + } + + // A part of column "l" of matrix A from row "l" to end multiply by -1.0 + for (i = l; i < rowsA; i++) + { + u[(l * rowsA) + i] = u[(l * rowsA) + i] * -1.0; + } + + u[(l * rowsA) + l] = 1.0 + u[(l * rowsA) + l]; + for (i = 0; i < l; i++) + { + u[(l * rowsA) + i] = 0.0; + } + } + else + { + for (i = 0; i < rowsA; i++) + { + u[(l * rowsA) + i] = 0.0; + } + + u[(l * rowsA) + l] = 1.0; + } + } + } + + // If it is required, generate v. + if (computeVectors) + { + for (l = columnsA - 1; l >= 0; l--) + { + lp1 = l + 1; + if (l < nrt) + { + if (e[l] != 0.0) + { + for (j = lp1; j < columnsA; j++) + { + t = 0.0; + for (i = lp1; i < columnsA; i++) + { + t += v[(l * columnsA) + i].Conjugate() * v[(j * columnsA) + i]; + } + + t = -t / v[(l * columnsA) + lp1]; + for (var ii = l; ii < columnsA; ii++) + { + v[(j * columnsA) + ii] += t * v[(l * columnsA) + ii]; + } + } + } + } + + for (i = 0; i < columnsA; i++) + { + v[(l * columnsA) + i] = 0.0; + } + + v[(l * columnsA) + l] = 1.0; + } + } + + // Transform "s" and "e" so that they are double + for (i = 0; i < m; i++) + { + Complex r; + if (stemp[i] != 0.0) + { + t = stemp[i].Magnitude; + r = stemp[i] / t; + stemp[i] = t; + if (i < m - 1) + { + e[i] = e[i] / r; + } + + if (computeVectors) + { + // A part of column "i" of matrix U from row 0 to end multiply by r + for (j = 0; j < rowsA; j++) + { + u[(i * rowsA) + j] = u[(i * rowsA) + j] * r; + } + } + } + + // Exit + if (i == m - 1) + { + break; + } + + if (e[i] == 0.0) + { + continue; + } + + t = e[i].Magnitude; + r = t / e[i]; + e[i] = t; + stemp[i + 1] = stemp[i + 1] * r; + if (!computeVectors) + { + continue; + } + + // A part of column "i+1" of matrix VT from row 0 to end multiply by r + for (j = 0; j < columnsA; j++) + { + v[((i + 1) * columnsA) + j] = v[((i + 1) * columnsA) + j] * 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. + 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 mS[m] and e[l-1] are negligible and l < m + // kase = 2: if mS[l] is negligible and l < m + // kase = 3: if e[l-1] is negligible, l < m, and mS[l, ..., mS[m] are not negligible (qr step). + // kase = 4: if e[m-1] is negligible (convergence). + double ztest; + double test; + for (l = m - 2; l >= 0; l--) + { + test = stemp[l].Magnitude + stemp[l + 1].Magnitude; + ztest = test + e[l].Magnitude; + if (ztest.AlmostEqualInDecimalPlaces(test, 15)) + { + e[l] = 0.0; + break; + } + } + + int kase; + if (l == m - 2) + { + kase = 4; + } + else + { + int ls; + for (ls = m - 1; ls > l; ls--) + { + test = 0.0; + if (ls != m - 1) + { + test = test + e[ls].Magnitude; + } + + if (ls != l + 1) + { + test = test + e[ls - 1].Magnitude; + } + + ztest = test + stemp[ls].Magnitude; + if (ztest.AlmostEqualInDecimalPlaces(test, 15)) + { + stemp[ls] = 0.0; + 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; + double f; + switch (kase) + { + // Deflate negligible s[m]. + case 1: + f = e[m - 2].Real; + e[m - 2] = 0.0; + double t1; + for (var kk = l; kk < m - 1; kk++) + { + k = m - 2 - kk + l; + t1 = stemp[k].Real; + Drotg(ref t1, ref f, ref cs, ref sn); + stemp[k] = t1; + if (k != l) + { + f = -sn * e[k - 1].Real; + e[k - 1] = cs * e[k - 1]; + } + + if (computeVectors) + { + // Rotate + for (i = 0; i < columnsA; i++) + { + var z = (cs * v[(k * columnsA) + i]) + (sn * v[((m - 1) * columnsA) + i]); + v[((m - 1) * columnsA) + i] = (cs * v[((m - 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); + v[(k * columnsA) + i] = z; + } + } + } + + break; + + // Split at negligible s[l]. + case 2: + f = e[l - 1].Real; + e[l - 1] = 0.0; + for (k = l; k < m; k++) + { + t1 = stemp[k].Real; + Drotg(ref t1, ref f, ref cs, ref sn); + stemp[k] = t1; + f = -sn * e[k].Real; + e[k] = cs * e[k]; + if (computeVectors) + { + // Rotate + for (i = 0; i < rowsA; i++) + { + var z = (cs * u[(k * rowsA) + i]) + (sn * u[((l - 1) * rowsA) + i]); + u[((l - 1) * rowsA) + i] = (cs * u[((l - 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); + u[(k * rowsA) + i] = z; + } + } + } + + break; + + // Perform one qr step. + case 3: + // calculate the shift. + var scale = 0.0; + scale = Math.Max(scale, stemp[m - 1].Magnitude); + scale = Math.Max(scale, stemp[m - 2].Magnitude); + scale = Math.Max(scale, e[m - 2].Magnitude); + scale = Math.Max(scale, stemp[l].Magnitude); + scale = Math.Max(scale, e[l].Magnitude); + var sm = stemp[m - 1].Real / scale; + var smm1 = stemp[m - 2].Real / scale; + var emm1 = e[m - 2].Real / scale; + var sl = stemp[l].Real / scale; + var el = e[l].Real / scale; + var b = (((smm1 + sm) * (smm1 - sm)) + (emm1 * emm1)) / 2.0; + var c = (sm * emm1) * (sm * emm1); + var shift = 0.0; + if (b != 0.0 || c != 0.0) + { + shift = 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 * stemp[k].Real) + (sn * e[k].Real); + e[k] = (cs * e[k]) - (sn * stemp[k]); + g = sn * stemp[k + 1].Real; + stemp[k + 1] = cs * stemp[k + 1]; + if (computeVectors) + { + for (i = 0; i < columnsA; i++) + { + var z = (cs * v[(k * columnsA) + i]) + (sn * v[((k + 1) * columnsA) + i]); + v[((k + 1) * columnsA) + i] = (cs * v[((k + 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); + v[(k * columnsA) + i] = z; + } + } + + Drotg(ref f, ref g, ref cs, ref sn); + stemp[k] = f; + f = (cs * e[k].Real) + (sn * stemp[k + 1].Real); + stemp[k + 1] = -(sn * e[k]) + (cs * stemp[k + 1]); + g = sn * e[k + 1].Real; + e[k + 1] = cs * e[k + 1]; + if (computeVectors && k < rowsA) + { + for (i = 0; i < rowsA; i++) + { + var z = (cs * u[(k * rowsA) + i]) + (sn * u[((k + 1) * rowsA) + i]); + u[((k + 1) * rowsA) + i] = (cs * u[((k + 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); + u[(k * rowsA) + i] = z; + } + } + } + + e[m - 2] = f; + iter = iter + 1; + break; + + // Convergence + case 4: + + // Make the singular value positive + if (stemp[l].Real < 0.0) + { + stemp[l] = -stemp[l]; + if (computeVectors) + { + // A part of column "l" of matrix VT from row 0 to end multiply by -1 + for (i = 0; i < columnsA; i++) + { + v[(l * columnsA) + i] = v[(l * columnsA) + i] * -1.0; + } + } + } + + // Order the singular value. + while (l != mn - 1) + { + if (stemp[l].Real >= stemp[l + 1].Real) + { + break; + } + + t = stemp[l]; + stemp[l] = stemp[l + 1]; + stemp[l + 1] = t; + if (computeVectors && l < columnsA) + { + // Swap columns l, l + 1 + for (i = 0; i < columnsA; i++) + { + var z = v[(l * columnsA) + i]; + v[(l * columnsA) + i] = v[((l + 1) * columnsA) + i]; + v[((l + 1) * columnsA) + i] = z; + } + } + + if (computeVectors && l < rowsA) + { + // Swap columns l, l + 1 + for (i = 0; i < rowsA; i++) + { + var z = u[(l * rowsA) + i]; + u[(l * rowsA) + i] = u[((l + 1) * rowsA) + i]; + u[((l + 1) * rowsA) + i] = z; + } + } + + l = l + 1; + } + + iter = 0; + m = m - 1; + break; + } + } + + if (computeVectors) + { + // Finally transpose "v" to get "vt" matrix + for (i = 0; i < columnsA; i++) + { + for (j = 0; j < columnsA; j++) + { + vt[(j * columnsA) + i] = v[(i * columnsA) + j].Conjugate(); + } + } + } + + // Copy stemp to s with size adjustment. We are using ported copy of linpack's svd code and it uses + // a singular vector of length rows+1 when rows < columns. The last element is not used and needs to be removed. + // We should port lapack's svd routine to remove this problem. + CommonParallel.For(0, Math.Min(rowsA, columnsA), index => s[index] = stemp[index]); + + // On return the first element of the work array stores the min size of the work array could have been + // work[0] = Math.Max(3 * Math.Min(aRows, aColumns) + Math.Max(aRows, aColumns), 5 * Math.Min(aRows, aColumns)); + work[0] = rowsA; + } + + /// + /// Solves A*X=B for X using the singular value decomposition of A. + /// + /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The singular values of A in ascending value. + /// On exit U contains the left singular vectors. + /// On exit VT contains the transposed right singular vectors. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + public void SvdSolve(Complex[] a, int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int columnsB, Complex[] x) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (s == null) + { + throw new ArgumentNullException("s"); + } + + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (vt == null) + { + throw new ArgumentNullException("vt"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } + + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } + + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } + + if (b.Length != rowsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + // Actually "work = new Complex[aRows]" is acceptable size of work array. I set size proposed in method description + var work = new Complex[(2 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA)]; + SvdSolve(a, rowsA, columnsA, s, u, vt, b, columnsB, x, work); + } + + /// + /// Solves A*X=B for X using the singular value decomposition of A. + /// + /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The singular values of A in ascending value. + /// On exit U contains the left singular vectors. + /// On exit VT contains the transposed right singular vectors. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + /// The work array. For real matrices, the work array should be at least + /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). + /// On exit, work[0] contains the optimal work size value. + public void SvdSolve(Complex[] a, int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int columnsB, Complex[] x, Complex[] work) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (s == null) + { + throw new ArgumentNullException("s"); + } + + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (vt == null) + { + throw new ArgumentNullException("vt"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } + + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } + + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } + + if (b.Length != rowsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (work.Length == 0) + { + throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); + } + + if (work.Length < rowsA) + { + work[0] = rowsA; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + SingularValueDecomposition(true, a, rowsA, columnsA, s, u, vt, work); + SvdSolveFactored(rowsA, columnsA, s, u, vt, b, columnsB, x); + } + + /// + /// Solves A*X=B for X using a previously SVD decomposed matrix. + /// + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The s values returned by . + /// The left singular vectors returned by . + /// The right singular vectors returned by . + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + public void SvdSolveFactored(int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int columnsB, Complex[] x) + { + if (s == null) + { + throw new ArgumentNullException("s"); + } + + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (vt == null) + { + throw new ArgumentNullException("vt"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } + + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } + + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } + + if (b.Length != rowsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + var mn = Math.Min(rowsA, columnsA); + var tmp = new Complex[columnsA]; + + for (var k = 0; k < columnsB; k++) + { + for (var j = 0; j < columnsA; j++) + { + var value = Complex.Zero; + if (j < mn) + { + for (var i = 0; i < rowsA; i++) + { + value += u[(j * rowsA) + i].Conjugate() * b[(k * rowsA) + i]; + } + + value /= s[j]; + } + + tmp[j] = value; + } + + for (var j = 0; j < columnsA; j++) + { + var value = Complex.Zero; + for (var i = 0; i < columnsA; i++) + { + value += vt[(j * columnsA) + i].Conjugate() * tmp[i]; + } + + x[(k * columnsA) + j] = value; + } + } + } + } +} diff --git a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex32.cs b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex32.cs new file mode 100644 index 00000000..d26a4b04 --- /dev/null +++ b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Complex32.cs @@ -0,0 +1,2872 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// http://mathnetnumerics.codeplex.com +// Copyright (c) 2009-2010 Math.NET +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +namespace MathNet.Numerics.Algorithms.LinearAlgebra +{ + using System; + using System.Numerics; + using Properties; + using Threading; + + /// + /// The managed linear algebra provider. + /// + public partial class ManagedLinearAlgebraProvider : ILinearAlgebraProvider + { + /// + /// Adds a scaled vector to another: y += alpha*x. + /// + /// The vector to update. + /// The value to scale by. + /// The vector to add to . + /// This equivalent to the AXPY BLAS routine. + public void AddVectorToScaledVector(Complex32[] y, Complex32 alpha, Complex32[] x) + { + 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 (alpha == 0.0F) + { + return; + } + + if (alpha == 1.0F) + { + CommonParallel.For(0, y.Length, i => y[i] += x[i]); + } + else + { + CommonParallel.For(0, y.Length, i => y[i] += alpha * x[i]); + } + } + + /// + /// Scales an array. Can be used to scale a vector and a matrix. + /// + /// The scalar. + /// The values to scale. + /// This is equivalent to the SCAL BLAS routine. + public void ScaleArray(Complex32 alpha, Complex32[] x) + { + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (alpha.IsOne()) + { + return; + } + + CommonParallel.For(0, x.Length, i => x[i] = alpha * x[i]); + } + + /// + /// Computes the dot product of x and y. + /// + /// The vector x. + /// The vector y. + /// The dot product of x and y. + /// This is equivalent to the DOT BLAS routine. + public Complex32 DotProduct(Complex32[] x, Complex32[] y) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (y.Length != x.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + var d = new Complex32(0.0F, 0.0F); + + for (var i = 0; i < y.Length; i++) + { + d += y[i] * x[i]; + } + + return d; + } + + /// + /// Does a point wise add of two arrays z = x + y. This can be used + /// to add vectors or matrices. + /// + /// The array x. + /// The array y. + /// The result of the addition. + /// There is no equivalent BLAS routine, but many libraries + /// provide optimized (parallel and/or vectorized) versions of this + /// routine. + public void AddArrays(Complex32[] x, Complex32[] y, Complex32[] result) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (y.Length != x.Length || y.Length != result.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + CommonParallel.For(0, y.Length, i => result[i] = x[i] + y[i]); + } + + /// + /// Does a point wise subtraction of two arrays z = x - y. This can be used + /// to subtract vectors or matrices. + /// + /// The array x. + /// The array y. + /// The result of the subtraction. + /// There is no equivalent BLAS routine, but many libraries + /// provide optimized (parallel and/or vectorized) versions of this + /// routine. + public void SubtractArrays(Complex32[] x, Complex32[] y, Complex32[] result) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (y.Length != x.Length || y.Length != result.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + CommonParallel.For(0, y.Length, i => result[i] = x[i] - y[i]); + } + + /// + /// Does a point wise multiplication of two arrays z = x * y. This can be used + /// to multiple elements of vectors or matrices. + /// + /// The array x. + /// The array y. + /// The result of the point wise multiplication. + /// There is no equivalent BLAS routine, but many libraries + /// provide optimized (parallel and/or vectorized) versions of this + /// routine. + public void PointWiseMultiplyArrays(Complex32[] x, Complex32[] y, Complex32[] result) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (y.Length != x.Length || y.Length != result.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + CommonParallel.For(0, y.Length, i => result[i] = x[i] * y[i]); + } + + /// + /// Does a point wise division of two arrays z = x / y. This can be used + /// to divide elements of vectors or matrices. + /// + /// The array x. + /// The array y. + /// The result of the point wise division. + /// There is no equivalent BLAS routine, but many libraries + /// provide optimized (parallel and/or vectorized) versions of this + /// routine. + public void PointWiseDivideArrays(Complex32[] x, Complex32[] y, Complex32[] result) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (y.Length != x.Length || y.Length != result.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + CommonParallel.For(0, y.Length, index => { result[index] = x[index] / y[index]; }); + } + + /// + /// Computes the requested of the matrix. + /// + /// The type of norm to compute. + /// The number of rows. + /// The number of columns. + /// The matrix to compute the norm from. + /// + /// The requested of the matrix. + /// + public Complex32 MatrixNorm(Norm norm, int rows, int columns, Complex32[] matrix) + { + var ret = 0.0; + switch (norm) + { + case Norm.OneNorm: + for (var j = 0; j < columns; j++) + { + var s = 0.0; + for (var i = 0; i < rows; i++) + { + s += matrix[(j * rows) + i].Magnitude; + } + + ret = Math.Max(ret, s); + } + + break; + case Norm.LargestAbsoluteValue: + + for (var i = 0; i < rows; i++) + { + for (var j = 0; j < columns; j++) + { + ret = Math.Max(matrix[(j * rows) + i].Magnitude, ret); + } + } + + break; + case Norm.InfinityNorm: + for (var i = 0; i < rows; i++) + { + var s = 0.0; + for (var j = 0; j < columns; j++) + { + s += matrix[(j * rows) + i].Magnitude; + } + + ret = Math.Max(ret, s); + } + + break; + case Norm.FrobeniusNorm: + var aat = new Complex32[rows * rows]; + MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.Transpose, 1.0f, matrix, rows, columns, matrix, rows, columns, 0.0f, aat); + + for (var i = 0; i < rows; i++) + { + ret += aat[(i * rows) + i].Magnitude; + } + + ret = Math.Sqrt(ret); + break; + } + + return Convert.ToSingle(ret); + } + + /// + /// Computes the requested of the matrix. + /// + /// The type of norm to compute. + /// The number of rows. + /// The number of columns. + /// The matrix to compute the norm from. + /// The work array. Only used when + /// and needs to be have a length of at least M (number of rows of . + /// + /// The requested of the matrix. + /// + public Complex32 MatrixNorm(Norm norm, int rows, int columns, Complex32[] matrix, Complex32[] work) + { + return MatrixNorm(norm, rows, columns, matrix); + } + + /// + /// Multiples two matrices. result = x * y + /// + /// The x matrix. + /// The number of rows in the x matrix. + /// The number of columns in the x matrix. + /// The y matrix. + /// The number of rows in the y matrix. + /// The number of columns in the y matrix. + /// Where to store the result of the multiplication. + /// This is a simplified version of the BLAS GEMM routine with alpha + /// set to 1.0 and beta set to 0.0, and x and y are not transposed. + public void MatrixMultiply(Complex32[] x, int rowsX, int columnsX, Complex32[] y, int rowsY, int columnsY, Complex32[] result) + { + // First check some basic requirement on the parameters of the matrix multiplication. + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (rowsX * columnsX != x.Length) + { + throw new ArgumentException("x.Length != xRows * xColumns"); + } + + if (rowsY * columnsY != y.Length) + { + throw new ArgumentException("y.Length != yRows * yColumns"); + } + + if (columnsX != rowsY) + { + throw new ArgumentException("xColumns != yRows"); + } + + if (rowsX * columnsY != result.Length) + { + throw new ArgumentException("xRows * yColumns != result.Length"); + } + + // Check whether we will be overwriting any of our inputs and make copies if necessary. + // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory + // as result, we can do it on a row wise basis. We should investigate this. + Complex32[] xdata; + if (ReferenceEquals(x, result)) + { + xdata = (Complex32[])x.Clone(); + } + else + { + xdata = x; + } + + Complex32[] ydata; + if (ReferenceEquals(y, result)) + { + ydata = (Complex32[])y.Clone(); + } + else + { + ydata = y; + } + + // Start the actual matrix multiplication. + // TODO - For small matrices we should get rid of the parallelism because of startup costs. + // Perhaps the following implementations would be a good one + // http://blog.feradz.com/2009/01/cache-efficient-matrix-multiplication/ + MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, Complex32.One, xdata, rowsX, columnsX, ydata, rowsY, columnsY, Complex32.Zero, result); + } + + /// + /// Multiplies two matrices and updates another with the result. c = alpha*op(a)*op(b) + beta*c + /// + /// How to transpose the matrix. + /// How to transpose the matrix. + /// The value to scale matrix. + /// The a matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. + /// The b matrix + /// The number of rows in the matrix. + /// The number of columns in the matrix. + /// The value to scale the matrix. + /// The c matrix. + public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, Complex32 alpha, Complex32[] a, int rowsA, int columnsA, Complex32[] b, int rowsB, int columnsB, Complex32 beta, Complex32[] c) + { + // Choose nonsensical values for the number of rows in c; fill them in depending + // on the operations on a and b. + int rowsC; + + // First check some basic requirement on the parameters of the matrix multiplication. + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if ((int)transposeA > 111 && (int)transposeB > 111) + { + if (rowsA != columnsB) + { + throw new ArgumentOutOfRangeException(); + } + + if (columnsA * rowsB != c.Length) + { + throw new ArgumentOutOfRangeException(); + } + + rowsC = columnsA; + } + else if ((int)transposeA > 111) + { + if (rowsA != rowsB) + { + throw new ArgumentOutOfRangeException(); + } + + if (columnsA * columnsB != c.Length) + { + throw new ArgumentOutOfRangeException(); + } + + rowsC = columnsA; + } + else if ((int)transposeB > 111) + { + if (columnsA != columnsB) + { + throw new ArgumentOutOfRangeException(); + } + + if (rowsA * rowsB != c.Length) + { + throw new ArgumentOutOfRangeException(); + } + + rowsC = rowsA; + } + else + { + if (columnsA != rowsB) + { + throw new ArgumentOutOfRangeException(); + } + + if (rowsA * columnsB != c.Length) + { + throw new ArgumentOutOfRangeException(); + } + + rowsC = rowsA; + } + + if (alpha.IsZero() && beta.IsZero()) + { + Array.Clear(c, 0, c.Length); + return; + } + + // Check whether we will be overwriting any of our inputs and make copies if necessary. + // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory + // as result, we can do it on a row wise basis. We should investigate this. + Complex32[] adata; + if (ReferenceEquals(a, c)) + { + adata = (Complex32[])a.Clone(); + } + else + { + adata = a; + } + + Complex32[] bdata; + if (ReferenceEquals(b, c)) + { + bdata = (Complex32[])b.Clone(); + } + else + { + bdata = b; + } + + if (alpha.IsOne()) + { + if (beta.IsZero()) + { + if ((int)transposeA > 111 && (int)transposeB > 111) + { + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + Complex32 s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s; + } + }); + } + else if ((int)transposeA > 111) + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + Complex32 s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s; + } + }); + } + else if ((int)transposeB > 111) + { + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + Complex32 s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s; + } + }); + } + else + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + Complex32 s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s; + } + }); + } + } + else + { + if ((int)transposeA > 111 && (int)transposeB > 111) + { + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + Complex32 s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (c[jIndex + i] * beta) + s; + } + }); + } + else if ((int)transposeA > 111) + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + Complex32 s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s + (c[jcIndex + i] * beta); + } + }); + } + else if ((int)transposeB > 111) + { + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + Complex32 s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s + (c[jIndex + i] * beta); + } + }); + } + else + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + Complex32 s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s + (c[jcIndex + i] * beta); + } + }); + } + } + } + else + { + if ((int)transposeA > 111 && (int)transposeB > 111) + { + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + Complex32 s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (c[jIndex + i] * beta) + (alpha * s); + } + }); + } + else if ((int)transposeA > 111) + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + Complex32 s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); + } + }); + } + else if ((int)transposeB > 111) + { + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + Complex32 s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (alpha * s) + (c[jIndex + i] * beta); + } + }); + } + else + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + Complex32 s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); + } + }); + } + } + } + + /// + /// Computes the LUP factorization of A. P*A = L*U. + /// + /// An by matrix. The matrix is overwritten with the + /// the LU factorization on exit. The lower triangular factor L is stored in under the diagonal of (the diagonal is always 1.0 + /// for the L factor). The upper triangular factor U is stored on and above the diagonal of . + /// The order of the square matrix . + /// On exit, it contains the pivot indices. The size of the array must be . + /// This is equivalent to the GETRF LAPACK routine. + public void LUFactor(Complex32[] data, int order, int[] ipiv) + { + if (data == null) + { + throw new ArgumentNullException("data"); + } + + if (ipiv == null) + { + throw new ArgumentNullException("ipiv"); + } + + if (data.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "data"); + } + + if (ipiv.Length != order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); + } + + // Initialize the pivot matrix to the identity permutation. + for (var i = 0; i < order; i++) + { + ipiv[i] = i; + } + + var vecLUcolj = new Complex32[order]; + + // Outer loop. + for (var j = 0; j < order; j++) + { + var indexj = j * order; + var indexjj = indexj + j; + + // Make a copy of the j-th column to localize references. + for (var i = 0; i < order; i++) + { + vecLUcolj[i] = data[indexj + i]; + } + + // Apply previous transformations. + for (var i = 0; i < order; i++) + { + // Most of the time is spent in the following dot product. + var kmax = Math.Min(i, j); + var s = Complex32.Zero; + for (var k = 0; k < kmax; k++) + { + s += data[(k * order) + i] * vecLUcolj[k]; + } + + data[indexj + i] = vecLUcolj[i] -= s; + } + + // Find pivot and exchange if necessary. + var p = j; + for (var i = j + 1; i < order; i++) + { + if (vecLUcolj[i].Magnitude > vecLUcolj[p].Magnitude) + { + p = i; + } + } + + if (p != j) + { + for (var k = 0; k < order; k++) + { + var indexk = k * order; + var indexkp = indexk + p; + var indexkj = indexk + j; + var temp = data[indexkp]; + data[indexkp] = data[indexkj]; + data[indexkj] = temp; + } + + ipiv[j] = p; + } + + // Compute multipliers. + if (j < order & data[indexjj] != 0.0f) + { + for (var i = j + 1; i < order; i++) + { + data[indexj + i] /= data[indexjj]; + } + } + } + } + + /// + /// Computes the inverse of matrix using LU factorization. + /// + /// The N by N matrix to invert. Contains the inverse On exit. + /// The order of the square matrix . + /// This is equivalent to the GETRF and GETRI LAPACK routines. + public void LUInverse(Complex32[] a, int order) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (a.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); + } + + var ipiv = new int[order]; + LUFactor(a, order, ipiv); + LUInverseFactored(a, order, ipiv); + } + + /// + /// Computes the inverse of a previously factored matrix. + /// + /// The LU factored N by N matrix. Contains the inverse On exit. + /// The order of the square matrix . + /// The pivot indices of . + /// This is equivalent to the GETRI LAPACK routine. + public void LUInverseFactored(Complex32[] 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 Complex32[a.Length]; + for (var i = 0; i < order; i++) + { + inverse[i + (order * i)] = Complex32.One; + } + + LUSolveFactored(order, a, order, ipiv, inverse); + CommonParallel.For(0, a.Length, index => a[index] = inverse[index]); + } + + /// + /// Computes the inverse of matrix using LU factorization. + /// + /// The N by N matrix to invert. Contains the inverse On exit. + /// The order of the square matrix . + /// 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. + /// This is equivalent to the GETRF and GETRI LAPACK routines. + public void LUInverse(Complex32[] a, int order, Complex32[] work) + { + LUInverse(a, order); + } + + /// + /// Computes the inverse of a previously factored matrix. + /// + /// The LU factored N by N matrix. Contains the inverse On exit. + /// The order of the square matrix . + /// The pivot indices of . + /// 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. + /// This is equivalent to the GETRI LAPACK routine. + public void LUInverseFactored(Complex32[] a, int order, int[] ipiv, Complex32[] work) + { + LUInverseFactored(a, order, ipiv); + } + + /// + /// Solves A*X=B for X using LU factorization. + /// + /// The number of columns of B. + /// The square matrix A. + /// The order of the square matrix . + /// The B matrix. + /// This is equivalent to the GETRF and GETRS LAPACK routines. + public void LUSolve(int columnsOfB, Complex32[] a, int order, Complex32[] 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"); + } + + var ipiv = new int[order]; + LUFactor(a, order, ipiv); + LUSolveFactored(columnsOfB, a, order, ipiv, b); + } + + /// + /// Solves A*X=B for X using a previously factored A matrix. + /// + /// The number of columns of B. + /// The factored A matrix. + /// The order of the square matrix . + /// The pivot indices of . + /// The B matrix. + /// This is equivalent to the GETRS LAPACK routine. + public void LUSolveFactored(int columnsOfB, Complex32[] a, int order, int[] ipiv, Complex32[] b) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (ipiv == null) + { + throw new ArgumentNullException("ipiv"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (a.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); + } + + if (ipiv.Length != order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); + } + + if (b.Length != order * columnsOfB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + // Compute the column vector P*B + for (var i = 0; i < ipiv.Length; i++) + { + if (ipiv[i] == i) + { + continue; + } + + var p = ipiv[i]; + for (var j = 0; j < columnsOfB; j++) + { + var indexk = j * order; + var indexkp = indexk + p; + var indexkj = indexk + i; + var temp = b[indexkp]; + b[indexkp] = b[indexkj]; + b[indexkj] = temp; + } + } + + // Solve L*Y = P*B + for (var k = 0; k < order; k++) + { + var korder = k * order; + for (var i = k + 1; i < order; i++) + { + for (var j = 0; j < columnsOfB; j++) + { + var index = j * order; + b[i + index] -= b[k + index] * a[i + korder]; + } + } + } + + // Solve U*X = Y; + for (var k = order - 1; k >= 0; k--) + { + var korder = k + (k * order); + for (var j = 0; j < columnsOfB; j++) + { + b[k + (j * order)] /= a[korder]; + } + + korder = k * order; + for (var i = 0; i < k; i++) + { + for (var j = 0; j < columnsOfB; j++) + { + var index = j * order; + b[i + index] -= b[k + index] * a[i + korder]; + } + } + } + } + + /// + /// Solves A*X=B for X using LU factorization. + /// + /// How to transpose the matrix. + /// The number of columns of B. + /// The square matrix A. + /// The order of the square matrix . + /// The B matrix. + /// This is equivalent to the GETRF and GETRS LAPACK routines. + public void LUSolve(Transpose transposeA, int columnsOfB, Complex32[] a, int order, Complex32[] 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"); + } + + var ipiv = new int[order]; + LUFactor(a, order, ipiv); + LUSolveFactored(transposeA, columnsOfB, a, order, ipiv, b); + } + + /// + /// Solves A*X=B for X using a previously factored A matrix. + /// + /// How to transpose the matrix. + /// The number of columns of B. + /// The factored A matrix. + /// The order of the square matrix . + /// The pivot indices of . + /// The B matrix. + /// This is equivalent to the GETRS LAPACK routine. + public void LUSolveFactored(Transpose transposeA, int columnsOfB, Complex32[] a, int order, int[] ipiv, Complex32[] b) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (ipiv == null) + { + throw new ArgumentNullException("ipiv"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (a.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); + } + + if (ipiv.Length != order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); + } + + if (b.Length != order * columnsOfB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (transposeA == Transpose.Transpose) + { + var aT = new Complex32[a.Length]; + for (var i = 0; i < order; i++) + { + for (var j = 0; j < order; j++) + { + aT[(j * order) + i] = a[(i * order) + j]; + } + } + + LUSolveFactored(columnsOfB, aT, order, ipiv, b); + } + else if (transposeA == Transpose.ConjugateTranspose) + { + var acT = new Complex32[a.Length]; + for (var i = 0; i < order; i++) + { + for (var j = 0; j < order; j++) + { + acT[(j * order) + i] = a[(i * order) + j].Conjugate(); + } + } + + LUSolveFactored(columnsOfB, acT, order, ipiv, b); + } + else + { + LUSolveFactored(columnsOfB, a, order, ipiv, b); + } + } + + /// + /// Computes the Cholesky factorization of A. + /// + /// On entry, a square, positive definite matrix. On exit, the matrix is overwritten with the + /// the Cholesky factorization. + /// The number of rows or columns in the matrix. + /// This is equivalent to the POTRF LAPACK routine. + public void CholeskyFactor(Complex32[] a, int order) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + var tmpColumn = new Complex32[order]; + + // Main loop - along the diagonal + for (var ij = 0; ij < order; ij++) + { + // "Pivot" element + var tmpVal = a[(ij * order) + ij]; + + if (tmpVal.Real > 0.0) + { + tmpVal = tmpVal.SquareRoot(); + a[(ij * order) + ij] = tmpVal; + tmpColumn[ij] = tmpVal; + + // Calculate multipliers and copy to local column + // Current column, below the diagonal + for (var i = ij + 1; i < order; i++) + { + a[(ij * order) + i] /= tmpVal; + tmpColumn[i] = a[(ij * order) + i]; + } + + // Remaining columns, below the diagonal + DoCholeskyStep(a, order, ij + 1, order, tmpColumn, Control.NumberOfParallelWorkerThreads); + } + else + { + throw new ArgumentException(Resources.ArgumentMatrixPositiveDefinite); + } + + for (var i = ij + 1; i < order; i++) + { + a[(i * order) + ij] = 0.0f; + } + } + } + + /// + /// Calculate Cholesky step + /// + /// Factor matrix + /// Number of rows + /// Column start + /// Total columns + /// Multipliears calculated previously + /// Number of available processors + private static void DoCholeskyStep(Complex32[] data, int rowDim, int firstCol, int colLimit, Complex32[] multipliers, int availableCores) + { + var tmpColCount = colLimit - firstCol; + + if ((availableCores > 1) && (tmpColCount > 200)) + { + var tmpSplit = firstCol + (tmpColCount / 3); + var tmpCores = availableCores / 2; + + CommonParallel.Invoke( + () => DoCholeskyStep(data, rowDim, firstCol, tmpSplit, multipliers, tmpCores), + () => DoCholeskyStep(data, rowDim, tmpSplit, colLimit, multipliers, tmpCores)); + } + else + { + for (var j = firstCol; j < colLimit; j++) + { + var tmpVal = multipliers[j]; + for (var i = j; i < rowDim; i++) + { + data[(j * rowDim) + i] -= multipliers[i] * tmpVal.Conjugate(); + } + } + } + } + + /// + /// Solves A*X=B for X using Cholesky factorization. + /// + /// The square, positive definite matrix A. + /// The number of rows and columns in A. + /// The B matrix. + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. + /// This is equivalent to the POTRF add POTRS LAPACK routines. + public void CholeskySolve(Complex32[] a, int orderA, Complex32[] b, int rowsB, int columnsB) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (orderA != rowsB) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions); + } + + if (ReferenceEquals(a, b)) + { + throw new ArgumentException(Resources.ArgumentReferenceDifferent); + } + + CholeskyFactor(a, orderA); + CholeskySolveFactored(a, orderA, b, rowsB, columnsB); + } + + /// + /// Solves A*X=B for X using a previously factored A matrix. + /// + /// The square, positive definite matrix A. + /// The number of rows and columns in A. + /// The B matrix. + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. + /// This is equivalent to the POTRS LAPACK routine. + public void CholeskySolveFactored(Complex32[] a, int orderA, Complex32[] b, int rowsB, int columnsB) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (orderA != rowsB) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions); + } + + if (ReferenceEquals(a, b)) + { + throw new ArgumentException(Resources.ArgumentReferenceDifferent); + } + + CommonParallel.For( + 0, + columnsB, + c => + { + var cindex = c * orderA; + + // Solve L*Y = B; + Complex32 sum; + for (var i = 0; i < orderA; i++) + { + sum = b[cindex + i]; + for (var k = i - 1; k >= 0; k--) + { + sum -= a[(k * orderA) + i] * b[cindex + k]; + } + + b[cindex + i] = sum / a[(i * orderA) + i]; + } + + // Solve L'*X = Y; + for (var i = orderA - 1; i >= 0; i--) + { + sum = b[cindex + i]; + var iindex = i * orderA; + for (var k = i + 1; k < orderA; k++) + { + sum -= a[iindex + k].Conjugate() * b[cindex + k]; + } + + b[cindex + i] = sum / a[iindex + i]; + } + }); + } + + /// + /// Computes the QR factorization of A. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the R matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A M by M matrix that holds the Q matrix of the + /// QR factorization. + /// This is similar to the GEQRF and ORGQR LAPACK routines. + public void QRFactor(Complex32[] r, int rowsR, int columnsR, Complex32[] q) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + var work = new Complex32[rowsR * rowsR]; + QRFactor(r, rowsR, columnsR, q, work); + } + + /// + /// Computes the QR factorization of A. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the R matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A M by M matrix that holds the Q matrix of the + /// QR factorization. + /// 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. + /// This is similar to the GEQRF and ORGQR LAPACK routines. + public void QRFactor(Complex32[] r, int rowsR, int columnsR, Complex32[] q, Complex32[] work) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (work == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + if (work.Length < rowsR * rowsR) + { + work[0] = rowsR * rowsR; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + CommonParallel.For(0, rowsR, i => q[(i * rowsR) + i] = Complex32.One); + + var minmn = Math.Min(rowsR, columnsR); + for (var i = 0; i < minmn; i++) + { + GenerateColumn(work, r, rowsR, i, i); + ComputeQR(work, i, r, i, rowsR, i + 1, columnsR, Control.NumberOfParallelWorkerThreads); + } + + for (var i = minmn - 1; i >= 0; i--) + { + ComputeQR(work, i, q, i, rowsR, i, rowsR, Control.NumberOfParallelWorkerThreads); + } + + work[0] = rowsR * rowsR; + } + + #region QR Factor Helper functions + + /// + /// Perform calculation of Q or R + /// + /// Work array + /// Index of colunn in work array + /// Q or R matrices + /// The first row in + /// The last row + /// The first column + /// The last column + /// Number of available CPUs + private static void ComputeQR(Complex32[] work, int workIndex, Complex32[] a, int rowStart, int rowCount, int columnStart, int columnCount, int availableCores) + { + if (rowStart > rowCount || columnStart > columnCount) + { + return; + } + + var tmpColCount = columnCount - columnStart; + + if ((availableCores > 1) && (tmpColCount > 200)) + { + var tmpSplit = columnStart + (tmpColCount / 2); + var tmpCores = availableCores / 2; + + CommonParallel.Invoke( + () => ComputeQR(work, workIndex, a, rowStart, rowCount, columnStart, tmpSplit, tmpCores), + () => ComputeQR(work, workIndex, a, rowStart, rowCount, tmpSplit, columnCount, tmpCores)); + } + else + { + for (var j = columnStart; j < columnCount; j++) + { + var scale = Complex32.Zero; + for (var i = rowStart; i < rowCount; i++) + { + scale += work[(workIndex * rowCount) + i - rowStart] * a[(j * rowCount) + i]; + } + + for (var i = rowStart; i < rowCount; i++) + { + a[(j * rowCount) + i] -= work[(workIndex * rowCount) + i - rowStart].Conjugate() * scale; + } + } + } + } + + /// + /// Generate column from initial matrix to work array + /// + /// Work array + /// Initial matrix + /// The number of rows in matrix + /// The firts row + /// Column index + private static void GenerateColumn(Complex32[] work, Complex32[] a, int rowCount, int row, int column) + { + var tmp = column * rowCount; + var index = tmp + row; + + CommonParallel.For( + row, + rowCount, + i => + { + var iIndex = tmp + i; + work[iIndex - row] = a[iIndex]; + a[iIndex] = Complex32.Zero; + }); + + var norm = Complex32.Zero; + for (var i = 0; i < rowCount - row; ++i) + { + var index1 = tmp + i; + norm += work[index1].Magnitude * work[index1].Magnitude; + } + + norm = norm.SquareRoot(); + if (row == rowCount - 1 || norm.Magnitude == 0) + { + a[index] = -work[tmp]; + work[tmp] = new Complex32(2.0f, 0).SquareRoot(); + return; + } + + if (work[tmp].Magnitude != 0.0f) + { + norm = norm.Magnitude * (work[tmp] / work[tmp].Magnitude); + } + + a[index] = -norm; + CommonParallel.For(0, rowCount - row, i => work[tmp + i] /= norm); + work[tmp] += 1.0f; + + var s = (1.0f / work[tmp]).SquareRoot(); + CommonParallel.For(0, rowCount - row, i => work[tmp + i] = work[tmp + i].Conjugate() * s); + } + + #endregion + + /// + /// Solves A*X=B for X using QR factorization of A. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the R matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A M by M matrix that holds the Q matrix of the + /// QR factorization. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + public void QRSolve(Complex32[] r, int rowsR, int columnsR, Complex32[] q, Complex32[] b, int columnsB, Complex32[] x) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (b == null) + { + throw new ArgumentNullException("q"); + } + + if (x == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + if (b.Length != rowsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + } + + var work = new Complex32[rowsR * rowsR]; + QRSolve(r, rowsR, columnsR, q, b, columnsB, x, work); + } + + /// + /// Solves A*X=B for X using QR factorization of A. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the R matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A M by M matrix that holds the Q matrix of the + /// QR factorization. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + /// 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. + public void QRSolve(Complex32[] r, int rowsR, int columnsR, Complex32[] q, Complex32[] b, int columnsB, Complex32[] x, Complex32[] work) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (b == null) + { + throw new ArgumentNullException("q"); + } + + if (x == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + if (b.Length != rowsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + } + + if (work.Length < rowsR * rowsR) + { + work[0] = rowsR * rowsR; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + QRFactor(r, rowsR, columnsR, q, work); + QRSolveFactored(q, r, rowsR, columnsR, b, columnsB, x); + + work[0] = rowsR * rowsR; + } + + /// + /// Solves A*X=B for X using a previously QR factored matrix. + /// + /// The Q matrix obtained by calling . + /// The R matrix obtained by calling . + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + public void QRSolveFactored(Complex32[] q, Complex32[] r, int rowsR, int columnsR, Complex32[] b, int columnsB, Complex32[] x) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (b == null) + { + throw new ArgumentNullException("q"); + } + + if (x == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + if (b.Length != rowsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + } + + var sol = new Complex32[b.Length]; + + // Copy B matrix to "sol", so B data will not be changed + CommonParallel.For(0, b.Length, index => sol[index] = b[index]); + + // Compute Y = transpose(Q)*B + var column = new Complex32[rowsR]; + for (var j = 0; j < columnsB; j++) + { + var jm = j * rowsR; + CommonParallel.For(0, rowsR, k => column[k] = sol[jm + k]); + CommonParallel.For( + 0, + rowsR, + i => + { + var im = i * rowsR; + sol[jm + i] = CommonParallel.Aggregate(0, rowsR, k => q[im + k].Conjugate() * column[k]); + }); + } + + // Solve R*X = Y; + for (var k = columnsR - 1; k >= 0; k--) + { + var km = k * rowsR; + for (var j = 0; j < columnsB; j++) + { + sol[(j * rowsR) + k] /= r[km + k]; + } + + for (var i = 0; i < k; i++) + { + for (var j = 0; j < columnsB; j++) + { + var jm = j * rowsR; + sol[jm + i] -= sol[jm + k] * r[km + i]; + } + } + } + + // Fill result matrix + CommonParallel.For( + 0, + columnsR, + row => + { + for (var col = 0; col < columnsB; col++) + { + x[(col * columnsR) + row] = sol[row + (col * rowsR)]; + } + }); + } + + /// + /// Computes the singular value decomposition of A. + /// + /// Compute the singular U and VT vectors or not. + /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The singular values of A in ascending value. + /// If is true, on exit U contains the left + /// singular vectors. + /// If is true, on exit VT contains the transposed + /// right singular vectors. + /// This is equivalent to the GESVD LAPACK routine. + public void SingularValueDecomposition(bool computeVectors, Complex32[] a, int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (s == null) + { + throw new ArgumentNullException("s"); + } + + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (vt == null) + { + throw new ArgumentNullException("vt"); + } + + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } + + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } + + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } + + // Actually "work = new Complex32[aRows]" is acceptable size of work array. I set size proposed in method description + var work = new Complex32[(2 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA)]; + SingularValueDecomposition(computeVectors, a, rowsA, columnsA, s, u, vt, work); + } + + /// + /// Computes the singular value decomposition of A. + /// + /// Compute the singular U and VT vectors or not. + /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The singular values of A in ascending value. + /// If is true, on exit U contains the left + /// singular vectors. + /// If is true, on exit VT contains the transposed + /// right singular vectors. + /// The work array. For real matrices, the work array should be at least + /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). + /// On exit, work[0] contains the optimal work size value. + /// This is equivalent to the GESVD LAPACK routine. + public void SingularValueDecomposition(bool computeVectors, Complex32[] a, int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] work) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (s == null) + { + throw new ArgumentNullException("s"); + } + + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (vt == null) + { + throw new ArgumentNullException("vt"); + } + + if (work == null) + { + throw new ArgumentNullException("work"); + } + + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } + + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } + + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } + + if (work.Length == 0) + { + throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); + } + + if (work.Length < rowsA) + { + work[0] = rowsA; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + const int Maxiter = 1000; + + var e = new Complex32[columnsA]; + var v = new Complex32[vt.Length]; + var stemp = new Complex32[Math.Min(rowsA + 1, columnsA)]; + + int i, j, l, lp1; + + var cs = 0.0f; + var sn = 0.0f; + Complex32 t; + + var ncu = rowsA; + + // Reduce matrix to bidiagonal form, storing the diagonal elements + // in "s" and the super-diagonal elements in "e". + var nct = Math.Min(rowsA - 1, columnsA); + var nrt = Math.Max(0, Math.Min(columnsA - 2, rowsA)); + 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 vector s[l]. + var sum = 0.0f; + for (i = l; i < rowsA; i++) + { + sum += a[(l * rowsA) + i].Magnitude * a[(l * rowsA) + i].Magnitude; + } + + stemp[l] = (float)Math.Sqrt(sum); + if (stemp[l] != 0.0f) + { + if (a[(l * rowsA) + l] != 0.0f) + { + stemp[l] = stemp[l].Magnitude * (a[(l * rowsA) + l] / a[(l * rowsA) + l].Magnitude); + } + + // A part of column "l" of Matrix A from row "l" to end multiply by 1.0f / s[l] + for (i = l; i < rowsA; i++) + { + a[(l * rowsA) + i] = a[(l * rowsA) + i] * (1.0f / stemp[l]); + } + + a[(l * rowsA) + l] = 1.0f + a[(l * rowsA) + l]; + } + + stemp[l] = -stemp[l]; + } + + for (j = lp1; j < columnsA; j++) + { + if (l < nct) + { + if (stemp[l] != 0.0f) + { + // Apply the transformation. + t = 0.0f; + for (i = l; i < rowsA; i++) + { + t += a[(l * rowsA) + i].Conjugate() * a[(j * rowsA) + i]; + } + + t = -t / a[(l * rowsA) + l]; + + for (var ii = l; ii < rowsA; ii++) + { + a[(j * rowsA) + ii] += t * a[(l * rowsA) + ii]; + } + } + } + + // Place the l-th row of matrix into "e" for the + // subsequent calculation of the row transformation. + e[j] = a[(j * rowsA) + l].Conjugate(); + } + + if (computeVectors && l < nct) + { + // Place the transformation in "u" for subsequent back multiplication. + for (i = l; i < rowsA; i++) + { + u[(l * rowsA) + i] = a[(l * rowsA) + i]; + } + } + + if (l >= nrt) + { + continue; + } + + // Compute the l-th row transformation and place the l-th super-diagonal in e(l). + var enorm = 0.0f; + for (i = lp1; i < e.Length; i++) + { + enorm += e[i].Magnitude * e[i].Magnitude; + } + + e[l] = (float)Math.Sqrt(enorm); + if (e[l] != 0.0f) + { + if (e[lp1] != 0.0f) + { + e[l] = e[l].Magnitude * (e[lp1] / e[lp1].Magnitude); + } + + // Scale vector "e" from "lp1" by 1.0f / e[l] + for (i = lp1; i < e.Length; i++) + { + e[i] = e[i] * (1.0f / e[l]); + } + + e[lp1] = 1.0f + e[lp1]; + } + + e[l] = -e[l].Conjugate(); + + if (lp1 < rowsA && e[l] != 0.0f) + { + // Apply the transformation. + for (i = lp1; i < rowsA; i++) + { + work[i] = 0.0f; + } + + for (j = lp1; j < columnsA; j++) + { + for (var ii = lp1; ii < rowsA; ii++) + { + work[ii] += e[j] * a[(j * rowsA) + ii]; + } + } + + for (j = lp1; j < columnsA; j++) + { + var ww = (-e[j] / e[lp1]).Conjugate(); + for (var ii = lp1; ii < rowsA; ii++) + { + a[(j * rowsA) + ii] += ww * work[ii]; + } + } + } + + if (!computeVectors) + { + continue; + } + + // Place the transformation in v for subsequent back multiplication. + for (i = lp1; i < columnsA; i++) + { + v[(l * columnsA) + i] = e[i]; + } + } + + // Set up the final bidiagonal matrix or order m. + var m = Math.Min(columnsA, rowsA + 1); + var nctp1 = nct + 1; + var nrtp1 = nrt + 1; + if (nct < columnsA) + { + stemp[nctp1 - 1] = a[((nctp1 - 1) * rowsA) + (nctp1 - 1)]; + } + + if (rowsA < m) + { + stemp[m - 1] = 0.0f; + } + + if (nrtp1 < m) + { + e[nrtp1 - 1] = a[((m - 1) * rowsA) + (nrtp1 - 1)]; + } + + e[m - 1] = 0.0f; + + // If required, generate "u". + if (computeVectors) + { + for (j = nctp1 - 1; j < ncu; j++) + { + for (i = 0; i < rowsA; i++) + { + u[(j * rowsA) + i] = 0.0f; + } + + u[(j * rowsA) + j] = 1.0f; + } + + for (l = nct - 1; l >= 0; l--) + { + if (stemp[l] != 0.0f) + { + for (j = l + 1; j < ncu; j++) + { + t = 0.0f; + for (i = l; i < rowsA; i++) + { + t += u[(l * rowsA) + i].Conjugate() * u[(j * rowsA) + i]; + } + + t = -t / u[(l * rowsA) + l]; + for (var ii = l; ii < rowsA; ii++) + { + u[(j * rowsA) + ii] += t * u[(l * rowsA) + ii]; + } + } + + // A part of column "l" of matrix A from row "l" to end multiply by -1.0f + for (i = l; i < rowsA; i++) + { + u[(l * rowsA) + i] = u[(l * rowsA) + i] * -1.0f; + } + + u[(l * rowsA) + l] = 1.0f + u[(l * rowsA) + l]; + for (i = 0; i < l; i++) + { + u[(l * rowsA) + i] = 0.0f; + } + } + else + { + for (i = 0; i < rowsA; i++) + { + u[(l * rowsA) + i] = 0.0f; + } + + u[(l * rowsA) + l] = 1.0f; + } + } + } + + // If it is required, generate v. + if (computeVectors) + { + for (l = columnsA - 1; l >= 0; l--) + { + lp1 = l + 1; + if (l < nrt) + { + if (e[l] != 0.0f) + { + for (j = lp1; j < columnsA; j++) + { + t = 0.0f; + for (i = lp1; i < columnsA; i++) + { + t += v[(l * columnsA) + i].Conjugate() * v[(j * columnsA) + i]; + } + + t = -t / v[(l * columnsA) + lp1]; + for (var ii = l; ii < columnsA; ii++) + { + v[(j * columnsA) + ii] += t * v[(l * columnsA) + ii]; + } + } + } + } + + for (i = 0; i < columnsA; i++) + { + v[(l * columnsA) + i] = 0.0f; + } + + v[(l * columnsA) + l] = 1.0f; + } + } + + // Transform "s" and "e" so that they are float + for (i = 0; i < m; i++) + { + Complex32 r; + if (stemp[i] != 0.0f) + { + t = stemp[i].Magnitude; + r = stemp[i] / t; + stemp[i] = t; + if (i < m - 1) + { + e[i] = e[i] / r; + } + + if (computeVectors) + { + // A part of column "i" of matrix U from row 0 to end multiply by r + for (j = 0; j < rowsA; j++) + { + u[(i * rowsA) + j] = u[(i * rowsA) + j] * r; + } + } + } + + // Exit + if (i == m - 1) + { + break; + } + + if (e[i] == 0.0f) + { + continue; + } + + t = e[i].Magnitude; + r = t / e[i]; + e[i] = t; + stemp[i + 1] = stemp[i + 1] * r; + if (!computeVectors) + { + continue; + } + + // A part of column "i+1" of matrix VT from row 0 to end multiply by r + for (j = 0; j < columnsA; j++) + { + v[((i + 1) * columnsA) + j] = v[((i + 1) * columnsA) + j] * 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. + 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 mS[m] and e[l-1] are negligible and l < m + // kase = 2: if mS[l] is negligible and l < m + // kase = 3: if e[l-1] is negligible, l < m, and mS[l, ..., mS[m] are not negligible (qr step). + // kase = 4: if e[m-1] is negligible (convergence). + float ztest; + float test; + for (l = m - 2; l >= 0; l--) + { + test = stemp[l].Magnitude + stemp[l + 1].Magnitude; + ztest = test + e[l].Magnitude; + 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 + e[ls].Magnitude; + } + + if (ls != l + 1) + { + test = test + e[ls - 1].Magnitude; + } + + ztest = test + stemp[ls].Magnitude; + if (ztest.AlmostEqualInDecimalPlaces(test, 7)) + { + stemp[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 s[m]. + case 1: + f = e[m - 2].Real; + e[m - 2] = 0.0f; + float t1; + for (var kk = l; kk < m - 1; kk++) + { + k = m - 2 - kk + l; + t1 = stemp[k].Real; + Drotg(ref t1, ref f, ref cs, ref sn); + stemp[k] = t1; + if (k != l) + { + f = -sn * e[k - 1].Real; + e[k - 1] = cs * e[k - 1]; + } + + if (computeVectors) + { + // Rotate + for (i = 0; i < columnsA; i++) + { + var z = (cs * v[(k * columnsA) + i]) + (sn * v[((m - 1) * columnsA) + i]); + v[((m - 1) * columnsA) + i] = (cs * v[((m - 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); + v[(k * columnsA) + i] = z; + } + } + } + + break; + + // Split at negligible s[l]. + case 2: + f = e[l - 1].Real; + e[l - 1] = 0.0f; + for (k = l; k < m; k++) + { + t1 = stemp[k].Real; + Drotg(ref t1, ref f, ref cs, ref sn); + stemp[k] = t1; + f = -sn * e[k].Real; + e[k] = cs * e[k]; + if (computeVectors) + { + // Rotate + for (i = 0; i < rowsA; i++) + { + var z = (cs * u[(k * rowsA) + i]) + (sn * u[((l - 1) * rowsA) + i]); + u[((l - 1) * rowsA) + i] = (cs * u[((l - 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); + u[(k * rowsA) + i] = z; + } + } + } + + break; + + // Perform one qr step. + case 3: + // calculate the shift. + var scale = 0.0f; + scale = Math.Max(scale, stemp[m - 1].Magnitude); + scale = Math.Max(scale, stemp[m - 2].Magnitude); + scale = Math.Max(scale, e[m - 2].Magnitude); + scale = Math.Max(scale, stemp[l].Magnitude); + scale = Math.Max(scale, e[l].Magnitude); + var sm = stemp[m - 1].Real / scale; + var smm1 = stemp[m - 2].Real / scale; + var emm1 = e[m - 2].Real / scale; + var sl = stemp[l].Real / scale; + var el = e[l].Real / scale; + var b = (((smm1 + sm) * (smm1 - sm)) + (emm1 * emm1)) / 2.0f; + var c = (sm * emm1) * (sm * emm1); + var shift = 0.0f; + if (b != 0.0f || c != 0.0f) + { + shift = (float)Math.Sqrt((b * b) + c); + if (b < 0.0f) + { + 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 * stemp[k].Real) + (sn * e[k].Real); + e[k] = (cs * e[k]) - (sn * stemp[k]); + g = sn * stemp[k + 1].Real; + stemp[k + 1] = cs * stemp[k + 1]; + if (computeVectors) + { + for (i = 0; i < columnsA; i++) + { + var z = (cs * v[(k * columnsA) + i]) + (sn * v[((k + 1) * columnsA) + i]); + v[((k + 1) * columnsA) + i] = (cs * v[((k + 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); + v[(k * columnsA) + i] = z; + } + } + + Drotg(ref f, ref g, ref cs, ref sn); + stemp[k] = f; + f = (cs * e[k].Real) + (sn * stemp[k + 1].Real); + stemp[k + 1] = -(sn * e[k]) + (cs * stemp[k + 1]); + g = sn * e[k + 1].Real; + e[k + 1] = cs * e[k + 1]; + if (computeVectors && k < rowsA) + { + for (i = 0; i < rowsA; i++) + { + var z = (cs * u[(k * rowsA) + i]) + (sn * u[((k + 1) * rowsA) + i]); + u[((k + 1) * rowsA) + i] = (cs * u[((k + 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); + u[(k * rowsA) + i] = z; + } + } + } + + e[m - 2] = f; + iter = iter + 1; + break; + + // Convergence + case 4: + + // Make the singular value positive + if (stemp[l].Real < 0.0f) + { + stemp[l] = -stemp[l]; + if (computeVectors) + { + // A part of column "l" of matrix VT from row 0 to end multiply by -1 + for (i = 0; i < columnsA; i++) + { + v[(l * columnsA) + i] = v[(l * columnsA) + i] * -1.0f; + } + } + } + + // Order the singular value. + while (l != mn - 1) + { + if (stemp[l].Real >= stemp[l + 1].Real) + { + break; + } + + t = stemp[l]; + stemp[l] = stemp[l + 1]; + stemp[l + 1] = t; + if (computeVectors && l < columnsA) + { + // Swap columns l, l + 1 + for (i = 0; i < columnsA; i++) + { + var z = v[(l * columnsA) + i]; + v[(l * columnsA) + i] = v[((l + 1) * columnsA) + i]; + v[((l + 1) * columnsA) + i] = z; + } + } + + if (computeVectors && l < rowsA) + { + // Swap columns l, l + 1 + for (i = 0; i < rowsA; i++) + { + var z = u[(l * rowsA) + i]; + u[(l * rowsA) + i] = u[((l + 1) * rowsA) + i]; + u[((l + 1) * rowsA) + i] = z; + } + } + + l = l + 1; + } + + iter = 0; + m = m - 1; + break; + } + } + + if (computeVectors) + { + // Finally transpose "v" to get "vt" matrix + for (i = 0; i < columnsA; i++) + { + for (j = 0; j < columnsA; j++) + { + vt[(j * columnsA) + i] = v[(i * columnsA) + j].Conjugate(); + } + } + } + + // Copy stemp to s with size adjustment. We are using ported copy of linpack's svd code and it uses + // a singular vector of length rows+1 when rows < columns. The last element is not used and needs to be removed. + // We should port lapack's svd routine to remove this problem. + CommonParallel.For(0, Math.Min(rowsA, columnsA), index => s[index] = stemp[index]); + + // On return the first element of the work array stores the min size of the work array could have been + // work[0] = Math.Max(3 * Math.Min(aRows, aColumns) + Math.Max(aRows, aColumns), 5 * Math.Min(aRows, aColumns)); + work[0] = rowsA; + } + + /// + /// Solves A*X=B for X using the singular value decomposition of A. + /// + /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The singular values of A in ascending value. + /// On exit U contains the left singular vectors. + /// On exit VT contains the transposed right singular vectors. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + public void SvdSolve(Complex32[] a, int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int columnsB, Complex32[] x) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (s == null) + { + throw new ArgumentNullException("s"); + } + + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (vt == null) + { + throw new ArgumentNullException("vt"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } + + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } + + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } + + if (b.Length != rowsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + // TODO: Actually "work = new double[aRows]" is acceptable size of work array. I set size proposed in method description + var work = new Complex32[(2 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA)]; + SvdSolve(a, rowsA, columnsA, s, u, vt, b, columnsB, x, work); + } + + /// + /// Solves A*X=B for X using the singular value decomposition of A. + /// + /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The singular values of A in ascending value. + /// On exit U contains the left singular vectors. + /// On exit VT contains the transposed right singular vectors. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + /// The work array. For real matrices, the work array should be at least + /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). + /// On exit, work[0] contains the optimal work size value. + public void SvdSolve(Complex32[] a, int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int columnsB, Complex32[] x, Complex32[] work) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (s == null) + { + throw new ArgumentNullException("s"); + } + + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (vt == null) + { + throw new ArgumentNullException("vt"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } + + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } + + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } + + if (b.Length != rowsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (work.Length == 0) + { + throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); + } + + if (work.Length < rowsA) + { + work[0] = rowsA; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + SingularValueDecomposition(true, a, rowsA, columnsA, s, u, vt, work); + SvdSolveFactored(rowsA, columnsA, s, u, vt, b, columnsB, x); + } + + /// + /// Solves A*X=B for X using a previously SVD decomposed matrix. + /// + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The s values returned by . + /// The left singular vectors returned by . + /// The right singular vectors returned by . + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + public void SvdSolveFactored(int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int columnsB, Complex32[] x) + { + if (s == null) + { + throw new ArgumentNullException("s"); + } + + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (vt == null) + { + throw new ArgumentNullException("vt"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } + + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } + + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } + + if (b.Length != rowsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + var mn = Math.Min(rowsA, columnsA); + var tmp = new Complex32[columnsA]; + + for (var k = 0; k < columnsB; k++) + { + for (var j = 0; j < columnsA; j++) + { + var value = Complex32.Zero; + if (j < mn) + { + for (var i = 0; i < rowsA; i++) + { + value += u[(j * rowsA) + i].Conjugate() * b[(k * rowsA) + i]; + } + + value /= s[j]; + } + + tmp[j] = value; + } + + for (var j = 0; j < columnsA; j++) + { + var value = Complex32.Zero; + for (var i = 0; i < columnsA; i++) + { + value += vt[(j * columnsA) + i].Conjugate() * tmp[i]; + } + + x[(k * columnsA) + j] = value; + } + } + } + } +} diff --git a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Double.cs b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Double.cs new file mode 100644 index 00000000..35339af6 --- /dev/null +++ b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Double.cs @@ -0,0 +1,2907 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// http://mathnetnumerics.codeplex.com +// Copyright (c) 2009-2010 Math.NET +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +namespace MathNet.Numerics.Algorithms.LinearAlgebra +{ + using System; + using Properties; + using Threading; + + /// + /// The managed linear algebra provider. + /// + public partial class ManagedLinearAlgebraProvider : ILinearAlgebraProvider + { + /// + /// Adds a scaled vector to another: y += alpha*x. + /// + /// The vector to update. + /// The value to scale by. + /// The vector to add to . + /// This equivalent to the AXPY BLAS routine. + public void AddVectorToScaledVector(double[] y, double alpha, double[] x) + { + 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 (alpha == 0.0) + { + return; + } + + if (alpha == 1.0) + { + CommonParallel.For(0, y.Length, index => { y[index] += x[index]; }); + } + else + { + CommonParallel.For(0, y.Length, index => { y[index] += alpha * x[index]; }); + } + } + + /// + /// Scales an array. Can be used to scale a vector and a matrix. + /// + /// The scalar. + /// The values to scale. + /// This is equivalent to the SCAL BLAS routine. + public void ScaleArray(double alpha, double[] x) + { + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (alpha == 1.0) + { + return; + } + + CommonParallel.For(0, x.Length, index => { x[index] = alpha * x[index]; }); + } + + /// + /// Computes the dot product of x and y. + /// + /// The vector x. + /// The vector y. + /// The dot product of x and y. + /// This is equivalent to the DOT BLAS routine. + public double DotProduct(double[] x, double[] y) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (y.Length != x.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + return CommonParallel.Aggregate(0, y.Length, index => y[index] * x[index]); + } + + /// + /// Does a point wise add of two arrays z = x + y. This can be used + /// to add vectors or matrices. + /// + /// The array x. + /// The array y. + /// The result of the addition. + /// There is no equivalent BLAS routine, but many libraries + /// provide optimized (parallel and/or vectorized) versions of this + /// routine. + public void AddArrays(double[] x, double[] y, double[] result) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (y.Length != x.Length || y.Length != result.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + CommonParallel.For(0, y.Length, index => { result[index] = x[index] + y[index]; }); + } + + /// + /// Does a point wise subtraction of two arrays z = x - y. This can be used + /// to subtract vectors or matrices. + /// + /// The array x. + /// The array y. + /// The result of the subtraction. + /// There is no equivalent BLAS routine, but many libraries + /// provide optimized (parallel and/or vectorized) versions of this + /// routine. + public void SubtractArrays(double[] x, double[] y, double[] result) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (y.Length != x.Length || y.Length != result.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + CommonParallel.For(0, y.Length, index => { result[index] = x[index] - y[index]; }); + } + + /// + /// Does a point wise multiplication of two arrays z = x * y. This can be used + /// to multiple elements of vectors or matrices. + /// + /// The array x. + /// The array y. + /// The result of the point wise multiplication. + /// There is no equivalent BLAS routine, but many libraries + /// provide optimized (parallel and/or vectorized) versions of this + /// routine. + public void PointWiseMultiplyArrays(double[] x, double[] y, double[] result) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (y.Length != x.Length || y.Length != result.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + CommonParallel.For(0, y.Length, index => { result[index] = x[index] * y[index]; }); + } + + /// + /// Does a point wise division of two arrays z = x / y. This can be used + /// to divide elements of vectors or matrices. + /// + /// The array x. + /// The array y. + /// The result of the point wise division. + /// There is no equivalent BLAS routine, but many libraries + /// provide optimized (parallel and/or vectorized) versions of this + /// routine. + public void PointWiseDivideArrays(double[] x, double[] y, double[] result) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (y.Length != x.Length || y.Length != result.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + CommonParallel.For(0, y.Length, index => { result[index] = x[index] / y[index]; }); + } + + /// + /// Computes the requested of the matrix. + /// + /// The type of norm to compute. + /// The number of rows. + /// The number of columns. + /// The matrix to compute the norm from. + /// + /// The requested of the matrix. + /// + public double MatrixNorm(Norm norm, int rows, int columns, double[] matrix) + { + var ret = 0.0; + switch (norm) + { + case Norm.OneNorm: + for (var j = 0; j < columns; j++) + { + var s = 0.0; + for (var i = 0; i < rows; i++) + { + s += Math.Abs(matrix[(j * rows) + i]); + } + + ret = Math.Max(ret, s); + } + + break; + case Norm.LargestAbsoluteValue: + + for (var i = 0; i < rows; i++) + { + for (var j = 0; j < columns; j++) + { + ret = Math.Max(Math.Abs(matrix[(j * rows) + i]), ret); + } + } + + break; + case Norm.InfinityNorm: + for (var i = 0; i < rows; i++) + { + var s = 0.0; + for (var j = 0; j < columns; j++) + { + s += Math.Abs(matrix[(j * rows) + i]); + } + + ret = Math.Max(ret, s); + } + + break; + case Norm.FrobeniusNorm: + var aat = new double[rows * rows]; + MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.Transpose, 1.0, matrix, rows, columns, matrix, rows, columns, 0.0, aat); + + for (var i = 0; i < rows; i++) + { + ret += Math.Abs(aat[(i * rows) + i]); + } + + ret = Math.Sqrt(ret); + break; + } + + return ret; + } + + /// + /// Computes the requested of the matrix. + /// + /// The type of norm to compute. + /// The number of rows. + /// The number of columns. + /// The matrix to compute the norm from. + /// The work array. Not used in the managed provider. + /// + /// The requested of the matrix. + /// + public double MatrixNorm(Norm norm, int rows, int columns, double[] matrix, double[] work) + { + return MatrixNorm(norm, rows, columns, matrix); + } + + /// + /// Multiples two matrices. result = x * y + /// + /// The x matrix. + /// The number of rows in the x matrix. + /// The number of columns in the x matrix. + /// The y matrix. + /// The number of rows in the y matrix. + /// The number of columns in the y matrix. + /// Where to store the result of the multiplication. + /// This is a simplified version of the BLAS GEMM routine with alpha + /// set to 1.0 and beta set to 0.0, and x and y are not transposed. + public void MatrixMultiply(double[] x, int rowsX, int columnsX, double[] y, int rowsY, int columnsY, double[] result) + { + // First check some basic requirement on the parameters of the matrix multiplication. + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (rowsX * columnsX != x.Length) + { + throw new ArgumentException("x.Length != xRows * xColumns"); + } + + if (rowsY * columnsY != y.Length) + { + throw new ArgumentException("y.Length != yRows * yColumns"); + } + + if (columnsX != rowsY) + { + throw new ArgumentException("xColumns != yRows"); + } + + if (rowsX * columnsY != result.Length) + { + throw new ArgumentException("xRows * yColumns != result.Length"); + } + + // Check whether we will be overwriting any of our inputs and make copies if necessary. + // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory + // as result, we can do it on a row wise basis. We should investigate this. + double[] xdata; + if (ReferenceEquals(x, result)) + { + xdata = (double[])x.Clone(); + } + else + { + xdata = x; + } + + double[] ydata; + if (ReferenceEquals(y, result)) + { + ydata = (double[])y.Clone(); + } + else + { + ydata = y; + } + + // Start the actual matrix multiplication. + // TODO - For small matrices we should get rid of the parallelism because of startup costs. + // Perhaps the following implementations would be a good one + // http://blog.feradz.com/2009/01/cache-efficient-matrix-multiplication/ + MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 1.0, xdata, rowsX, columnsX, ydata, rowsY, columnsY, 0.0, result); + } + + /// + /// Multiplies two matrices and updates another with the result. c = alpha*op(a)*op(b) + beta*c + /// + /// How to transpose the matrix. + /// How to transpose the matrix. + /// The value to scale matrix. + /// The a matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. + /// The b matrix + /// The number of rows in the matrix. + /// The number of columns in the matrix. + /// The value to scale the matrix. + /// The c matrix. + public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, double alpha, double[] a, int rowsA, int columnsA, double[] b, int rowsB, int columnsB, double beta, double[] c) + { + // Choose nonsensical values for the number of rows in c; fill them in depending + // on the operations on a and b. + int rowsC; + + // First check some basic requirement on the parameters of the matrix multiplication. + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if ((int)transposeA > 111 && (int)transposeB > 111) + { + if (rowsA != columnsB) + { + throw new ArgumentOutOfRangeException(); + } + + if (columnsA * rowsB != c.Length) + { + throw new ArgumentOutOfRangeException(); + } + + rowsC = columnsA; + } + else if ((int)transposeA > 111) + { + if (rowsA != rowsB) + { + throw new ArgumentOutOfRangeException(); + } + + if (columnsA * columnsB != c.Length) + { + throw new ArgumentOutOfRangeException(); + } + + rowsC = columnsA; + } + else if ((int)transposeB > 111) + { + if (columnsA != columnsB) + { + throw new ArgumentOutOfRangeException(); + } + + if (rowsA * rowsB != c.Length) + { + throw new ArgumentOutOfRangeException(); + } + + rowsC = rowsA; + } + else + { + if (columnsA != rowsB) + { + throw new ArgumentOutOfRangeException(); + } + + if (rowsA * columnsB != c.Length) + { + throw new ArgumentOutOfRangeException(); + } + + rowsC = rowsA; + } + + if (alpha == 0.0 && beta == 0.0) + { + Array.Clear(c, 0, c.Length); + return; + } + + // Check whether we will be overwriting any of our inputs and make copies if necessary. + // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory + // as result, we can do it on a row wise basis. We should investigate this. + double[] adata; + if (ReferenceEquals(a, c)) + { + adata = (double[])a.Clone(); + } + else + { + adata = a; + } + + double[] bdata; + if (ReferenceEquals(b, c)) + { + bdata = (double[])b.Clone(); + } + else + { + bdata = b; + } + + if (alpha == 1.0) + { + if (beta == 0.0) + { + if ((int)transposeA > 111 && (int)transposeB > 111) + { + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + double s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s; + } + }); + } + else if ((int)transposeA > 111) + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + double s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s; + } + }); + } + else if ((int)transposeB > 111) + { + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + double s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s; + } + }); + } + else + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + double s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s; + } + }); + } + } + else + { + if ((int)transposeA > 111 && (int)transposeB > 111) + { + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + double s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (c[jIndex + i] * beta) + s; + } + }); + } + else if ((int)transposeA > 111) + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + double s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s + (c[jcIndex + i] * beta); + } + }); + } + else if ((int)transposeB > 111) + { + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + double s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s + (c[jIndex + i] * beta); + } + }); + } + else + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + double s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s + (c[jcIndex + i] * beta); + } + }); + } + } + } + else + { + if ((int)transposeA > 111 && (int)transposeB > 111) + { + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + double s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (c[jIndex + i] * beta) + (alpha * s); + } + }); + } + else if ((int)transposeA > 111) + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + double s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); + } + }); + } + else if ((int)transposeB > 111) + { + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + double s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (alpha * s) + (c[jIndex + i] * beta); + } + }); + } + else + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + double s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); + } + }); + } + } + } + + /// + /// Computes the LUP factorization of A. P*A = L*U. + /// + /// An by matrix. The matrix is overwritten with the + /// the LU factorization on exit. The lower triangular factor L is stored in under the diagonal of (the diagonal is always 1.0 + /// for the L factor). The upper triangular factor U is stored on and above the diagonal of . + /// The order of the square matrix . + /// On exit, it contains the pivot indices. The size of the array must be . + /// This is equivalent to the GETRF LAPACK routine. + public void LUFactor(double[] data, int order, int[] ipiv) + { + if (data == null) + { + throw new ArgumentNullException("data"); + } + + if (ipiv == null) + { + throw new ArgumentNullException("ipiv"); + } + + if (data.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "data"); + } + + if (ipiv.Length != order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); + } + + // Initialize the pivot matrix to the identity permutation. + for (var i = 0; i < order; i++) + { + ipiv[i] = i; + } + + var vecLUcolj = new double[order]; + + // Outer loop. + for (var j = 0; j < order; j++) + { + var indexj = j * order; + var indexjj = indexj + j; + + // Make a copy of the j-th column to localize references. + for (var i = 0; i < order; i++) + { + vecLUcolj[i] = data[indexj + i]; + } + + // Apply previous transformations. + for (var i = 0; i < order; i++) + { + // Most of the time is spent in the following dot product. + var kmax = Math.Min(i, j); + var s = 0.0; + for (var k = 0; k < kmax; k++) + { + s += data[(k * order) + i] * vecLUcolj[k]; + } + + data[indexj + i] = vecLUcolj[i] -= s; + } + + // Find pivot and exchange if necessary. + var p = j; + for (var i = j + 1; i < order; i++) + { + if (Math.Abs(vecLUcolj[i]) > Math.Abs(vecLUcolj[p])) + { + p = i; + } + } + + if (p != j) + { + for (var k = 0; k < order; k++) + { + var indexk = k * order; + var indexkp = indexk + p; + var indexkj = indexk + j; + var temp = data[indexkp]; + data[indexkp] = data[indexkj]; + data[indexkj] = temp; + } + + ipiv[j] = p; + } + + // Compute multipliers. + if (j < order & data[indexjj] != 0.0) + { + for (var i = j + 1; i < order; i++) + { + data[indexj + i] /= data[indexjj]; + } + } + } + } + + /// + /// Computes the inverse of matrix using LU factorization. + /// + /// The N by N matrix to invert. Contains the inverse On exit. + /// The order of the square matrix . + /// This is equivalent to the GETRF and GETRI LAPACK routines. + public void LUInverse(double[] a, int order) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (a.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); + } + + var ipiv = new int[order]; + LUFactor(a, order, ipiv); + LUInverseFactored(a, order, ipiv); + } + + /// + /// Computes the inverse of a previously factored matrix. + /// + /// The LU factored N by N matrix. Contains the inverse On exit. + /// The order of the square matrix . + /// The pivot indices of . + /// This is equivalent to the GETRI LAPACK routine. + public void LUInverseFactored(double[] 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 double[a.Length]; + for (var i = 0; i < order; i++) + { + inverse[i + (order * i)] = 1.0; + } + + LUSolveFactored(order, a, order, ipiv, inverse); + Buffer.BlockCopy(inverse, 0, a, 0, a.Length * Constants.SizeOfDouble); + } + + /// + /// Computes the inverse of matrix using LU factorization. + /// + /// The N by N matrix to invert. Contains the inverse On exit. + /// The order of the square matrix . + /// 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. + /// This is equivalent to the GETRF and GETRI LAPACK routines. + public void LUInverse(double[] a, int order, double[] work) + { + LUInverse(a, order); + } + + /// + /// Computes the inverse of a previously factored matrix. + /// + /// The LU factored N by N matrix. Contains the inverse On exit. + /// The order of the square matrix . + /// The pivot indices of . + /// 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. + /// This is equivalent to the GETRI LAPACK routine. + public void LUInverseFactored(double[] a, int order, int[] ipiv, double[] work) + { + LUInverseFactored(a, order, ipiv); + } + + /// + /// Solves A*X=B for X using LU factorization. + /// + /// The number of columns of B. + /// The square matrix A. + /// The order of the square matrix . + /// The B matrix. + /// This is equivalent to the GETRF and GETRS LAPACK routines. + public void LUSolve(int columnsOfB, double[] a, int order, double[] 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"); + } + + var ipiv = new int[order]; + LUFactor(a, order, ipiv); + LUSolveFactored(columnsOfB, a, order, ipiv, b); + } + + /// + /// Solves A*X=B for X using a previously factored A matrix. + /// + /// The number of columns of B. + /// The factored A matrix. + /// The order of the square matrix . + /// The pivot indices of . + /// The B matrix. + /// This is equivalent to the GETRS LAPACK routine. + public void LUSolveFactored(int columnsOfB, double[] a, int order, int[] ipiv, double[] b) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (ipiv == null) + { + throw new ArgumentNullException("ipiv"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (a.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); + } + + if (ipiv.Length != order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); + } + + if (b.Length != order * columnsOfB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + // Compute the column vector P*B + for (var i = 0; i < ipiv.Length; i++) + { + if (ipiv[i] == i) + { + continue; + } + + var p = ipiv[i]; + for (var j = 0; j < columnsOfB; j++) + { + var indexk = j * order; + var indexkp = indexk + p; + var indexkj = indexk + i; + var temp = b[indexkp]; + b[indexkp] = b[indexkj]; + b[indexkj] = temp; + } + } + + // Solve L*Y = P*B + for (var k = 0; k < order; k++) + { + var korder = k * order; + for (var i = k + 1; i < order; i++) + { + for (var j = 0; j < columnsOfB; j++) + { + var index = j * order; + b[i + index] -= b[k + index] * a[i + korder]; + } + } + } + + // Solve U*X = Y; + for (var k = order - 1; k >= 0; k--) + { + var korder = k + (k * order); + for (var j = 0; j < columnsOfB; j++) + { + b[k + (j * order)] /= a[korder]; + } + + korder = k * order; + for (var i = 0; i < k; i++) + { + for (var j = 0; j < columnsOfB; j++) + { + var index = j * order; + b[i + index] -= b[k + index] * a[i + korder]; + } + } + } + } + + /// + /// Solves A*X=B for X using LU factorization. + /// + /// How to transpose the matrix. + /// The number of columns of B. + /// The square matrix A. + /// The order of the square matrix . + /// The B matrix. + /// This is equivalent to the GETRF and GETRS LAPACK routines. + public void LUSolve(Transpose transposeA, int columnsOfB, double[] a, int order, double[] 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"); + } + + var ipiv = new int[order]; + LUFactor(a, order, ipiv); + LUSolveFactored(transposeA, columnsOfB, a, order, ipiv, b); + } + + /// + /// Solves A*X=B for X using a previously factored A matrix. + /// + /// How to transpose the matrix. + /// The number of columns of B. + /// The factored A matrix. + /// The order of the square matrix . + /// The pivot indices of . + /// The B matrix. + /// This is equivalent to the GETRS LAPACK routine. + public void LUSolveFactored(Transpose transposeA, int columnsOfB, double[] a, int order, int[] ipiv, double[] b) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (ipiv == null) + { + throw new ArgumentNullException("ipiv"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (a.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); + } + + if (ipiv.Length != order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); + } + + if (b.Length != order * columnsOfB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if ((transposeA == Transpose.Transpose) || (transposeA == Transpose.ConjugateTranspose)) + { + var aT = new double[a.Length]; + for (var i = 0; i < order; i++) + { + for (var j = 0; j < order; j++) + { + aT[(j * order) + i] = a[(i * order) + j]; + } + } + + LUSolveFactored(columnsOfB, aT, order, ipiv, b); + } + else + { + LUSolveFactored(columnsOfB, a, order, ipiv, b); + } + } + + /// + /// Computes the Cholesky factorization of A. + /// + /// On entry, a square, positive definite matrix. On exit, the matrix is overwritten with the + /// the Cholesky factorization. + /// The number of rows or columns in the matrix. + /// This is equivalent to the POTRF LAPACK routine. + public void CholeskyFactor(double[] a, int order) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + var tmpColumn = new double[order]; + + // Main loop - along the diagonal + for (int ij = 0; ij < order; ij++) + { + // "Pivot" element + double tmpVal = a[(ij * order) + ij]; + + if (tmpVal > 0.0) + { + tmpVal = Math.Sqrt(tmpVal); + a[(ij * order) + ij] = tmpVal; + tmpColumn[ij] = tmpVal; + + // Calculate multipliers and copy to local column + // Current column, below the diagonal + for (int i = ij + 1; i < order; i++) + { + a[(ij * order) + i] /= tmpVal; + tmpColumn[i] = a[(ij * order) + i]; + } + + // Remaining columns, below the diagonal + DoCholeskyStep(a, order, ij + 1, order, tmpColumn, Control.NumberOfParallelWorkerThreads); + } + else + { + throw new ArgumentException(Resources.ArgumentMatrixPositiveDefinite); + } + + for (int i = ij + 1; i < order; i++) + { + a[(i * order) + ij] = 0.0; + } + } + } + + /// + /// Calculate Cholesky step + /// + /// Factor matrix + /// Number of rows + /// Column start + /// Total columns + /// Multipliears calculated previously + /// Number of available processors + private static void DoCholeskyStep(double[] data, int rowDim, int firstCol, int colLimit, double[] multipliers, int availableCores) + { + var tmpColCount = colLimit - firstCol; + + if ((availableCores > 1) && (tmpColCount > 200)) + { + var tmpSplit = firstCol + (tmpColCount / 3); + var tmpCores = availableCores / 2; + + CommonParallel.Invoke( + () => DoCholeskyStep(data, rowDim, firstCol, tmpSplit, multipliers, tmpCores), + () => DoCholeskyStep(data, rowDim, tmpSplit, colLimit, multipliers, tmpCores)); + } + else + { + for (var j = firstCol; j < colLimit; j++) + { + var tmpVal = multipliers[j]; + for (var i = j; i < rowDim; i++) + { + data[(j * rowDim) + i] -= multipliers[i] * tmpVal; + } + } + } + } + + /// + /// Solves A*X=B for X using Cholesky factorization. + /// + /// The square, positive definite matrix A. + /// The number of rows and columns in A. + /// The B matrix. + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. + /// This is equivalent to the POTRF add POTRS LAPACK routines. + public void CholeskySolve(double[] a, int orderA, double[] b, int rowsB, int columnsB) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (orderA != rowsB) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions); + } + + if (ReferenceEquals(a, b)) + { + throw new ArgumentException(Resources.ArgumentReferenceDifferent); + } + + CholeskyFactor(a, orderA); + CholeskySolveFactored(a, orderA, b, rowsB, columnsB); + } + + /// + /// Solves A*X=B for X using a previously factored A matrix. + /// + /// The square, positive definite matrix A. Has to be different than . + /// The number of rows and columns in A. + /// The B matrix. Has to be different than . + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. + /// This is equivalent to the POTRS LAPACK routine. + public void CholeskySolveFactored(double[] a, int orderA, double[] b, int rowsB, int columnsB) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (orderA != rowsB) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions); + } + + if (ReferenceEquals(a, b)) + { + throw new ArgumentException(Resources.ArgumentReferenceDifferent); + } + + CommonParallel.For( + 0, + columnsB, + c => + { + var cindex = c * orderA; + + // Solve L*Y = B; + double sum; + for (var i = 0; i < orderA; i++) + { + sum = b[cindex + i]; + for (var k = i - 1; k >= 0; k--) + { + sum -= a[(k * orderA) + i] * b[cindex + k]; + } + + b[cindex + i] = sum / a[(i * orderA) + i]; + } + + // Solve L'*X = Y; + for (var i = orderA - 1; i >= 0; i--) + { + sum = b[cindex + i]; + var iindex = i * orderA; + for (var k = i + 1; k < orderA; k++) + { + sum -= a[iindex + k] * b[cindex + k]; + } + + b[cindex + i] = sum / a[iindex + i]; + } + }); + } + + /// + /// Computes the QR factorization of A. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the R matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A M by M matrix that holds the Q matrix of the + /// QR factorization. + /// This is similar to the GEQRF and ORGQR LAPACK routines. + public void QRFactor(double[] r, int rowsR, int columnsR, double[] q) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + var work = new double[rowsR * rowsR]; + QRFactor(r, rowsR, columnsR, q, work); + } + + /// + /// Computes the QR factorization of A. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the R matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A M by M matrix that holds the Q matrix of the + /// QR factorization. + /// 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. + /// This is similar to the GEQRF and ORGQR LAPACK routines. + public void QRFactor(double[] r, int rowsR, int columnsR, double[] q, double[] work) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (work == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + if (work.Length < rowsR * rowsR) + { + work[0] = rowsR * rowsR; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + CommonParallel.For(0, rowsR, i => q[(i * rowsR) + i] = 1.0); + + var minmn = Math.Min(rowsR, columnsR); + for (var i = 0; i < minmn; i++) + { + GenerateColumn(work, r, rowsR, i, i); + ComputeQR(work, i, r, i, rowsR, i + 1, columnsR, Control.NumberOfParallelWorkerThreads); + } + + for (var i = minmn - 1; i >= 0; i--) + { + ComputeQR(work, i, q, i, rowsR, i, rowsR, Control.NumberOfParallelWorkerThreads); + } + + work[0] = rowsR * rowsR; + } + + #region QR Factor Helper functions + + /// + /// Perform calculation of Q or R + /// + /// Work array + /// Index of colunn in work array + /// Q or R matrices + /// The first row in + /// The last row + /// The first column + /// The last column + /// Number of available CPUs + private static void ComputeQR(double[] work, int workIndex, double[] a, int rowStart, int rowCount, int columnStart, int columnCount, int availableCores) + { + if (rowStart > rowCount || columnStart > columnCount) + { + return; + } + + var tmpColCount = columnCount - columnStart; + + if ((availableCores > 1) && (tmpColCount > 200)) + { + var tmpSplit = columnStart + (tmpColCount / 2); + var tmpCores = availableCores / 2; + + CommonParallel.Invoke( + () => ComputeQR(work, workIndex, a, rowStart, rowCount, columnStart, tmpSplit, tmpCores), + () => ComputeQR(work, workIndex, a, rowStart, rowCount, tmpSplit, columnCount, tmpCores)); + } + else + { + for (var j = columnStart; j < columnCount; j++) + { + var scale = 0.0; + for (var i = rowStart; i < rowCount; i++) + { + scale += work[(workIndex * rowCount) + i - rowStart] * a[(j * rowCount) + i]; + } + + for (var i = rowStart; i < rowCount; i++) + { + a[(j * rowCount) + i] -= work[(workIndex * rowCount) + i - rowStart] * scale; + } + } + } + } + + /// + /// Generate column from initial matrix to work array + /// + /// Work array + /// Initial matrix + /// The number of rows in matrix + /// The firts row + /// Column index + private static void GenerateColumn(double[] work, double[] a, int rowCount, int row, int column) + { + var tmp = column * rowCount; + var index = tmp + row; + + CommonParallel.For( + row, + rowCount, + i => + { + var iIndex = tmp + i; + work[iIndex - row] = a[iIndex]; + a[iIndex] = 0.0; + }); + + var norm = 0.0; + for (var i = 0; i < rowCount - row; ++i) + { + var iindex = tmp + i; + norm += work[iindex] * work[iindex]; + } + + norm = Math.Sqrt(norm); + if (row == rowCount - 1 || norm == 0) + { + a[index] = -work[tmp]; + work[tmp] = Math.Sqrt(2.0); + return; + } + + var scale = 1.0 / norm; + if (work[tmp] < 0.0) + { + scale *= -1.0; + } + + a[index] = -1.0 / scale; + CommonParallel.For(0, rowCount - row, i => work[tmp + i] *= scale); + work[tmp] += 1.0; + + var s = Math.Sqrt(1.0 / work[tmp]); + CommonParallel.For(0, rowCount - row, i => work[tmp + i] *= s); + } + + #endregion + + /// + /// Solves A*X=B for X using QR factorization of A. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the R matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A M by M matrix that holds the Q matrix of the + /// QR factorization. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + public void QRSolve(double[] r, int rowsR, int columnsR, double[] q, double[] b, int columnsB, double[] x) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (b == null) + { + throw new ArgumentNullException("q"); + } + + if (x == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + if (b.Length != rowsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + } + + var work = new double[rowsR * rowsR]; + QRSolve(r, rowsR, columnsR, q, b, columnsB, x, work); + } + + /// + /// Solves A*X=B for X using QR factorization of A. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the R matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A M by M matrix that holds the Q matrix of the + /// QR factorization. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + /// 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. + public void QRSolve(double[] r, int rowsR, int columnsR, double[] q, double[] b, int columnsB, double[] x, double[] work) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (b == null) + { + throw new ArgumentNullException("q"); + } + + if (x == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + if (b.Length != rowsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + } + + if (work.Length < rowsR * rowsR) + { + work[0] = rowsR * rowsR; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + QRFactor(r, rowsR, columnsR, q, work); + QRSolveFactored(q, r, rowsR, columnsR, b, columnsB, x); + + work[0] = rowsR * rowsR; + } + + /// + /// Solves A*X=B for X using a previously QR factored matrix. + /// + /// The Q matrix obtained by calling . + /// The R matrix obtained by calling . + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + public void QRSolveFactored(double[] q, double[] r, int rowsR, int columnsR, double[] b, int columnsB, double[] x) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (b == null) + { + throw new ArgumentNullException("q"); + } + + if (x == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + if (b.Length != rowsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + } + + var sol = new double[b.Length]; + + // Copy B matrix to "sol", so B data will not be changed + Buffer.BlockCopy(b, 0, sol, 0, b.Length * Constants.SizeOfDouble); + + // Compute Y = transpose(Q)*B + var column = new double[rowsR]; + for (var j = 0; j < columnsB; j++) + { + var jm = j * rowsR; + CommonParallel.For(0, rowsR, k => column[k] = sol[jm + k]); + CommonParallel.For( + 0, + rowsR, + i => + { + var im = i * rowsR; + sol[jm + i] = CommonParallel.Aggregate(0, rowsR, k => q[im + k] * column[k]); + }); + } + + // Solve R*X = Y; + for (var k = columnsR - 1; k >= 0; k--) + { + var km = k * rowsR; + for (var j = 0; j < columnsB; j++) + { + sol[(j * rowsR) + k] /= r[km + k]; + } + + for (var i = 0; i < k; i++) + { + for (var j = 0; j < columnsB; j++) + { + var jm = j * rowsR; + sol[jm + i] -= sol[jm + k] * r[km + i]; + } + } + } + + // Fill result matrix + CommonParallel.For( + 0, + columnsR, + row => + { + for (var col = 0; col < columnsB; col++) + { + x[(col * columnsR) + row] = sol[row + (col * rowsR)]; + } + }); + } + + /// + /// Computes the singular value decomposition of A. + /// + /// Compute the singular U and VT vectors or not. + /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The singular values of A in ascending value. + /// If is true, on exit U contains the left + /// singular vectors. + /// If is true, on exit VT contains the transposed + /// right singular vectors. + /// This is equivalent to the GESVD LAPACK routine. + public void SingularValueDecomposition(bool computeVectors, double[] a, int rowsA, int columnsA, double[] s, double[] u, double[] vt) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (s == null) + { + throw new ArgumentNullException("s"); + } + + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (vt == null) + { + throw new ArgumentNullException("vt"); + } + + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } + + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } + + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } + + // Actually "work = new double[aRows]" is acceptable size of work array. I set size proposed in method description + var work = new double[Math.Max((3 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA), 5 * Math.Min(rowsA, columnsA))]; + SingularValueDecomposition(computeVectors, a, rowsA, columnsA, s, u, vt, work); + } + + /// + /// Computes the singular value decomposition of A. + /// + /// Compute the singular U and VT vectors or not. + /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The singular values of A in ascending value. + /// If is true, on exit U contains the left + /// singular vectors. + /// If is true, on exit VT contains the transposed + /// right singular vectors. + /// The work array. For real matrices, the work array should be at least + /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). + /// On exit, work[0] contains the optimal work size value. + /// This is equivalent to the GESVD LAPACK routine. + public void SingularValueDecomposition(bool computeVectors, double[] a, int rowsA, int columnsA, double[] s, double[] u, double[] vt, double[] work) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (s == null) + { + throw new ArgumentNullException("s"); + } + + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (vt == null) + { + throw new ArgumentNullException("vt"); + } + + if (work == null) + { + throw new ArgumentNullException("work"); + } + + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } + + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } + + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } + + if (work.Length == 0) + { + throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); + } + + if (work.Length < rowsA) + { + work[0] = rowsA; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + const int Maxiter = 1000; + + var e = new double[columnsA]; + var v = new double[vt.Length]; + var stemp = new double[Math.Min(rowsA + 1, columnsA)]; + + int i, j, l, lp1; + + var cs = 0.0; + var sn = 0.0; + double t; + + var ncu = rowsA; + + // Reduce matrix to bidiagonal form, storing the diagonal elements + // in "s" and the super-diagonal elements in "e". + var nct = Math.Min(rowsA - 1, columnsA); + var nrt = Math.Max(0, Math.Min(columnsA - 2, rowsA)); + 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 vector s[l]. + var l1 = l; + stemp[l] = Math.Sqrt(CommonParallel.Aggregate(l, rowsA, i1 => (a[(l1 * rowsA) + i1] * a[(l1 * rowsA) + i1]))); + + if (stemp[l] != 0.0) + { + if (a[(l * rowsA) + l] != 0.0) + { + stemp[l] = Math.Abs(stemp[l]) * (a[(l * rowsA) + l] / Math.Abs(a[(l * rowsA) + l])); + } + + // A part of column "l" of Matrix A from row "l" to end multiply by 1.0 / s[l] + for (i = l; i < rowsA; i++) + { + a[(l * rowsA) + i] = a[(l * rowsA) + i] * (1.0 / stemp[l]); + } + + a[(l * rowsA) + l] = 1.0 + a[(l * rowsA) + l]; + } + + stemp[l] = -stemp[l]; + } + + for (j = lp1; j < columnsA; j++) + { + if (l < nct) + { + if (stemp[l] != 0.0) + { + // Apply the transformation. + t = 0.0; + for (i = l; i < rowsA; i++) + { + t += a[(j * rowsA) + i] * a[(l * rowsA) + i]; + } + + t = -t / a[(l * rowsA) + l]; + + for (var ii = l; ii < rowsA; ii++) + { + a[(j * rowsA) + ii] += t * a[(l * rowsA) + ii]; + } + } + } + + // Place the l-th row of matrix into "e" for the + // subsequent calculation of the row transformation. + e[j] = a[(j * rowsA) + l]; + } + + if (computeVectors && l < nct) + { + // Place the transformation in "u" for subsequent back multiplication. + for (i = l; i < rowsA; i++) + { + u[(l * rowsA) + i] = a[(l * rowsA) + i]; + } + } + + if (l >= nrt) + { + continue; + } + + // Compute the l-th row transformation and place the l-th super-diagonal in e(l). + var enorm = 0.0; + for (i = lp1; i < e.Length; i++) + { + enorm += e[i] * e[i]; + } + + e[l] = Math.Sqrt(enorm); + if (e[l] != 0.0) + { + if (e[lp1] != 0.0) + { + e[l] = Math.Abs(e[l]) * (e[lp1] / Math.Abs(e[lp1])); + } + + // Scale vector "e" from "lp1" by 1.0 / e[l] + for (i = lp1; i < e.Length; i++) + { + e[i] = e[i] * (1.0 / e[l]); + } + + e[lp1] = 1.0 + e[lp1]; + } + + e[l] = -e[l]; + + if (lp1 < rowsA && e[l] != 0.0) + { + // Apply the transformation. + for (i = lp1; i < rowsA; i++) + { + work[i] = 0.0; + } + + for (j = lp1; j < columnsA; j++) + { + for (var ii = lp1; ii < rowsA; ii++) + { + work[ii] += e[j] * a[(j * rowsA) + ii]; + } + } + + for (j = lp1; j < columnsA; j++) + { + var ww = -e[j] / e[lp1]; + for (var ii = lp1; ii < rowsA; ii++) + { + a[(j * rowsA) + ii] += ww * work[ii]; + } + } + } + + if (!computeVectors) + { + continue; + } + + // Place the transformation in v for subsequent back multiplication. + for (i = lp1; i < columnsA; i++) + { + v[(l * columnsA) + i] = e[i]; + } + } + + // Set up the final bidiagonal matrix or order m. + var m = Math.Min(columnsA, rowsA + 1); + var nctp1 = nct + 1; + var nrtp1 = nrt + 1; + if (nct < columnsA) + { + stemp[nctp1 - 1] = a[((nctp1 - 1) * rowsA) + (nctp1 - 1)]; + } + + if (rowsA < m) + { + stemp[m - 1] = 0.0; + } + + if (nrtp1 < m) + { + e[nrtp1 - 1] = a[((m - 1) * rowsA) + (nrtp1 - 1)]; + } + + e[m - 1] = 0.0; + + // If required, generate "u". + if (computeVectors) + { + for (j = nctp1 - 1; j < ncu; j++) + { + for (i = 0; i < rowsA; i++) + { + u[(j * rowsA) + i] = 0.0; + } + + u[(j * rowsA) + j] = 1.0; + } + + for (l = nct - 1; l >= 0; l--) + { + if (stemp[l] != 0.0) + { + for (j = l + 1; j < ncu; j++) + { + t = 0.0; + for (i = l; i < rowsA; i++) + { + t += u[(j * rowsA) + i] * u[(l * rowsA) + i]; + } + + t = -t / u[(l * rowsA) + l]; + + for (var ii = l; ii < rowsA; ii++) + { + u[(j * rowsA) + ii] += t * u[(l * rowsA) + ii]; + } + } + + // A part of column "l" of matrix A from row "l" to end multiply by -1.0 + for (i = l; i < rowsA; i++) + { + u[(l * rowsA) + i] = u[(l * rowsA) + i] * -1.0; + } + + u[(l * rowsA) + l] = 1.0 + u[(l * rowsA) + l]; + for (i = 0; i < l; i++) + { + u[(l * rowsA) + i] = 0.0; + } + } + else + { + for (i = 0; i < rowsA; i++) + { + u[(l * rowsA) + i] = 0.0; + } + + u[(l * rowsA) + l] = 1.0; + } + } + } + + // If it is required, generate v. + if (computeVectors) + { + for (l = columnsA - 1; l >= 0; l--) + { + lp1 = l + 1; + if (l < nrt) + { + if (e[l] != 0.0) + { + for (j = lp1; j < columnsA; j++) + { + t = 0.0; + for (i = lp1; i < columnsA; i++) + { + t += v[(j * columnsA) + i] * v[(l * columnsA) + i]; + } + + t = -t / v[(l * columnsA) + lp1]; + for (var ii = l; ii < columnsA; ii++) + { + v[(j * columnsA) + ii] += t * v[(l * columnsA) + ii]; + } + } + } + } + + for (i = 0; i < columnsA; i++) + { + v[(l * columnsA) + i] = 0.0; + } + + v[(l * columnsA) + l] = 1.0; + } + } + + // Transform "s" and "e" so that they are double + for (i = 0; i < m; i++) + { + double r; + if (stemp[i] != 0.0) + { + t = stemp[i]; + r = stemp[i] / t; + stemp[i] = t; + if (i < m - 1) + { + e[i] = e[i] / r; + } + + if (computeVectors) + { + // A part of column "i" of matrix U from row 0 to end multiply by r + for (j = 0; j < rowsA; j++) + { + u[(i * rowsA) + j] = u[(i * rowsA) + j] * r; + } + } + } + + // Exit + if (i == m - 1) + { + break; + } + + if (e[i] == 0.0) + { + continue; + } + + t = e[i]; + r = t / e[i]; + e[i] = t; + stemp[i + 1] = stemp[i + 1] * r; + if (!computeVectors) + { + continue; + } + + // A part of column "i+1" of matrix VT from row 0 to end multiply by r + for (j = 0; j < columnsA; j++) + { + v[((i + 1) * columnsA) + j] = v[((i + 1) * columnsA) + j] * 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. + 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 mS[m] and e[l-1] are negligible and l < m + // kase = 2: if mS[l] is negligible and l < m + // kase = 3: if e[l-1] is negligible, l < m, and mS[l, ..., mS[m] are not negligible (qr step). + // kase = 4: if e[m-1] is negligible (convergence). + double ztest; + double test; + for (l = m - 2; l >= 0; l--) + { + test = Math.Abs(stemp[l]) + Math.Abs(stemp[l + 1]); + ztest = test + Math.Abs(e[l]); + if (ztest.AlmostEqualInDecimalPlaces(test, 15)) + { + e[l] = 0.0; + break; + } + } + + int kase; + if (l == m - 2) + { + kase = 4; + } + else + { + int ls; + for (ls = m - 1; ls > l; ls--) + { + test = 0.0; + 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(stemp[ls]); + if (ztest.AlmostEqualInDecimalPlaces(test, 15)) + { + stemp[ls] = 0.0; + 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; + double f; + switch (kase) + { + // Deflate negligible s[m]. + case 1: + f = e[m - 2]; + e[m - 2] = 0.0; + double t1; + for (var kk = l; kk < m - 1; kk++) + { + k = m - 2 - kk + l; + t1 = stemp[k]; + + Drotg(ref t1, ref f, ref cs, ref sn); + stemp[k] = t1; + if (k != l) + { + f = -sn * e[k - 1]; + e[k - 1] = cs * e[k - 1]; + } + + if (computeVectors) + { + // Rotate + for (i = 0; i < columnsA; i++) + { + var z = (cs * v[(k * columnsA) + i]) + (sn * v[((m - 1) * columnsA) + i]); + v[((m - 1) * columnsA) + i] = (cs * v[((m - 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); + v[(k * columnsA) + i] = z; + } + } + } + + break; + + // Split at negligible s[l]. + case 2: + f = e[l - 1]; + e[l - 1] = 0.0; + for (k = l; k < m; k++) + { + t1 = stemp[k]; + Drotg(ref t1, ref f, ref cs, ref sn); + stemp[k] = t1; + f = -sn * e[k]; + e[k] = cs * e[k]; + if (computeVectors) + { + // Rotate + for (i = 0; i < rowsA; i++) + { + var z = (cs * u[(k * rowsA) + i]) + (sn * u[((l - 1) * rowsA) + i]); + u[((l - 1) * rowsA) + i] = (cs * u[((l - 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); + u[(k * rowsA) + i] = z; + } + } + } + + break; + + // Perform one qr step. + case 3: + + // calculate the shift. + var scale = 0.0; + scale = Math.Max(scale, Math.Abs(stemp[m - 1])); + scale = Math.Max(scale, Math.Abs(stemp[m - 2])); + scale = Math.Max(scale, Math.Abs(e[m - 2])); + scale = Math.Max(scale, Math.Abs(stemp[l])); + scale = Math.Max(scale, Math.Abs(e[l])); + var sm = stemp[m - 1] / scale; + var smm1 = stemp[m - 2] / scale; + var emm1 = e[m - 2] / scale; + var sl = stemp[l] / scale; + var el = e[l] / scale; + var b = (((smm1 + sm) * (smm1 - sm)) + (emm1 * emm1)) / 2.0; + var c = (sm * emm1) * (sm * emm1); + var shift = 0.0; + if (b != 0.0 || c != 0.0) + { + shift = 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 * stemp[k]) + (sn * e[k]); + e[k] = (cs * e[k]) - (sn * stemp[k]); + g = sn * stemp[k + 1]; + stemp[k + 1] = cs * stemp[k + 1]; + if (computeVectors) + { + for (i = 0; i < columnsA; i++) + { + var z = (cs * v[(k * columnsA) + i]) + (sn * v[((k + 1) * columnsA) + i]); + v[((k + 1) * columnsA) + i] = (cs * v[((k + 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); + v[(k * columnsA) + i] = z; + } + } + + Drotg(ref f, ref g, ref cs, ref sn); + stemp[k] = f; + f = (cs * e[k]) + (sn * stemp[k + 1]); + stemp[k + 1] = -(sn * e[k]) + (cs * stemp[k + 1]); + g = sn * e[k + 1]; + e[k + 1] = cs * e[k + 1]; + if (computeVectors && k < rowsA) + { + for (i = 0; i < rowsA; i++) + { + var z = (cs * u[(k * rowsA) + i]) + (sn * u[((k + 1) * rowsA) + i]); + u[((k + 1) * rowsA) + i] = (cs * u[((k + 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); + u[(k * rowsA) + i] = z; + } + } + } + + e[m - 2] = f; + iter = iter + 1; + break; + + // Convergence + case 4: + + // Make the singular value positive + if (stemp[l] < 0.0) + { + stemp[l] = -stemp[l]; + if (computeVectors) + { + // A part of column "l" of matrix VT from row 0 to end multiply by -1 + for (i = 0; i < columnsA; i++) + { + v[(l * columnsA) + i] = v[(l * columnsA) + i] * -1.0; + } + } + } + + // Order the singular value. + while (l != mn - 1) + { + if (stemp[l] >= stemp[l + 1]) + { + break; + } + + t = stemp[l]; + stemp[l] = stemp[l + 1]; + stemp[l + 1] = t; + if (computeVectors && l < columnsA) + { + // Swap columns l, l + 1 + for (i = 0; i < columnsA; i++) + { + var z = v[(l * columnsA) + i]; + v[(l * columnsA) + i] = v[((l + 1) * columnsA) + i]; + v[((l + 1) * columnsA) + i] = z; + } + } + + if (computeVectors && l < rowsA) + { + // Swap columns l, l + 1 + for (i = 0; i < rowsA; i++) + { + var z = u[(l * rowsA) + i]; + u[(l * rowsA) + i] = u[((l + 1) * rowsA) + i]; + u[((l + 1) * rowsA) + i] = z; + } + } + + l = l + 1; + } + + iter = 0; + m = m - 1; + break; + } + } + + if (computeVectors) + { + // Finally transpose "v" to get "vt" matrix + for (i = 0; i < columnsA; i++) + { + for (j = 0; j < columnsA; j++) + { + vt[(j * columnsA) + i] = v[(i * columnsA) + j]; + } + } + } + + // Copy stemp to s with size adjustment. We are using ported copy of linpack's svd code and it uses + // a singular vector of length rows+1 when rows < columns. The last element is not used and needs to be removed. + // We should port lapack's svd routine to remove this problem. + Buffer.BlockCopy(stemp, 0, s, 0, Math.Min(rowsA, columnsA) * Constants.SizeOfDouble); + + // On return the first element of the work array stores the min size of the work array could have been + // work[0] = Math.Max(3 * Math.Min(aRows, aColumns) + Math.Max(aRows, aColumns), 5 * Math.Min(aRows, aColumns)); + work[0] = rowsA; + } + + /// + /// Given the Cartesian coordinates (da, db) of a point p, these fucntion return the parameters da, db, c, and s + /// associated with the Givens rotation that zeros the y-coordinate of the point. + /// + /// Provides the x-coordinate of the point p. On exit contains the parameter r associated with the Givens rotation + /// Provides the y-coordinate of the point p. On exit contains the parameter z associated with the Givens rotation + /// Contains the parameter c associated with the Givens rotation + /// Contains the parameter s associated with the Givens rotation + /// This is equivalent to the DROTG LAPACK routine. + private static void Drotg(ref double da, ref double db, ref double c, ref double s) + { + double r, z; + + var roe = db; + var absda = Math.Abs(da); + var absdb = Math.Abs(db); + if (absda > absdb) + { + roe = da; + } + + var scale = absda + absdb; + if (scale == 0.0) + { + c = 1.0; + s = 0.0; + r = 0.0; + z = 0.0; + } + else + { + var sda = da / scale; + var sdb = db / scale; + r = scale * Math.Sqrt((sda * sda) + (sdb * sdb)); + if (roe < 0.0) + { + r = -r; + } + + c = da / r; + s = db / r; + z = 1.0; + if (absda > absdb) + { + z = s; + } + + if (absdb >= absda && c != 0.0) + { + z = 1.0 / c; + } + } + + da = r; + db = z; + } + + /// + /// Solves A*X=B for X using the singular value decomposition of A. + /// + /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The singular values of A in ascending value. + /// On exit U contains the left singular vectors. + /// On exit VT contains the transposed right singular vectors. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + public void SvdSolve(double[] a, int rowsA, int columnsA, double[] s, double[] u, double[] vt, double[] b, int columnsB, double[] x) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (s == null) + { + throw new ArgumentNullException("s"); + } + + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (vt == null) + { + throw new ArgumentNullException("vt"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } + + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } + + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } + + if (b.Length != rowsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + // Actually "work = new double[aRows]" is acceptable size of work array. I set size proposed in method description + var work = new double[Math.Max((3 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA), 5 * Math.Min(rowsA, columnsA))]; + SvdSolve(a, rowsA, columnsA, s, u, vt, b, columnsB, x, work); + } + + /// + /// Solves A*X=B for X using the singular value decomposition of A. + /// + /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The singular values of A in ascending value. + /// On exit U contains the left singular vectors. + /// On exit VT contains the transposed right singular vectors. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + /// The work array. For real matrices, the work array should be at least + /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). + /// On exit, work[0] contains the optimal work size value. + public void SvdSolve(double[] a, int rowsA, int columnsA, double[] s, double[] u, double[] vt, double[] b, int columnsB, double[] x, double[] work) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (s == null) + { + throw new ArgumentNullException("s"); + } + + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (vt == null) + { + throw new ArgumentNullException("vt"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } + + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } + + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } + + if (b.Length != rowsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (work.Length == 0) + { + throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); + } + + if (work.Length < rowsA) + { + work[0] = rowsA; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + SingularValueDecomposition(true, a, rowsA, columnsA, s, u, vt, work); + SvdSolveFactored(rowsA, columnsA, s, u, vt, b, columnsB, x); + } + + /// + /// Solves A*X=B for X using a previously SVD decomposed matrix. + /// + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The s values returned by . + /// The left singular vectors returned by . + /// The right singular vectors returned by . + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + public void SvdSolveFactored(int rowsA, int columnsA, double[] s, double[] u, double[] vt, double[] b, int columnsB, double[] x) + { + if (s == null) + { + throw new ArgumentNullException("s"); + } + + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (vt == null) + { + throw new ArgumentNullException("vt"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } + + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } + + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } + + if (b.Length != rowsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + var mn = Math.Min(rowsA, columnsA); + var tmp = new double[columnsA]; + + for (var k = 0; k < columnsB; k++) + { + for (var j = 0; j < columnsA; j++) + { + double value = 0; + if (j < mn) + { + for (var i = 0; i < rowsA; i++) + { + value += u[(j * rowsA) + i] * b[(k * rowsA) + i]; + } + + value /= s[j]; + } + + tmp[j] = value; + } + + for (var j = 0; j < columnsA; j++) + { + double value = 0; + for (var i = 0; i < columnsA; i++) + { + value += vt[(j * columnsA) + i] * tmp[i]; + } + + x[(k * columnsA) + j] = value; + } + } + } + } +} diff --git a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Single.cs b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Single.cs new file mode 100644 index 00000000..fde7d228 --- /dev/null +++ b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.Single.cs @@ -0,0 +1,2911 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// http://mathnetnumerics.codeplex.com +// Copyright (c) 2009-2010 Math.NET +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +namespace MathNet.Numerics.Algorithms.LinearAlgebra +{ + using System; + using System.Numerics; + using Properties; + using Threading; + + /// + /// The managed linear algebra provider. + /// + public partial class ManagedLinearAlgebraProvider : ILinearAlgebraProvider + { + /// + /// Adds a scaled vector to another: y += alpha*x. + /// + /// The vector to update. + /// The value to scale by. + /// The vector to add to . + /// This equivalent to the AXPY BLAS routine. + public void AddVectorToScaledVector(float[] y, float alpha, float[] x) + { + 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 (alpha == 0.0) + { + return; + } + + if (alpha == 1.0) + { + CommonParallel.For(0, y.Length, i => y[i] += x[i]); + } + else + { + CommonParallel.For(0, y.Length, i => y[i] += alpha * x[i]); + } + } + + /// + /// Scales an array. Can be used to scale a vector and a matrix. + /// + /// The scalar. + /// The values to scale. + /// This is equivalent to the SCAL BLAS routine. + public void ScaleArray(float alpha, float[] x) + { + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (alpha == 1.0) + { + return; + } + + CommonParallel.For(0, x.Length, i => x[i] = alpha * x[i]); + } + + /// + /// Computes the dot product of x and y. + /// + /// The vector x. + /// The vector y. + /// The dot product of x and y. + /// This is equivalent to the DOT BLAS routine. + public float DotProduct(float[] x, float[] y) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (y.Length != x.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + float sum = 0; + CommonParallel.For(0, y.Length, index => sum += y[index] * x[index]); + return sum; + } + + /// + /// Does a point wise add of two arrays z = x + y. This can be used + /// to add vectors or matrices. + /// + /// The array x. + /// The array y. + /// The result of the addition. + /// There is no equivalent BLAS routine, but many libraries + /// provide optimized (parallel and/or vectorized) versions of this + /// routine. + public void AddArrays(float[] x, float[] y, float[] result) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (y.Length != x.Length || y.Length != result.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + CommonParallel.For(0, y.Length, i => result[i] = x[i] + y[i]); + } + + /// + /// Does a point wise subtraction of two arrays z = x - y. This can be used + /// to subtract vectors or matrices. + /// + /// The array x. + /// The array y. + /// The result of the subtraction. + /// There is no equivalent BLAS routine, but many libraries + /// provide optimized (parallel and/or vectorized) versions of this + /// routine. + public void SubtractArrays(float[] x, float[] y, float[] result) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (y.Length != x.Length || y.Length != result.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + CommonParallel.For(0, y.Length, i => result[i] = x[i] - y[i]); + } + + /// + /// Does a point wise multiplication of two arrays z = x * y. This can be used + /// to multiple elements of vectors or matrices. + /// + /// The array x. + /// The array y. + /// The result of the point wise multiplication. + /// There is no equivalent BLAS routine, but many libraries + /// provide optimized (parallel and/or vectorized) versions of this + /// routine. + public void PointWiseMultiplyArrays(float[] x, float[] y, float[] result) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (y.Length != x.Length || y.Length != result.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + CommonParallel.For(0, y.Length, i => result[i] = x[i] * y[i]); + } + + /// + /// Does a point wise division of two arrays z = x / y. This can be used + /// to divide elements of vectors or matrices. + /// + /// The array x. + /// The array y. + /// The result of the point wise division. + /// There is no equivalent BLAS routine, but many libraries + /// provide optimized (parallel and/or vectorized) versions of this + /// routine. + public void PointWiseDivideArrays(float[] x, float[] y, float[] result) + { + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (y.Length != x.Length || y.Length != result.Length) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength); + } + + CommonParallel.For(0, y.Length, index => { result[index] = x[index] / y[index]; }); + } + + /// + /// Computes the requested of the matrix. + /// + /// The type of norm to compute. + /// The number of rows. + /// The number of columns. + /// The matrix to compute the norm from. + /// + /// The requested of the matrix. + /// + public float MatrixNorm(Norm norm, int rows, int columns, float[] matrix) + { + var ret = 0.0; + switch (norm) + { + case Norm.OneNorm: + for (var j = 0; j < columns; j++) + { + var s = 0.0; + for (var i = 0; i < rows; i++) + { + s += Math.Abs(matrix[(j * rows) + i]); + } + + ret = Math.Max(ret, s); + } + + break; + case Norm.LargestAbsoluteValue: + + for (var i = 0; i < rows; i++) + { + for (var j = 0; j < columns; j++) + { + ret = Math.Max(Math.Abs(matrix[(j * rows) + i]), ret); + } + } + + break; + case Norm.InfinityNorm: + for (var i = 0; i < rows; i++) + { + var s = 0.0; + for (var j = 0; j < columns; j++) + { + s += Math.Abs(matrix[(j * rows) + i]); + } + + ret = Math.Max(ret, s); + } + + break; + case Norm.FrobeniusNorm: + var aat = new float[rows * rows]; + MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.Transpose, 1.0f, matrix, rows, columns, matrix, rows, columns, 0.0f, aat); + + for (var i = 0; i < rows; i++) + { + ret += Math.Abs(aat[(i * rows) + i]); + } + + ret = Math.Sqrt(ret); + break; + } + + return Convert.ToSingle(ret); + } + + /// + /// Computes the requested of the matrix. + /// + /// The type of norm to compute. + /// The number of rows. + /// The number of columns. + /// The matrix to compute the norm from. + /// The work array. Only used when + /// and needs to be have a length of at least M (number of rows of . + /// + /// The requested of the matrix. + /// + public float MatrixNorm(Norm norm, int rows, int columns, float[] matrix, float[] work) + { + return MatrixNorm(norm, rows, columns, matrix); + } + + /// + /// Multiples two matrices. result = x * y + /// + /// The x matrix. + /// The number of rows in the x matrix. + /// The number of columns in the x matrix. + /// The y matrix. + /// The number of rows in the y matrix. + /// The number of columns in the y matrix. + /// Where to store the result of the multiplication. + /// This is a simplified version of the BLAS GEMM routine with alpha + /// set to 1.0 and beta set to 0.0, and x and y are not transposed. + public void MatrixMultiply(float[] x, int rowsX, int columnsX, float[] y, int rowsY, int columnsY, float[] result) + { + // First check some basic requirement on the parameters of the matrix multiplication. + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (y == null) + { + throw new ArgumentNullException("y"); + } + + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (rowsX * columnsX != x.Length) + { + throw new ArgumentException("x.Length != xRows * xColumns"); + } + + if (rowsY * columnsY != y.Length) + { + throw new ArgumentException("y.Length != yRows * yColumns"); + } + + if (columnsX != rowsY) + { + throw new ArgumentException("xColumns != yRows"); + } + + if (rowsX * columnsY != result.Length) + { + throw new ArgumentException("xRows * yColumns != result.Length"); + } + + // Check whether we will be overwriting any of our inputs and make copies if necessary. + // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory + // as result, we can do it on a row wise basis. We should investigate this. + float[] xdata; + if (ReferenceEquals(x, result)) + { + xdata = (float[])x.Clone(); + } + else + { + xdata = x; + } + + float[] ydata; + if (ReferenceEquals(y, result)) + { + ydata = (float[])y.Clone(); + } + else + { + ydata = y; + } + + // Start the actual matrix multiplication. + // TODO - For small matrices we should get rid of the parallelism because of startup costs. + // Perhaps the following implementations would be a good one + // http://blog.feradz.com/2009/01/cache-efficient-matrix-multiplication/ + MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 1.0f, xdata, rowsX, columnsX, ydata, rowsY, columnsY, 0.0f, result); + } + + /// + /// Multiplies two matrices and updates another with the result. c = alpha*op(a)*op(b) + beta*c + /// + /// How to transpose the matrix. + /// How to transpose the matrix. + /// The value to scale matrix. + /// The a matrix. + /// The number of rows in the matrix. + /// The number of columns in the matrix. + /// The b matrix + /// The number of rows in the matrix. + /// The number of columns in the matrix. + /// The value to scale the matrix. + /// The c matrix. + public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, float alpha, float[] a, int rowsA, int columnsA, float[] b, int rowsB, int columnsB, float beta, float[] c) + { + // Choose nonsensical values for the number of rows in c; fill them in depending + // on the operations on a and b. + int rowsC; + + // First check some basic requirement on the parameters of the matrix multiplication. + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if ((int)transposeA > 111 && (int)transposeB > 111) + { + if (rowsA != columnsB) + { + throw new ArgumentOutOfRangeException(); + } + + if (columnsA * rowsB != c.Length) + { + throw new ArgumentOutOfRangeException(); + } + + rowsC = columnsA; + } + else if ((int)transposeA > 111) + { + if (rowsA != rowsB) + { + throw new ArgumentOutOfRangeException(); + } + + if (columnsA * columnsB != c.Length) + { + throw new ArgumentOutOfRangeException(); + } + + rowsC = columnsA; + } + else if ((int)transposeB > 111) + { + if (columnsA != columnsB) + { + throw new ArgumentOutOfRangeException(); + } + + if (rowsA * rowsB != c.Length) + { + throw new ArgumentOutOfRangeException(); + } + + rowsC = rowsA; + } + else + { + if (columnsA != rowsB) + { + throw new ArgumentOutOfRangeException(); + } + + if (rowsA * columnsB != c.Length) + { + throw new ArgumentOutOfRangeException(); + } + + rowsC = rowsA; + } + + if (alpha == 0.0 && beta == 0.0) + { + Array.Clear(c, 0, c.Length); + return; + } + + // Check whether we will be overwriting any of our inputs and make copies if necessary. + // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory + // as result, we can do it on a row wise basis. We should investigate this. + float[] adata; + if (ReferenceEquals(a, c)) + { + adata = (float[])a.Clone(); + } + else + { + adata = a; + } + + float[] bdata; + if (ReferenceEquals(b, c)) + { + bdata = (float[])b.Clone(); + } + else + { + bdata = b; + } + + if (alpha == 1.0) + { + if (beta == 0.0) + { + if ((int)transposeA > 111 && (int)transposeB > 111) + { + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + float s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s; + } + }); + } + else if ((int)transposeA > 111) + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + float s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s; + } + }); + } + else if ((int)transposeB > 111) + { + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + float s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s; + } + }); + } + else + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + float s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s; + } + }); + } + } + else + { + if ((int)transposeA > 111 && (int)transposeB > 111) + { + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + float s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (c[jIndex + i] * beta) + s; + } + }); + } + else if ((int)transposeA > 111) + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + float s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s + (c[jcIndex + i] * beta); + } + }); + } + else if ((int)transposeB > 111) + { + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + float s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = s + (c[jIndex + i] * beta); + } + }); + } + else + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + float s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = s + (c[jcIndex + i] * beta); + } + }); + } + } + } + else + { + if ((int)transposeA > 111 && (int)transposeB > 111) + { + CommonParallel.For( + 0, + columnsA, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsB; i++) + { + var iIndex = i * rowsA; + float s = 0; + for (var l = 0; l != columnsB; l++) + { + s += adata[iIndex + l] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (c[jIndex + i] * beta) + (alpha * s); + } + }); + } + else if ((int)transposeA > 111) + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != columnsA; i++) + { + var iIndex = i * rowsA; + float s = 0; + for (var l = 0; l != rowsA; l++) + { + s += adata[iIndex + l] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); + } + }); + } + else if ((int)transposeB > 111) + { + CommonParallel.For( + 0, + rowsB, + j => + { + var jIndex = j * rowsC; + for (var i = 0; i != rowsA; i++) + { + float s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; + } + + c[jIndex + i] = (alpha * s) + (c[jIndex + i] * beta); + } + }); + } + else + { + CommonParallel.For( + 0, + columnsB, + j => + { + var jcIndex = j * rowsC; + var jbIndex = j * rowsB; + for (var i = 0; i != rowsA; i++) + { + float s = 0; + for (var l = 0; l != columnsA; l++) + { + s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; + } + + c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); + } + }); + } + } + } + + /// + /// Computes the LUP factorization of A. P*A = L*U. + /// + /// An by matrix. The matrix is overwritten with the + /// the LU factorization on exit. The lower triangular factor L is stored in under the diagonal of (the diagonal is always 1.0 + /// for the L factor). The upper triangular factor U is stored on and above the diagonal of . + /// The order of the square matrix . + /// On exit, it contains the pivot indices. The size of the array must be . + /// This is equivalent to the GETRF LAPACK routine. + public void LUFactor(float[] data, int order, int[] ipiv) + { + if (data == null) + { + throw new ArgumentNullException("data"); + } + + if (ipiv == null) + { + throw new ArgumentNullException("ipiv"); + } + + if (data.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "data"); + } + + if (ipiv.Length != order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); + } + + // Initialize the pivot matrix to the identity permutation. + for (var i = 0; i < order; i++) + { + ipiv[i] = i; + } + + var vecLUcolj = new float[order]; + + // Outer loop. + for (var j = 0; j < order; j++) + { + var indexj = j * order; + var indexjj = indexj + j; + + // Make a copy of the j-th column to localize references. + for (var i = 0; i < order; i++) + { + vecLUcolj[i] = data[indexj + i]; + } + + // Apply previous transformations. + for (var i = 0; i < order; i++) + { + // Most of the time is spent in the following dot product. + var kmax = Math.Min(i, j); + var s = 0.0f; + for (var k = 0; k < kmax; k++) + { + s += data[(k * order) + i] * vecLUcolj[k]; + } + + data[indexj + i] = vecLUcolj[i] -= s; + } + + // Find pivot and exchange if necessary. + var p = j; + for (var i = j + 1; i < order; i++) + { + if (Math.Abs(vecLUcolj[i]) > Math.Abs(vecLUcolj[p])) + { + p = i; + } + } + + if (p != j) + { + for (var k = 0; k < order; k++) + { + var indexk = k * order; + var indexkp = indexk + p; + var indexkj = indexk + j; + var temp = data[indexkp]; + data[indexkp] = data[indexkj]; + data[indexkj] = temp; + } + + ipiv[j] = p; + } + + // Compute multipliers. + if (j < order & data[indexjj] != 0.0) + { + for (var i = j + 1; i < order; i++) + { + data[indexj + i] /= data[indexjj]; + } + } + } + } + + /// + /// Computes the inverse of matrix using LU factorization. + /// + /// The N by N matrix to invert. Contains the inverse On exit. + /// The order of the square matrix . + /// This is equivalent to the GETRF and GETRI LAPACK routines. + public void LUInverse(float[] a, int order) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (a.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); + } + + var ipiv = new int[order]; + LUFactor(a, order, ipiv); + LUInverseFactored(a, order, ipiv); + } + + /// + /// Computes the inverse of a previously factored matrix. + /// + /// The LU factored N by N matrix. Contains the inverse On exit. + /// The order of the square matrix . + /// The pivot indices of . + /// This is equivalent to the GETRI LAPACK routine. + public void LUInverseFactored(float[] 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 float[a.Length]; + for (var i = 0; i < order; i++) + { + inverse[i + (order * i)] = 1.0f; + } + + LUSolveFactored(order, a, order, ipiv, inverse); + Buffer.BlockCopy(inverse, 0, a, 0, a.Length * Constants.SizeOfFloat); + } + + /// + /// Computes the inverse of matrix using LU factorization. + /// + /// The N by N matrix to invert. Contains the inverse On exit. + /// The order of the square matrix . + /// 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. + /// This is equivalent to the GETRF and GETRI LAPACK routines. + public void LUInverse(float[] a, int order, float[] work) + { + LUInverse(a, order); + } + + /// + /// Computes the inverse of a previously factored matrix. + /// + /// The LU factored N by N matrix. Contains the inverse On exit. + /// The order of the square matrix . + /// The pivot indices of . + /// 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. + /// This is equivalent to the GETRI LAPACK routine. + public void LUInverseFactored(float[] a, int order, int[] ipiv, float[] work) + { + LUInverseFactored(a, order, ipiv); + } + + /// + /// Solves A*X=B for X using LU factorization. + /// + /// The number of columns of B. + /// The square matrix A. + /// The order of the square matrix . + /// The B matrix. + /// This is equivalent to the GETRF and GETRS LAPACK routines. + public void LUSolve(int columnsOfB, float[] a, int order, float[] 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"); + } + + var ipiv = new int[order]; + LUFactor(a, order, ipiv); + LUSolveFactored(columnsOfB, a, order, ipiv, b); + } + + /// + /// Solves A*X=B for X using a previously factored A matrix. + /// + /// The number of columns of B. + /// The factored A matrix. + /// The order of the square matrix . + /// The pivot indices of . + /// The B matrix. + /// This is equivalent to the GETRS LAPACK routine. + public void LUSolveFactored(int columnsOfB, float[] a, int order, int[] ipiv, float[] b) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (ipiv == null) + { + throw new ArgumentNullException("ipiv"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (a.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); + } + + if (ipiv.Length != order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); + } + + if (b.Length != order * columnsOfB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + // Compute the column vector P*B + for (var i = 0; i < ipiv.Length; i++) + { + if (ipiv[i] == i) + { + continue; + } + + var p = ipiv[i]; + for (var j = 0; j < columnsOfB; j++) + { + var indexk = j * order; + var indexkp = indexk + p; + var indexkj = indexk + i; + var temp = b[indexkp]; + b[indexkp] = b[indexkj]; + b[indexkj] = temp; + } + } + + // Solve L*Y = P*B + for (var k = 0; k < order; k++) + { + var korder = k * order; + for (var i = k + 1; i < order; i++) + { + for (var j = 0; j < columnsOfB; j++) + { + var index = j * order; + b[i + index] -= b[k + index] * a[i + korder]; + } + } + } + + // Solve U*X = Y; + for (var k = order - 1; k >= 0; k--) + { + var korder = k + (k * order); + for (var j = 0; j < columnsOfB; j++) + { + b[k + (j * order)] /= a[korder]; + } + + korder = k * order; + for (var i = 0; i < k; i++) + { + for (var j = 0; j < columnsOfB; j++) + { + var index = j * order; + b[i + index] -= b[k + index] * a[i + korder]; + } + } + } + } + + /// + /// Solves A*X=B for X using LU factorization. + /// + /// How to transpose the matrix. + /// The number of columns of B. + /// The square matrix A. + /// The order of the square matrix . + /// The B matrix. + /// This is equivalent to the GETRF and GETRS LAPACK routines. + public void LUSolve(Transpose transposeA, int columnsOfB, float[] a, int order, float[] 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"); + } + + var ipiv = new int[order]; + LUFactor(a, order, ipiv); + LUSolveFactored(transposeA, columnsOfB, a, order, ipiv, b); + } + + /// + /// Solves A*X=B for X using a previously factored A matrix. + /// + /// How to transpose the matrix. + /// The number of columns of B. + /// The factored A matrix. + /// The order of the square matrix . + /// The pivot indices of . + /// The B matrix. + /// This is equivalent to the GETRS LAPACK routine. + public void LUSolveFactored(Transpose transposeA, int columnsOfB, float[] a, int order, int[] ipiv, float[] b) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (ipiv == null) + { + throw new ArgumentNullException("ipiv"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (a.Length != order * order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); + } + + if (ipiv.Length != order) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); + } + + if (b.Length != order * columnsOfB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if ((transposeA == Transpose.Transpose) || (transposeA == Transpose.ConjugateTranspose)) + { + var aT = new float[a.Length]; + for (var i = 0; i < order; i++) + { + for (var j = 0; j < order; j++) + { + aT[(j * order) + i] = a[(i * order) + j]; + } + } + + LUSolveFactored(columnsOfB, aT, order, ipiv, b); + } + else + { + LUSolveFactored(columnsOfB, a, order, ipiv, b); + } + } + + /// + /// Computes the Cholesky factorization of A. + /// + /// On entry, a square, positive definite matrix. On exit, the matrix is overwritten with the + /// the Cholesky factorization. + /// The number of rows or columns in the matrix. + /// This is equivalent to the POTRF LAPACK routine. + public void CholeskyFactor(float[] a, int order) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + var tmpColumn = new float[order]; + + // Main loop - along the diagonal + for (var ij = 0; ij < order; ij++) + { + // "Pivot" element + var tmpVal = a[(ij * order) + ij]; + + if (tmpVal > 0.0) + { + tmpVal = (float)Math.Sqrt(tmpVal); + a[(ij * order) + ij] = tmpVal; + tmpColumn[ij] = tmpVal; + + // Calculate multipliers and copy to local column + // Current column, below the diagonal + for (var i = ij + 1; i < order; i++) + { + a[(ij * order) + i] /= tmpVal; + tmpColumn[i] = a[(ij * order) + i]; + } + + // Remaining columns, below the diagonal + DoCholeskyStep(a, order, ij + 1, order, tmpColumn, Control.NumberOfParallelWorkerThreads); + } + else + { + throw new ArgumentException(Resources.ArgumentMatrixPositiveDefinite); + } + + for (int i = ij + 1; i < order; i++) + { + a[(i * order) + ij] = 0.0f; + } + } + } + + /// + /// Calculate Cholesky step + /// + /// Factor matrix + /// Number of rows + /// Column start + /// Total columns + /// Multipliears calculated previously + /// Number of available processors + private static void DoCholeskyStep(float[] data, int rowDim, int firstCol, int colLimit, float[] multipliers, int availableCores) + { + var tmpColCount = colLimit - firstCol; + + if ((availableCores > 1) && (tmpColCount > 200)) + { + var tmpSplit = firstCol + (tmpColCount / 3); + var tmpCores = availableCores / 2; + + CommonParallel.Invoke( + () => DoCholeskyStep(data, rowDim, firstCol, tmpSplit, multipliers, tmpCores), + () => DoCholeskyStep(data, rowDim, tmpSplit, colLimit, multipliers, tmpCores)); + } + else + { + for (var j = firstCol; j < colLimit; j++) + { + var tmpVal = multipliers[j]; + for (var i = j; i < rowDim; i++) + { + data[(j * rowDim) + i] -= multipliers[i] * tmpVal; + } + } + } + } + + /// + /// Solves A*X=B for X using Cholesky factorization. + /// + /// The square, positive definite matrix A. + /// The number of rows and columns in A. + /// The B matrix. + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. + /// This is equivalent to the POTRF add POTRS LAPACK routines. + public void CholeskySolve(float[] a, int orderA, float[] b, int rowsB, int columnsB) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (orderA != rowsB) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions); + } + + if (ReferenceEquals(a, b)) + { + throw new ArgumentException(Resources.ArgumentReferenceDifferent); + } + + CholeskyFactor(a, orderA); + CholeskySolveFactored(a, orderA, b, rowsB, columnsB); + } + + /// + /// Solves A*X=B for X using a previously factored A matrix. + /// + /// The square, positive definite matrix A. + /// The number of rows and columns in A. + /// The B matrix. + /// The number of rows in the B matrix. + /// The number of columns in the B matrix. + /// This is equivalent to the POTRS LAPACK routine. + public void CholeskySolveFactored(float[] a, int orderA, float[] b, int rowsB, int columnsB) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (orderA != rowsB) + { + throw new ArgumentException(Resources.ArgumentMatrixDimensions); + } + + if (ReferenceEquals(a, b)) + { + throw new ArgumentException(Resources.ArgumentReferenceDifferent); + } + + CommonParallel.For( + 0, + columnsB, + c => + { + var cindex = c * orderA; + + // Solve L*Y = B; + float sum; + for (var i = 0; i < orderA; i++) + { + sum = b[cindex + i]; + for (var k = i - 1; k >= 0; k--) + { + sum -= a[(k * orderA) + i] * b[cindex + k]; + } + + b[cindex + i] = sum / a[(i * orderA) + i]; + } + + // Solve L'*X = Y; + for (var i = orderA - 1; i >= 0; i--) + { + sum = b[cindex + i]; + var iindex = i * orderA; + for (var k = i + 1; k < orderA; k++) + { + sum -= a[iindex + k] * b[cindex + k]; + } + + b[cindex + i] = sum / a[iindex + i]; + } + }); + } + + /// + /// Computes the QR factorization of A. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the R matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A M by M matrix that holds the Q matrix of the + /// QR factorization. + /// This is similar to the GEQRF and ORGQR LAPACK routines. + public void QRFactor(float[] r, int rowsR, int columnsR, float[] q) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + var work = new float[rowsR * rowsR]; + QRFactor(r, rowsR, columnsR, q, work); + } + + /// + /// Computes the QR factorization of A. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the R matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A M by M matrix that holds the Q matrix of the + /// QR factorization. + /// 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. + /// This is similar to the GEQRF and ORGQR LAPACK routines. + public void QRFactor(float[] r, int rowsR, int columnsR, float[] q, float[] work) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (work == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + if (work.Length < rowsR * rowsR) + { + work[0] = rowsR * rowsR; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + CommonParallel.For(0, rowsR, i => q[(i * rowsR) + i] = 1.0f); + + var minmn = Math.Min(rowsR, columnsR); + for (var i = 0; i < minmn; i++) + { + GenerateColumn(work, r, rowsR, i, i); + ComputeQR(work, i, r, i, rowsR, i + 1, columnsR, Control.NumberOfParallelWorkerThreads); + } + + for (var i = minmn - 1; i >= 0; i--) + { + ComputeQR(work, i, q, i, rowsR, i, rowsR, Control.NumberOfParallelWorkerThreads); + } + + work[0] = rowsR * rowsR; + } + + #region QR Factor Helper functions + + /// + /// Perform calculation of Q or R + /// + /// Work array + /// Index of colunn in work array + /// Q or R matrices + /// The first row in + /// The last row + /// The first column + /// The last column + /// Number of available CPUs + private static void ComputeQR(float[] work, int workIndex, float[] a, int rowStart, int rowCount, int columnStart, int columnCount, int availableCores) + { + if (rowStart > rowCount || columnStart > columnCount) + { + return; + } + + var tmpColCount = columnCount - columnStart; + + if ((availableCores > 1) && (tmpColCount > 200)) + { + var tmpSplit = columnStart + (tmpColCount / 2); + var tmpCores = availableCores / 2; + + CommonParallel.Invoke( + () => ComputeQR(work, workIndex, a, rowStart, rowCount, columnStart, tmpSplit, tmpCores), + () => ComputeQR(work, workIndex, a, rowStart, rowCount, tmpSplit, columnCount, tmpCores)); + } + else + { + for (var j = columnStart; j < columnCount; j++) + { + var scale = 0.0f; + for (var i = rowStart; i < rowCount; i++) + { + scale += work[(workIndex * rowCount) + i - rowStart] * a[(j * rowCount) + i]; + } + + for (var i = rowStart; i < rowCount; i++) + { + a[(j * rowCount) + i] -= work[(workIndex * rowCount) + i - rowStart] * scale; + } + } + } + } + + /// + /// Generate column from initial matrix to work array + /// + /// Work array + /// Initial matrix + /// The number of rows in matrix + /// The firts row + /// Column index + private static void GenerateColumn(float[] work, float[] a, int rowCount, int row, int column) + { + var tmp = column * rowCount; + var index = tmp + row; + + CommonParallel.For( + row, + rowCount, + i => + { + var iIndex = tmp + i; + work[iIndex - row] = a[iIndex]; + a[iIndex] = 0.0f; + }); + + var norm = 0.0; + for (var i = 0; i < rowCount - row; ++i) + { + var iindex = tmp + i; + norm += work[iindex] * work[iindex]; + } + + norm = Math.Sqrt(norm); + if (row == rowCount - 1 || norm == 0) + { + a[index] = -work[tmp]; + work[tmp] = (float)Math.Sqrt(2.0); + return; + } + + var scale = 1.0f / (float)norm; + if (work[tmp] < 0.0) + { + scale *= -1.0f; + } + + a[index] = -1.0f / scale; + CommonParallel.For(0, rowCount - row, i => work[tmp + i] *= scale); + work[tmp] += 1.0f; + + var s = (float)Math.Sqrt(1.0 / work[tmp]); + CommonParallel.For(0, rowCount - row, i => work[tmp + i] *= s); + } + + #endregion + + /// + /// Solves A*X=B for X using QR factorization of A. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the R matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A M by M matrix that holds the Q matrix of the + /// QR factorization. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + public void QRSolve(float[] r, int rowsR, int columnsR, float[] q, float[] b, int columnsB, float[] x) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (b == null) + { + throw new ArgumentNullException("q"); + } + + if (x == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + if (b.Length != rowsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + } + + var work = new float[rowsR * rowsR]; + QRSolve(r, rowsR, columnsR, q, b, columnsB, x, work); + } + + /// + /// Solves A*X=B for X using QR factorization of A. + /// + /// On entry, it is the M by N A matrix to factor. On exit, + /// it is overwritten with the R matrix of the QR factorization. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// On exit, A M by M matrix that holds the Q matrix of the + /// QR factorization. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + /// 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. + public void QRSolve(float[] r, int rowsR, int columnsR, float[] q, float[] b, int columnsB, float[] x, float[] work) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (b == null) + { + throw new ArgumentNullException("q"); + } + + if (x == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + if (b.Length != rowsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + } + + if (work.Length < rowsR * rowsR) + { + work[0] = rowsR * rowsR; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + QRFactor(r, rowsR, columnsR, q, work); + QRSolveFactored(q, r, rowsR, columnsR, b, columnsB, x); + + work[0] = rowsR * rowsR; + } + + /// + /// Solves A*X=B for X using a previously QR factored matrix. + /// + /// The Q matrix obtained by calling . + /// The R matrix obtained by calling . + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + public void QRSolveFactored(float[] q, float[] r, int rowsR, int columnsR, float[] b, int columnsB, float[] x) + { + if (r == null) + { + throw new ArgumentNullException("r"); + } + + if (q == null) + { + throw new ArgumentNullException("q"); + } + + if (b == null) + { + throw new ArgumentNullException("q"); + } + + if (x == null) + { + throw new ArgumentNullException("q"); + } + + if (r.Length != rowsR * columnsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); + } + + if (q.Length != rowsR * rowsR) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); + } + + if (b.Length != rowsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsR * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); + } + + var sol = new float[b.Length]; + + // Copy B matrix to "sol", so B data will not be changed + Buffer.BlockCopy(b, 0, sol, 0, b.Length * Constants.SizeOfFloat); + + // Compute Y = transpose(Q)*B + var column = new float[rowsR]; + for (var j = 0; j < columnsB; j++) + { + var jm = j * rowsR; + CommonParallel.For(0, rowsR, k => column[k] = sol[jm + k]); + CommonParallel.For( + 0, + rowsR, + i => + { + var im = i * rowsR; + sol[jm + i] = CommonParallel.Aggregate(0, rowsR, k => q[im + k] * column[k]); + }); + } + + // Solve R*X = Y; + for (var k = columnsR - 1; k >= 0; k--) + { + var km = k * rowsR; + for (var j = 0; j < columnsB; j++) + { + sol[(j * rowsR) + k] /= r[km + k]; + } + + for (var i = 0; i < k; i++) + { + for (var j = 0; j < columnsB; j++) + { + var jm = j * rowsR; + sol[jm + i] -= sol[jm + k] * r[km + i]; + } + } + } + + // Fill result matrix + CommonParallel.For( + 0, + columnsR, + row => + { + for (var col = 0; col < columnsB; col++) + { + x[(col * columnsR) + row] = sol[row + (col * rowsR)]; + } + }); + } + + /// + /// Computes the singular value decomposition of A. + /// + /// Compute the singular U and VT vectors or not. + /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The singular values of A in ascending value. + /// If is true, on exit U contains the left + /// singular vectors. + /// If is true, on exit VT contains the transposed + /// right singular vectors. + /// This is equivalent to the GESVD LAPACK routine. + public void SingularValueDecomposition(bool computeVectors, float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (s == null) + { + throw new ArgumentNullException("s"); + } + + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (vt == null) + { + throw new ArgumentNullException("vt"); + } + + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } + + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } + + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } + + // Actually "work = new float[aRows]" is acceptable size of work array. I set size proposed in method description + var work = new float[Math.Max((3 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA), 5 * Math.Min(rowsA, columnsA))]; + SingularValueDecomposition(computeVectors, a, rowsA, columnsA, s, u, vt, work); + } + + /// + /// Computes the singular value decomposition of A. + /// + /// Compute the singular U and VT vectors or not. + /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The singular values of A in ascending value. + /// If is true, on exit U contains the left + /// singular vectors. + /// If is true, on exit VT contains the transposed + /// right singular vectors. + /// The work array. For real matrices, the work array should be at least + /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). + /// On exit, work[0] contains the optimal work size value. + /// This is equivalent to the GESVD LAPACK routine. + public void SingularValueDecomposition(bool computeVectors, float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt, float[] work) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (s == null) + { + throw new ArgumentNullException("s"); + } + + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (vt == null) + { + throw new ArgumentNullException("vt"); + } + + if (work == null) + { + throw new ArgumentNullException("work"); + } + + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } + + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } + + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } + + if (work.Length == 0) + { + throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); + } + + if (work.Length < rowsA) + { + work[0] = rowsA; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + const int Maxiter = 1000; + + var e = new float[columnsA]; + var v = new float[vt.Length]; + var stemp = new float[Math.Min(rowsA + 1, columnsA)]; + + int i, j, l, lp1; + + var cs = 0.0f; + var sn = 0.0f; + float t; + + var ncu = rowsA; + + // Reduce matrix to bidiagonal form, storing the diagonal elements + // in "s" and the super-diagonal elements in "e". + var nct = Math.Min(rowsA - 1, columnsA); + var nrt = Math.Max(0, Math.Min(columnsA - 2, rowsA)); + 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 vector s[l]. + var l1 = l; + stemp[l] = (float)Math.Sqrt(CommonParallel.Aggregate(l, rowsA, i1 => (a[(l1 * rowsA) + i1] * a[(l1 * rowsA) + i1]))); + + if (stemp[l] != 0.0) + { + if (a[(l * rowsA) + l] != 0.0) + { + stemp[l] = Math.Abs(stemp[l]) * (a[(l * rowsA) + l] / Math.Abs(a[(l * rowsA) + l])); + } + + // A part of column "l" of Matrix A from row "l" to end multiply by 1.0 / s[l] + for (i = l; i < rowsA; i++) + { + a[(l * rowsA) + i] = a[(l * rowsA) + i] * (1.0f / stemp[l]); + } + + a[(l * rowsA) + l] = 1.0f + a[(l * rowsA) + l]; + } + + stemp[l] = -stemp[l]; + } + + for (j = lp1; j < columnsA; j++) + { + if (l < nct) + { + if (stemp[l] != 0.0) + { + // Apply the transformation. + t = 0.0f; + for (i = l; i < rowsA; i++) + { + t += a[(j * rowsA) + i] * a[(l * rowsA) + i]; + } + + t = -t / a[(l * rowsA) + l]; + + for (var ii = l; ii < rowsA; ii++) + { + a[(j * rowsA) + ii] += t * a[(l * rowsA) + ii]; + } + } + } + + // Place the l-th row of matrix into "e" for the + // subsequent calculation of the row transformation. + e[j] = a[(j * rowsA) + l]; + } + + if (computeVectors && l < nct) + { + // Place the transformation in "u" for subsequent back multiplication. + for (i = l; i < rowsA; i++) + { + u[(l * rowsA) + i] = a[(l * rowsA) + i]; + } + } + + if (l >= nrt) + { + continue; + } + + // Compute the l-th row transformation and place the l-th super-diagonal in e(l). + var enorm = 0.0; + for (i = lp1; i < e.Length; i++) + { + enorm += e[i] * e[i]; + } + + e[l] = (float)Math.Sqrt(enorm); + if (e[l] != 0.0) + { + if (e[lp1] != 0.0) + { + e[l] = Math.Abs(e[l]) * (e[lp1] / Math.Abs(e[lp1])); + } + + // Scale vector "e" from "lp1" by 1.0 / e[l] + for (i = lp1; i < e.Length; i++) + { + e[i] = e[i] * (1.0f / e[l]); + } + + e[lp1] = 1.0f + e[lp1]; + } + + e[l] = -e[l]; + + if (lp1 < rowsA && e[l] != 0.0) + { + // Apply the transformation. + for (i = lp1; i < rowsA; i++) + { + work[i] = 0.0f; + } + + for (j = lp1; j < columnsA; j++) + { + for (var ii = lp1; ii < rowsA; ii++) + { + work[ii] += e[j] * a[(j * rowsA) + ii]; + } + } + + for (j = lp1; j < columnsA; j++) + { + var ww = -e[j] / e[lp1]; + for (var ii = lp1; ii < rowsA; ii++) + { + a[(j * rowsA) + ii] += ww * work[ii]; + } + } + } + + if (!computeVectors) + { + continue; + } + + // Place the transformation in v for subsequent back multiplication. + for (i = lp1; i < columnsA; i++) + { + v[(l * columnsA) + i] = e[i]; + } + } + + // Set up the final bidiagonal matrix or order m. + var m = Math.Min(columnsA, rowsA + 1); + var nctp1 = nct + 1; + var nrtp1 = nrt + 1; + if (nct < columnsA) + { + stemp[nctp1 - 1] = a[((nctp1 - 1) * rowsA) + (nctp1 - 1)]; + } + + if (rowsA < m) + { + stemp[m - 1] = 0.0f; + } + + if (nrtp1 < m) + { + e[nrtp1 - 1] = a[((m - 1) * rowsA) + (nrtp1 - 1)]; + } + + e[m - 1] = 0.0f; + + // If required, generate "u". + if (computeVectors) + { + for (j = nctp1 - 1; j < ncu; j++) + { + for (i = 0; i < rowsA; i++) + { + u[(j * rowsA) + i] = 0.0f; + } + + u[(j * rowsA) + j] = 1.0f; + } + + for (l = nct - 1; l >= 0; l--) + { + if (stemp[l] != 0.0) + { + for (j = l + 1; j < ncu; j++) + { + t = 0.0f; + for (i = l; i < rowsA; i++) + { + t += u[(j * rowsA) + i] * u[(l * rowsA) + i]; + } + + t = -t / u[(l * rowsA) + l]; + + for (var ii = l; ii < rowsA; ii++) + { + u[(j * rowsA) + ii] += t * u[(l * rowsA) + ii]; + } + } + + // A part of column "l" of matrix A from row "l" to end multiply by -1.0 + for (i = l; i < rowsA; i++) + { + u[(l * rowsA) + i] = u[(l * rowsA) + i] * -1.0f; + } + + u[(l * rowsA) + l] = 1.0f + u[(l * rowsA) + l]; + for (i = 0; i < l; i++) + { + u[(l * rowsA) + i] = 0.0f; + } + } + else + { + for (i = 0; i < rowsA; i++) + { + u[(l * rowsA) + i] = 0.0f; + } + + u[(l * rowsA) + l] = 1.0f; + } + } + } + + // If it is required, generate v. + if (computeVectors) + { + for (l = columnsA - 1; l >= 0; l--) + { + lp1 = l + 1; + if (l < nrt) + { + if (e[l] != 0.0) + { + for (j = lp1; j < columnsA; j++) + { + t = 0.0f; + for (i = lp1; i < columnsA; i++) + { + t += v[(j * columnsA) + i] * v[(l * columnsA) + i]; + } + + t = -t / v[(l * columnsA) + lp1]; + for (var ii = l; ii < columnsA; ii++) + { + v[(j * columnsA) + ii] += t * v[(l * columnsA) + ii]; + } + } + } + } + + for (i = 0; i < columnsA; i++) + { + v[(l * columnsA) + i] = 0.0f; + } + + v[(l * columnsA) + l] = 1.0f; + } + } + + // Transform "s" and "e" so that they are double + for (i = 0; i < m; i++) + { + float r; + if (stemp[i] != 0.0) + { + t = stemp[i]; + r = stemp[i] / t; + stemp[i] = t; + if (i < m - 1) + { + e[i] = e[i] / r; + } + + if (computeVectors) + { + // A part of column "i" of matrix U from row 0 to end multiply by r + for (j = 0; j < rowsA; j++) + { + u[(i * rowsA) + j] = u[(i * rowsA) + j] * r; + } + } + } + + // Exit + if (i == m - 1) + { + break; + } + + if (e[i] == 0.0) + { + continue; + } + + t = e[i]; + r = t / e[i]; + e[i] = t; + stemp[i + 1] = stemp[i + 1] * r; + if (!computeVectors) + { + continue; + } + + // A part of column "i+1" of matrix VT from row 0 to end multiply by r + for (j = 0; j < columnsA; j++) + { + v[((i + 1) * columnsA) + j] = v[((i + 1) * columnsA) + j] * 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. + 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 mS[m] and e[l-1] are negligible and l < m + // kase = 2: if mS[l] is negligible and l < m + // kase = 3: if e[l-1] is negligible, l < m, and mS[l, ..., mS[m] are not negligible (qr step). + // kase = 4: if e[m-1] is negligible (convergence). + double ztest; + double test; + for (l = m - 2; l >= 0; l--) + { + test = Math.Abs(stemp[l]) + Math.Abs(stemp[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.0; + 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(stemp[ls]); + if (ztest.AlmostEqualInDecimalPlaces(test, 7)) + { + stemp[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 s[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 = stemp[k]; + + Drotg(ref t1, ref f, ref cs, ref sn); + stemp[k] = t1; + if (k != l) + { + f = -sn * e[k - 1]; + e[k - 1] = cs * e[k - 1]; + } + + if (computeVectors) + { + // Rotate + for (i = 0; i < columnsA; i++) + { + var z = (cs * v[(k * columnsA) + i]) + (sn * v[((m - 1) * columnsA) + i]); + v[((m - 1) * columnsA) + i] = (cs * v[((m - 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); + v[(k * columnsA) + i] = z; + } + } + } + + break; + + // Split at negligible s[l]. + case 2: + f = e[l - 1]; + e[l - 1] = 0.0f; + for (k = l; k < m; k++) + { + t1 = stemp[k]; + Drotg(ref t1, ref f, ref cs, ref sn); + stemp[k] = t1; + f = -sn * e[k]; + e[k] = cs * e[k]; + if (computeVectors) + { + // Rotate + for (i = 0; i < rowsA; i++) + { + var z = (cs * u[(k * rowsA) + i]) + (sn * u[((l - 1) * rowsA) + i]); + u[((l - 1) * rowsA) + i] = (cs * u[((l - 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); + u[(k * rowsA) + i] = z; + } + } + } + + break; + + // Perform one qr step. + case 3: + + // calculate the shift. + var scale = 0.0f; + scale = Math.Max(scale, Math.Abs(stemp[m - 1])); + scale = Math.Max(scale, Math.Abs(stemp[m - 2])); + scale = Math.Max(scale, Math.Abs(e[m - 2])); + scale = Math.Max(scale, Math.Abs(stemp[l])); + scale = Math.Max(scale, Math.Abs(e[l])); + var sm = stemp[m - 1] / scale; + var smm1 = stemp[m - 2] / scale; + var emm1 = e[m - 2] / scale; + var sl = stemp[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 * stemp[k]) + (sn * e[k]); + e[k] = (cs * e[k]) - (sn * stemp[k]); + g = sn * stemp[k + 1]; + stemp[k + 1] = cs * stemp[k + 1]; + if (computeVectors) + { + for (i = 0; i < columnsA; i++) + { + var z = (cs * v[(k * columnsA) + i]) + (sn * v[((k + 1) * columnsA) + i]); + v[((k + 1) * columnsA) + i] = (cs * v[((k + 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); + v[(k * columnsA) + i] = z; + } + } + + Drotg(ref f, ref g, ref cs, ref sn); + stemp[k] = f; + f = (cs * e[k]) + (sn * stemp[k + 1]); + stemp[k + 1] = -(sn * e[k]) + (cs * stemp[k + 1]); + g = sn * e[k + 1]; + e[k + 1] = cs * e[k + 1]; + if (computeVectors && k < rowsA) + { + for (i = 0; i < rowsA; i++) + { + var z = (cs * u[(k * rowsA) + i]) + (sn * u[((k + 1) * rowsA) + i]); + u[((k + 1) * rowsA) + i] = (cs * u[((k + 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); + u[(k * rowsA) + i] = z; + } + } + } + + e[m - 2] = f; + iter = iter + 1; + break; + + // Convergence + case 4: + + // Make the singular value positive + if (stemp[l] < 0.0) + { + stemp[l] = -stemp[l]; + if (computeVectors) + { + // A part of column "l" of matrix VT from row 0 to end multiply by -1 + for (i = 0; i < columnsA; i++) + { + v[(l * columnsA) + i] = v[(l * columnsA) + i] * -1.0f; + } + } + } + + // Order the singular value. + while (l != mn - 1) + { + if (stemp[l] >= stemp[l + 1]) + { + break; + } + + t = stemp[l]; + stemp[l] = stemp[l + 1]; + stemp[l + 1] = t; + if (computeVectors && l < columnsA) + { + // Swap columns l, l + 1 + for (i = 0; i < columnsA; i++) + { + var z = v[(l * columnsA) + i]; + v[(l * columnsA) + i] = v[((l + 1) * columnsA) + i]; + v[((l + 1) * columnsA) + i] = z; + } + } + + if (computeVectors && l < rowsA) + { + // Swap columns l, l + 1 + for (i = 0; i < rowsA; i++) + { + var z = u[(l * rowsA) + i]; + u[(l * rowsA) + i] = u[((l + 1) * rowsA) + i]; + u[((l + 1) * rowsA) + i] = z; + } + } + + l = l + 1; + } + + iter = 0; + m = m - 1; + break; + } + } + + if (computeVectors) + { + // Finally transpose "v" to get "vt" matrix + for (i = 0; i < columnsA; i++) + { + for (j = 0; j < columnsA; j++) + { + vt[(j * columnsA) + i] = v[(i * columnsA) + j]; + } + } + } + + // Copy stemp to s with size adjustment. We are using ported copy of linpack's svd code and it uses + // a singular vector of length rows+1 when rows < columns. The last element is not used and needs to be removed. + // We should port lapack's svd routine to remove this problem. + Buffer.BlockCopy(stemp, 0, s, 0, Math.Min(rowsA, columnsA) * Constants.SizeOfFloat); + + // On return the first element of the work array stores the min size of the work array could have been + // work[0] = Math.Max(3 * Math.Min(aRows, aColumns) + Math.Max(aRows, aColumns), 5 * Math.Min(aRows, aColumns)); + work[0] = rowsA; + } + + /// + /// Given the Cartesian coordinates (da, db) of a point p, these fucntion return the parameters da, db, c, and s + /// associated with the Givens rotation that zeros the y-coordinate of the point. + /// + /// Provides the x-coordinate of the point p. On exit contains the parameter r associated with the Givens rotation + /// Provides the y-coordinate of the point p. On exit contains the parameter z associated with the Givens rotation + /// Contains the parameter c associated with the Givens rotation + /// Contains the parameter s associated with the Givens rotation + /// This is equivalent to the DROTG LAPACK routine. + private static void Drotg(ref float da, ref float db, ref float c, ref float s) + { + float r, z; + + var roe = db; + var absda = Math.Abs(da); + var absdb = Math.Abs(db); + if (absda > absdb) + { + roe = da; + } + + var scale = absda + absdb; + if (scale == 0.0) + { + c = 1.0f; + s = 0.0f; + r = 0.0f; + z = 0.0f; + } + else + { + var sda = da / scale; + var sdb = db / scale; + r = scale * (float)Math.Sqrt((sda * sda) + (sdb * sdb)); + if (roe < 0.0) + { + r = -r; + } + + c = da / r; + s = db / r; + z = 1.0f; + if (absda > absdb) + { + z = s; + } + + if (absdb >= absda && c != 0.0) + { + z = 1.0f / c; + } + } + + da = r; + db = z; + } + + /// + /// Solves A*X=B for X using the singular value decomposition of A. + /// + /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The singular values of A in ascending value. + /// On exit U contains the left singular vectors. + /// On exit VT contains the transposed right singular vectors. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + public void SvdSolve(float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt, float[] b, int columnsB, float[] x) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (s == null) + { + throw new ArgumentNullException("s"); + } + + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (vt == null) + { + throw new ArgumentNullException("vt"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } + + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } + + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } + + if (b.Length != rowsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + // Actually "work = new float[aRows]" is acceptable size of work array. I set size proposed in method description + var work = new float[Math.Max((3 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA), 5 * Math.Min(rowsA, columnsA))]; + SvdSolve(a, rowsA, columnsA, s, u, vt, b, columnsB, x, work); + } + + /// + /// Solves A*X=B for X using the singular value decomposition of A. + /// + /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The singular values of A in ascending value. + /// On exit U contains the left singular vectors. + /// On exit VT contains the transposed right singular vectors. + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + /// The work array. For real matrices, the work array should be at least + /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). + /// On exit, work[0] contains the optimal work size value. + public void SvdSolve(float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt, float[] b, int columnsB, float[] x, float[] work) + { + if (a == null) + { + throw new ArgumentNullException("a"); + } + + if (s == null) + { + throw new ArgumentNullException("s"); + } + + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (vt == null) + { + throw new ArgumentNullException("vt"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } + + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } + + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } + + if (b.Length != rowsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (work.Length == 0) + { + throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); + } + + if (work.Length < rowsA) + { + work[0] = rowsA; + throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); + } + + SingularValueDecomposition(true, a, rowsA, columnsA, s, u, vt, work); + SvdSolveFactored(rowsA, columnsA, s, u, vt, b, columnsB, x); + } + + /// + /// Solves A*X=B for X using a previously SVD decomposed matrix. + /// + /// The number of rows in the A matrix. + /// The number of columns in the A matrix. + /// The s values returned by . + /// The left singular vectors returned by . + /// The right singular vectors returned by . + /// The B matrix. + /// The number of columns of B. + /// On exit, the solution matrix. + public void SvdSolveFactored(int rowsA, int columnsA, float[] s, float[] u, float[] vt, float[] b, int columnsB, float[] x) + { + if (s == null) + { + throw new ArgumentNullException("s"); + } + + if (u == null) + { + throw new ArgumentNullException("u"); + } + + if (vt == null) + { + throw new ArgumentNullException("vt"); + } + + if (b == null) + { + throw new ArgumentNullException("b"); + } + + if (x == null) + { + throw new ArgumentNullException("x"); + } + + if (u.Length != rowsA * rowsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); + } + + if (vt.Length != columnsA * columnsA) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); + } + + if (s.Length != Math.Min(rowsA, columnsA)) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); + } + + if (b.Length != rowsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + if (x.Length != columnsA * columnsB) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); + } + + var mn = Math.Min(rowsA, columnsA); + var tmp = new float[columnsA]; + + for (var k = 0; k < columnsB; k++) + { + for (var j = 0; j < columnsA; j++) + { + float value = 0; + if (j < mn) + { + for (var i = 0; i < rowsA; i++) + { + value += u[(j * rowsA) + i] * b[(k * rowsA) + i]; + } + + value /= s[j]; + } + + tmp[j] = value; + } + + for (var j = 0; j < columnsA; j++) + { + float value = 0; + for (var i = 0; i < columnsA; i++) + { + value += vt[(j * columnsA) + i] * tmp[i]; + } + + x[(k * columnsA) + j] = value; + } + } + } + } +} diff --git a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.cs b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.cs deleted file mode 100644 index 0776a7e5..00000000 --- a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebraProvider.cs +++ /dev/null @@ -1,11259 +0,0 @@ -// -// Math.NET Numerics, part of the Math.NET Project -// http://numerics.mathdotnet.com -// http://github.com/mathnet/mathnet-numerics -// http://mathnetnumerics.codeplex.com -// Copyright (c) 2009-2010 Math.NET -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// -namespace MathNet.Numerics.Algorithms.LinearAlgebra -{ - using System; - using System.Numerics; - using Properties; - using Threading; - - /// - /// The managed linear algebra provider. - /// - public class ManagedLinearAlgebraProvider : ILinearAlgebraProvider - { - #region ILinearAlgebraProvider Members - - /// - /// Adds a scaled vector to another: y += alpha*x. - /// - /// The vector to update. - /// The value to scale by. - /// The vector to add to . - /// This equivalent to the AXPY BLAS routine. - public void AddVectorToScaledVector(double[] y, double alpha, double[] x) - { - 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 (alpha == 0.0) - { - return; - } - - if (alpha == 1.0) - { - CommonParallel.For(0, y.Length, index => { y[index] += x[index]; }); - } - else - { - CommonParallel.For(0, y.Length, index => { y[index] += alpha * x[index]; }); - } - } - - /// - /// Scales an array. Can be used to scale a vector and a matrix. - /// - /// The scalar. - /// The values to scale. - /// This is equivalent to the SCAL BLAS routine. - public void ScaleArray(double alpha, double[] x) - { - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (alpha == 1.0) - { - return; - } - - CommonParallel.For(0, x.Length, index => { x[index] = alpha * x[index]; }); - } - - /// - /// Computes the dot product of x and y. - /// - /// The vector x. - /// The vector y. - /// The dot product of x and y. - /// This is equivalent to the DOT BLAS routine. - public double DotProduct(double[] x, double[] y) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (y.Length != x.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } - - return CommonParallel.Aggregate(0, y.Length, index => y[index] * x[index]); - } - - /// - /// Does a point wise add of two arrays z = x + y. This can be used - /// to add vectors or matrices. - /// - /// The array x. - /// The array y. - /// The result of the addition. - /// There is no equivalent BLAS routine, but many libraries - /// provide optimized (parallel and/or vectorized) versions of this - /// routine. - public void AddArrays(double[] x, double[] y, double[] result) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (y.Length != x.Length || y.Length != result.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } - - CommonParallel.For(0, y.Length, index => { result[index] = x[index] + y[index]; }); - } - - /// - /// Does a point wise subtraction of two arrays z = x - y. This can be used - /// to subtract vectors or matrices. - /// - /// The array x. - /// The array y. - /// The result of the subtraction. - /// There is no equivalent BLAS routine, but many libraries - /// provide optimized (parallel and/or vectorized) versions of this - /// routine. - public void SubtractArrays(double[] x, double[] y, double[] result) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (y.Length != x.Length || y.Length != result.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } - - CommonParallel.For(0, y.Length, index => { result[index] = x[index] - y[index]; }); - } - - /// - /// Does a point wise multiplication of two arrays z = x * y. This can be used - /// to multiple elements of vectors or matrices. - /// - /// The array x. - /// The array y. - /// The result of the point wise multiplication. - /// There is no equivalent BLAS routine, but many libraries - /// provide optimized (parallel and/or vectorized) versions of this - /// routine. - public void PointWiseMultiplyArrays(double[] x, double[] y, double[] result) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (y.Length != x.Length || y.Length != result.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } - - CommonParallel.For(0, y.Length, index => { result[index] = x[index] * y[index]; }); - } - - /// - /// Does a point wise division of two arrays z = x / y. This can be used - /// to divide elements of vectors or matrices. - /// - /// The array x. - /// The array y. - /// The result of the point wise division. - /// There is no equivalent BLAS routine, but many libraries - /// provide optimized (parallel and/or vectorized) versions of this - /// routine. - public void PointWiseDivideArrays(double[] x, double[] y, double[] result) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (y.Length != x.Length || y.Length != result.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } - - CommonParallel.For(0, y.Length, index => { result[index] = x[index] / y[index]; }); - } - - /// - /// Computes the requested of the matrix. - /// - /// The type of norm to compute. - /// The number of rows. - /// The number of columns. - /// The matrix to compute the norm from. - /// - /// The requested of the matrix. - /// - public double MatrixNorm(Norm norm, int rows, int columns, double[] matrix) - { - var ret = 0.0; - switch (norm) - { - case Norm.OneNorm: - break; - case Norm.LargestAbsoluteValue: - break; - case Norm.InfinityNorm: - break; - case Norm.FrobeniusNorm: - break; - } - - throw new NotImplementedException(); - } - - /// - /// Computes the requested of the matrix. - /// - /// The type of norm to compute. - /// The number of rows. - /// The number of columns. - /// The matrix to compute the norm from. - /// The work array. Not used in the managed provider. - /// - /// The requested of the matrix. - /// - public double MatrixNorm(Norm norm, int rows, int columns, double[] matrix, double[] work) - { - return MatrixNorm(norm, rows, columns, matrix); - } - - /// - /// Multiples two matrices. result = x * y - /// - /// The x matrix. - /// The number of rows in the x matrix. - /// The number of columns in the x matrix. - /// The y matrix. - /// The number of rows in the y matrix. - /// The number of columns in the y matrix. - /// Where to store the result of the multiplication. - /// This is a simplified version of the BLAS GEMM routine with alpha - /// set to 1.0 and beta set to 0.0, and x and y are not transposed. - public void MatrixMultiply(double[] x, int rowsX, int columnsX, double[] y, int rowsY, int columnsY, double[] result) - { - // First check some basic requirement on the parameters of the matrix multiplication. - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (rowsX * columnsX != x.Length) - { - throw new ArgumentException("x.Length != xRows * xColumns"); - } - - if (rowsY * columnsY != y.Length) - { - throw new ArgumentException("y.Length != yRows * yColumns"); - } - - if (columnsX != rowsY) - { - throw new ArgumentException("xColumns != yRows"); - } - - if (rowsX * columnsY != result.Length) - { - throw new ArgumentException("xRows * yColumns != result.Length"); - } - - // Check whether we will be overwriting any of our inputs and make copies if necessary. - // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory - // as result, we can do it on a row wise basis. We should investigate this. - double[] xdata; - if (ReferenceEquals(x, result)) - { - xdata = (double[])x.Clone(); - } - else - { - xdata = x; - } - - double[] ydata; - if (ReferenceEquals(y, result)) - { - ydata = (double[])y.Clone(); - } - else - { - ydata = y; - } - - // Start the actual matrix multiplication. - // TODO - For small matrices we should get rid of the parallelism because of startup costs. - // Perhaps the following implementations would be a good one - // http://blog.feradz.com/2009/01/cache-efficient-matrix-multiplication/ - MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 1.0, xdata, rowsX, columnsX, ydata, rowsY, columnsY, 0.0, result); - } - - /// - /// Multiplies two matrices and updates another with the result. c = alpha*op(a)*op(b) + beta*c - /// - /// How to transpose the matrix. - /// How to transpose the matrix. - /// The value to scale matrix. - /// The a matrix. - /// The number of rows in the matrix. - /// The number of columns in the matrix. - /// The b matrix - /// The number of rows in the matrix. - /// The number of columns in the matrix. - /// The value to scale the matrix. - /// The c matrix. - public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, double alpha, double[] a, int rowsA, int columnsA, double[] b, int rowsB, int columnsB, double beta, double[] c) - { - // Choose nonsensical values for the number of rows in c; fill them in depending - // on the operations on a and b. - int rowsC; - - // First check some basic requirement on the parameters of the matrix multiplication. - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if ((int)transposeA > 111 && (int)transposeB > 111) - { - if (rowsA != columnsB) - { - throw new ArgumentOutOfRangeException(); - } - - if (columnsA * rowsB != c.Length) - { - throw new ArgumentOutOfRangeException(); - } - - rowsC = columnsA; - } - else if ((int)transposeA > 111) - { - if (rowsA != rowsB) - { - throw new ArgumentOutOfRangeException(); - } - - if (columnsA * columnsB != c.Length) - { - throw new ArgumentOutOfRangeException(); - } - - rowsC = columnsA; - } - else if ((int)transposeB > 111) - { - if (columnsA != columnsB) - { - throw new ArgumentOutOfRangeException(); - } - - if (rowsA * rowsB != c.Length) - { - throw new ArgumentOutOfRangeException(); - } - - rowsC = rowsA; - } - else - { - if (columnsA != rowsB) - { - throw new ArgumentOutOfRangeException(); - } - - if (rowsA * columnsB != c.Length) - { - throw new ArgumentOutOfRangeException(); - } - - rowsC = rowsA; - } - - if (alpha == 0.0 && beta == 0.0) - { - Array.Clear(c, 0, c.Length); - return; - } - - // Check whether we will be overwriting any of our inputs and make copies if necessary. - // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory - // as result, we can do it on a row wise basis. We should investigate this. - double[] adata; - if (ReferenceEquals(a, c)) - { - adata = (double[])a.Clone(); - } - else - { - adata = a; - } - - double[] bdata; - if (ReferenceEquals(b, c)) - { - bdata = (double[])b.Clone(); - } - else - { - bdata = b; - } - - if (alpha == 1.0) - { - if (beta == 0.0) - { - if ((int)transposeA > 111 && (int)transposeB > 111) - { - CommonParallel.For( - 0, - columnsA, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsB; i++) - { - var iIndex = i * rowsA; - double s = 0; - for (var l = 0; l != columnsB; l++) - { - s += adata[iIndex + l] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = s; - } - }); - } - else if ((int)transposeA > 111) - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != columnsA; i++) - { - var iIndex = i * rowsA; - double s = 0; - for (var l = 0; l != rowsA; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s; - } - }); - } - else if ((int)transposeB > 111) - { - CommonParallel.For( - 0, - rowsB, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsA; i++) - { - double s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = s; - } - }); - } - else - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != rowsA; i++) - { - double s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s; - } - }); - } - } - else - { - if ((int)transposeA > 111 && (int)transposeB > 111) - { - CommonParallel.For( - 0, - columnsA, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsB; i++) - { - var iIndex = i * rowsA; - double s = 0; - for (var l = 0; l != columnsB; l++) - { - s += adata[iIndex + l] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = (c[jIndex + i] * beta) + s; - } - }); - } - else if ((int)transposeA > 111) - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != columnsA; i++) - { - var iIndex = i * rowsA; - double s = 0; - for (var l = 0; l != rowsA; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s + (c[jcIndex + i] * beta); - } - }); - } - else if ((int)transposeB > 111) - { - CommonParallel.For( - 0, - rowsB, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsA; i++) - { - double s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = s + (c[jIndex + i] * beta); - } - }); - } - else - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != rowsA; i++) - { - double s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s + (c[jcIndex + i] * beta); - } - }); - } - } - } - else - { - if ((int)transposeA > 111 && (int)transposeB > 111) - { - CommonParallel.For( - 0, - columnsA, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsB; i++) - { - var iIndex = i * rowsA; - double s = 0; - for (var l = 0; l != columnsB; l++) - { - s += adata[iIndex + l] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = (c[jIndex + i] * beta) + (alpha * s); - } - }); - } - else if ((int)transposeA > 111) - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != columnsA; i++) - { - var iIndex = i * rowsA; - double s = 0; - for (var l = 0; l != rowsA; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); - } - }); - } - else if ((int)transposeB > 111) - { - CommonParallel.For( - 0, - rowsB, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsA; i++) - { - double s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = (alpha * s) + (c[jIndex + i] * beta); - } - }); - } - else - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != rowsA; i++) - { - double s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); - } - }); - } - } - } - - /// - /// Computes the LUP factorization of A. P*A = L*U. - /// - /// An by matrix. The matrix is overwritten with the - /// the LU factorization on exit. The lower triangular factor L is stored in under the diagonal of (the diagonal is always 1.0 - /// for the L factor). The upper triangular factor U is stored on and above the diagonal of . - /// The order of the square matrix . - /// On exit, it contains the pivot indices. The size of the array must be . - /// This is equivalent to the GETRF LAPACK routine. - public void LUFactor(double[] data, int order, int[] ipiv) - { - if (data == null) - { - throw new ArgumentNullException("data"); - } - - if (ipiv == null) - { - throw new ArgumentNullException("ipiv"); - } - - if (data.Length != order * order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "data"); - } - - if (ipiv.Length != order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); - } - - // Initialize the pivot matrix to the identity permutation. - for (var i = 0; i < order; i++) - { - ipiv[i] = i; - } - - var vecLUcolj = new double[order]; - - // Outer loop. - for (var j = 0; j < order; j++) - { - var indexj = j * order; - var indexjj = indexj + j; - - // Make a copy of the j-th column to localize references. - for (var i = 0; i < order; i++) - { - vecLUcolj[i] = data[indexj + i]; - } - - // Apply previous transformations. - for (var i = 0; i < order; i++) - { - // Most of the time is spent in the following dot product. - var kmax = Math.Min(i, j); - var s = 0.0; - for (var k = 0; k < kmax; k++) - { - s += data[(k * order) + i] * vecLUcolj[k]; - } - - data[indexj + i] = vecLUcolj[i] -= s; - } - - // Find pivot and exchange if necessary. - var p = j; - for (var i = j + 1; i < order; i++) - { - if (Math.Abs(vecLUcolj[i]) > Math.Abs(vecLUcolj[p])) - { - p = i; - } - } - - if (p != j) - { - for (var k = 0; k < order; k++) - { - var indexk = k * order; - var indexkp = indexk + p; - var indexkj = indexk + j; - var temp = data[indexkp]; - data[indexkp] = data[indexkj]; - data[indexkj] = temp; - } - - ipiv[j] = p; - } - - // Compute multipliers. - if (j < order & data[indexjj] != 0.0) - { - for (var i = j + 1; i < order; i++) - { - data[indexj + i] /= data[indexjj]; - } - } - } - } - - /// - /// Computes the inverse of matrix using LU factorization. - /// - /// The N by N matrix to invert. Contains the inverse On exit. - /// The order of the square matrix . - /// This is equivalent to the GETRF and GETRI LAPACK routines. - public void LUInverse(double[] a, int order) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (a.Length != order * order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); - } - - var ipiv = new int[order]; - LUFactor(a, order, ipiv); - LUInverseFactored(a, order, ipiv); - } - - /// - /// Computes the inverse of a previously factored matrix. - /// - /// The LU factored N by N matrix. Contains the inverse On exit. - /// The order of the square matrix . - /// The pivot indices of . - /// This is equivalent to the GETRI LAPACK routine. - public void LUInverseFactored(double[] 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 double[a.Length]; - for (var i = 0; i < order; i++) - { - inverse[i + (order * i)] = 1.0; - } - - LUSolveFactored(order, a, order, ipiv, inverse); - Buffer.BlockCopy(inverse, 0, a, 0, a.Length * Constants.SizeOfDouble); - } - - /// - /// Computes the inverse of matrix using LU factorization. - /// - /// The N by N matrix to invert. Contains the inverse On exit. - /// The order of the square matrix . - /// 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. - /// This is equivalent to the GETRF and GETRI LAPACK routines. - public void LUInverse(double[] a, int order, double[] work) - { - LUInverse(a, order); - } - - /// - /// Computes the inverse of a previously factored matrix. - /// - /// The LU factored N by N matrix. Contains the inverse On exit. - /// The order of the square matrix . - /// The pivot indices of . - /// 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. - /// This is equivalent to the GETRI LAPACK routine. - public void LUInverseFactored(double[] a, int order, int[] ipiv, double[] work) - { - LUInverseFactored(a, order, ipiv); - } - - /// - /// Solves A*X=B for X using LU factorization. - /// - /// The number of columns of B. - /// The square matrix A. - /// The order of the square matrix . - /// The B matrix. - /// This is equivalent to the GETRF and GETRS LAPACK routines. - public void LUSolve(int columnsOfB, double[] a, int order, double[] 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"); - } - - var ipiv = new int[order]; - LUFactor(a, order, ipiv); - LUSolveFactored(columnsOfB, a, order, ipiv, b); - } - - /// - /// Solves A*X=B for X using a previously factored A matrix. - /// - /// The number of columns of B. - /// The factored A matrix. - /// The order of the square matrix . - /// The pivot indices of . - /// The B matrix. - /// This is equivalent to the GETRS LAPACK routine. - public void LUSolveFactored(int columnsOfB, double[] a, int order, int[] ipiv, double[] b) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (ipiv == null) - { - throw new ArgumentNullException("ipiv"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (a.Length != order * order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); - } - - if (ipiv.Length != order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); - } - - if (b.Length != order * columnsOfB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - // Compute the column vector P*B - for (var i = 0; i < ipiv.Length; i++) - { - if (ipiv[i] == i) - { - continue; - } - - var p = ipiv[i]; - for (var j = 0; j < columnsOfB; j++) - { - var indexk = j * order; - var indexkp = indexk + p; - var indexkj = indexk + i; - var temp = b[indexkp]; - b[indexkp] = b[indexkj]; - b[indexkj] = temp; - } - } - - // Solve L*Y = P*B - for (var k = 0; k < order; k++) - { - var korder = k * order; - for (var i = k + 1; i < order; i++) - { - for (var j = 0; j < columnsOfB; j++) - { - var index = j * order; - b[i + index] -= b[k + index] * a[i + korder]; - } - } - } - - // Solve U*X = Y; - for (var k = order - 1; k >= 0; k--) - { - var korder = k + (k * order); - for (var j = 0; j < columnsOfB; j++) - { - b[k + (j * order)] /= a[korder]; - } - - korder = k * order; - for (var i = 0; i < k; i++) - { - for (var j = 0; j < columnsOfB; j++) - { - var index = j * order; - b[i + index] -= b[k + index] * a[i + korder]; - } - } - } - } - - /// - /// Solves A*X=B for X using LU factorization. - /// - /// How to transpose the matrix. - /// The number of columns of B. - /// The square matrix A. - /// The order of the square matrix . - /// The B matrix. - /// This is equivalent to the GETRF and GETRS LAPACK routines. - public void LUSolve(Transpose transposeA, int columnsOfB, double[] a, int order, double[] 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"); - } - - var ipiv = new int[order]; - LUFactor(a, order, ipiv); - LUSolveFactored(transposeA, columnsOfB, a, order, ipiv, b); - } - - /// - /// Solves A*X=B for X using a previously factored A matrix. - /// - /// How to transpose the matrix. - /// The number of columns of B. - /// The factored A matrix. - /// The order of the square matrix . - /// The pivot indices of . - /// The B matrix. - /// This is equivalent to the GETRS LAPACK routine. - public void LUSolveFactored(Transpose transposeA, int columnsOfB, double[] a, int order, int[] ipiv, double[] b) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (ipiv == null) - { - throw new ArgumentNullException("ipiv"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (a.Length != order * order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); - } - - if (ipiv.Length != order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); - } - - if (b.Length != order * columnsOfB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if ((transposeA == Transpose.Transpose) || (transposeA == Transpose.ConjugateTranspose)) - { - var aT = new double[a.Length]; - for (var i = 0; i < order; i++) - { - for (var j = 0; j < order; j++) - { - aT[(j * order) + i] = a[(i * order) + j]; - } - } - - LUSolveFactored(columnsOfB, aT, order, ipiv, b); - } - else - { - LUSolveFactored(columnsOfB, a, order, ipiv, b); - } - } - - /// - /// Computes the Cholesky factorization of A. - /// - /// On entry, a square, positive definite matrix. On exit, the matrix is overwritten with the - /// the Cholesky factorization. - /// The number of rows or columns in the matrix. - /// This is equivalent to the POTRF LAPACK routine. - public void CholeskyFactor(double[] a, int order) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - var tmpColumn = new double[order]; - - // Main loop - along the diagonal - for (int ij = 0; ij < order; ij++) - { - // "Pivot" element - double tmpVal = a[(ij * order) + ij]; - - if (tmpVal > 0.0) - { - tmpVal = Math.Sqrt(tmpVal); - a[(ij * order) + ij] = tmpVal; - tmpColumn[ij] = tmpVal; - - // Calculate multipliers and copy to local column - // Current column, below the diagonal - for (int i = ij + 1; i < order; i++) - { - a[(ij * order) + i] /= tmpVal; - tmpColumn[i] = a[(ij * order) + i]; - } - - // Remaining columns, below the diagonal - DoCholeskyStep(a, order, ij + 1, order, tmpColumn, Control.NumberOfParallelWorkerThreads); - } - else - { - throw new ArgumentException(Resources.ArgumentMatrixPositiveDefinite); - } - - for (int i = ij + 1; i < order; i++) - { - a[(i * order) + ij] = 0.0; - } - } - } - - /// - /// Calculate Cholesky step - /// - /// Factor matrix - /// Number of rows - /// Column start - /// Total columns - /// Multipliears calculated previously - /// Number of available processors - private static void DoCholeskyStep(double[] data, int rowDim, int firstCol, int colLimit, double[] multipliers, int availableCores) - { - var tmpColCount = colLimit - firstCol; - - if ((availableCores > 1) && (tmpColCount > 200)) - { - var tmpSplit = firstCol + (tmpColCount / 3); - var tmpCores = availableCores / 2; - - CommonParallel.Invoke( - () => DoCholeskyStep(data, rowDim, firstCol, tmpSplit, multipliers, tmpCores), - () => DoCholeskyStep(data, rowDim, tmpSplit, colLimit, multipliers, tmpCores)); - } - else - { - for (var j = firstCol; j < colLimit; j++) - { - var tmpVal = multipliers[j]; - for (var i = j; i < rowDim; i++) - { - data[(j * rowDim) + i] -= multipliers[i] * tmpVal; - } - } - } - } - - /// - /// Solves A*X=B for X using Cholesky factorization. - /// - /// The square, positive definite matrix A. - /// The number of rows and columns in A. - /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. - /// This is equivalent to the POTRF add POTRS LAPACK routines. - public void CholeskySolve(double[] a, int orderA, double[] b, int rowsB, int columnsB) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (orderA != rowsB) - { - throw new ArgumentException(Resources.ArgumentMatrixDimensions); - } - - if (ReferenceEquals(a, b)) - { - throw new ArgumentException(Resources.ArgumentReferenceDifferent); - } - - CholeskyFactor(a, orderA); - CholeskySolveFactored(a, orderA, b, rowsB, columnsB); - } - - /// - /// Solves A*X=B for X using a previously factored A matrix. - /// - /// The square, positive definite matrix A. Has to be different than . - /// The number of rows and columns in A. - /// The B matrix. Has to be different than . - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. - /// This is equivalent to the POTRS LAPACK routine. - public void CholeskySolveFactored(double[] a, int orderA, double[] b, int rowsB, int columnsB) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (orderA != rowsB) - { - throw new ArgumentException(Resources.ArgumentMatrixDimensions); - } - - if (ReferenceEquals(a, b)) - { - throw new ArgumentException(Resources.ArgumentReferenceDifferent); - } - - CommonParallel.For( - 0, - columnsB, - c => - { - var cindex = c * orderA; - - // Solve L*Y = B; - double sum; - for (var i = 0; i < orderA; i++) - { - sum = b[cindex + i]; - for (var k = i - 1; k >= 0; k--) - { - sum -= a[(k * orderA) + i] * b[cindex + k]; - } - - b[cindex + i] = sum / a[(i * orderA) + i]; - } - - // Solve L'*X = Y; - for (var i = orderA - 1; i >= 0; i--) - { - sum = b[cindex + i]; - var iindex = i * orderA; - for (var k = i + 1; k < orderA; k++) - { - sum -= a[iindex + k] * b[cindex + k]; - } - - b[cindex + i] = sum / a[iindex + i]; - } - }); - } - - /// - /// Computes the QR factorization of A. - /// - /// On entry, it is the M by N A matrix to factor. On exit, - /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// On exit, A M by M matrix that holds the Q matrix of the - /// QR factorization. - /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(double[] r, int rowsR, int columnsR, double[] q) - { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (r.Length != rowsR * columnsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); - } - - if (q.Length != rowsR * rowsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); - } - - var work = new double[rowsR * rowsR]; - QRFactor(r, rowsR, columnsR, q, work); - } - - /// - /// Computes the QR factorization of A. - /// - /// On entry, it is the M by N A matrix to factor. On exit, - /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// On exit, A M by M matrix that holds the Q matrix of the - /// QR factorization. - /// 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. - /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(double[] r, int rowsR, int columnsR, double[] q, double[] work) - { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (work == null) - { - throw new ArgumentNullException("q"); - } - - if (r.Length != rowsR * columnsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); - } - - if (q.Length != rowsR * rowsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); - } - - if (work.Length < rowsR * rowsR) - { - work[0] = rowsR * rowsR; - throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); - } - - CommonParallel.For(0, rowsR, i => q[(i * rowsR) + i] = 1.0); - - var minmn = Math.Min(rowsR, columnsR); - for (var i = 0; i < minmn; i++) - { - GenerateColumn(work, r, rowsR, i, i); - ComputeQR(work, i, r, i, rowsR, i + 1, columnsR, Control.NumberOfParallelWorkerThreads); - } - - for (var i = minmn - 1; i >= 0; i--) - { - ComputeQR(work, i, q, i, rowsR, i, rowsR, Control.NumberOfParallelWorkerThreads); - } - - work[0] = rowsR * rowsR; - } - - #region QR Factor Helper functions - - /// - /// Perform calculation of Q or R - /// - /// Work array - /// Index of colunn in work array - /// Q or R matrices - /// The first row in - /// The last row - /// The first column - /// The last column - /// Number of available CPUs - private static void ComputeQR(double[] work, int workIndex, double[] a, int rowStart, int rowCount, int columnStart, int columnCount, int availableCores) - { - if (rowStart > rowCount || columnStart > columnCount) - { - return; - } - - var tmpColCount = columnCount - columnStart; - - if ((availableCores > 1) && (tmpColCount > 200)) - { - var tmpSplit = columnStart + (tmpColCount / 2); - var tmpCores = availableCores / 2; - - CommonParallel.Invoke( - () => ComputeQR(work, workIndex, a, rowStart, rowCount, columnStart, tmpSplit, tmpCores), - () => ComputeQR(work, workIndex, a, rowStart, rowCount, tmpSplit, columnCount, tmpCores)); - } - else - { - for (var j = columnStart; j < columnCount; j++) - { - var scale = 0.0; - for (var i = rowStart; i < rowCount; i++) - { - scale += work[(workIndex * rowCount) + i - rowStart] * a[(j * rowCount) + i]; - } - - for (var i = rowStart; i < rowCount; i++) - { - a[(j * rowCount) + i] -= work[(workIndex * rowCount) + i - rowStart] * scale; - } - } - } - } - - /// - /// Generate column from initial matrix to work array - /// - /// Work array - /// Initial matrix - /// The number of rows in matrix - /// The firts row - /// Column index - private static void GenerateColumn(double[] work, double[] a, int rowCount, int row, int column) - { - var tmp = column * rowCount; - var index = tmp + row; - - CommonParallel.For( - row, - rowCount, - i => - { - var iIndex = tmp + i; - work[iIndex - row] = a[iIndex]; - a[iIndex] = 0.0; - }); - - var norm = 0.0; - for (var i = 0; i < rowCount - row; ++i) - { - var iindex = tmp + i; - norm += work[iindex] * work[iindex]; - } - - norm = Math.Sqrt(norm); - if (row == rowCount - 1 || norm == 0) - { - a[index] = -work[tmp]; - work[tmp] = Math.Sqrt(2.0); - return; - } - - var scale = 1.0 / norm; - if (work[tmp] < 0.0) - { - scale *= -1.0; - } - - a[index] = -1.0 / scale; - CommonParallel.For(0, rowCount - row, i => work[tmp + i] *= scale); - work[tmp] += 1.0; - - var s = Math.Sqrt(1.0 / work[tmp]); - CommonParallel.For(0, rowCount - row, i => work[tmp + i] *= s); - } - - #endregion - - /// - /// Solves A*X=B for X using QR factorization of A. - /// - /// On entry, it is the M by N A matrix to factor. On exit, - /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// On exit, A M by M matrix that holds the Q matrix of the - /// QR factorization. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - public void QRSolve(double[] r, int rowsR, int columnsR, double[] q, double[] b, int columnsB, double[] x) - { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (b == null) - { - throw new ArgumentNullException("q"); - } - - if (x == null) - { - throw new ArgumentNullException("q"); - } - - if (r.Length != rowsR * columnsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); - } - - if (q.Length != rowsR * rowsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); - } - - if (b.Length != rowsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); - } - - var work = new double[rowsR * rowsR]; - QRSolve(r, rowsR, columnsR, q, b, columnsB, x, work); - } - - /// - /// Solves A*X=B for X using QR factorization of A. - /// - /// On entry, it is the M by N A matrix to factor. On exit, - /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// On exit, A M by M matrix that holds the Q matrix of the - /// QR factorization. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - /// 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. - public void QRSolve(double[] r, int rowsR, int columnsR, double[] q, double[] b, int columnsB, double[] x, double[] work) - { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (b == null) - { - throw new ArgumentNullException("q"); - } - - if (x == null) - { - throw new ArgumentNullException("q"); - } - - if (r.Length != rowsR * columnsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); - } - - if (q.Length != rowsR * rowsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); - } - - if (b.Length != rowsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); - } - - if (work.Length < rowsR * rowsR) - { - work[0] = rowsR * rowsR; - throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); - } - - QRFactor(r, rowsR, columnsR, q, work); - QRSolveFactored(q, r, rowsR, columnsR, b, columnsB, x); - - work[0] = rowsR * rowsR; - } - - /// - /// Solves A*X=B for X using a previously QR factored matrix. - /// - /// The Q matrix obtained by calling . - /// The R matrix obtained by calling . - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - public void QRSolveFactored(double[] q, double[] r, int rowsR, int columnsR, double[] b, int columnsB, double[] x) - { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (b == null) - { - throw new ArgumentNullException("q"); - } - - if (x == null) - { - throw new ArgumentNullException("q"); - } - - if (r.Length != rowsR * columnsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); - } - - if (q.Length != rowsR * rowsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); - } - - if (b.Length != rowsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); - } - - var sol = new double[b.Length]; - - // Copy B matrix to "sol", so B data will not be changed - Buffer.BlockCopy(b, 0, sol, 0, b.Length * Constants.SizeOfDouble); - - // Compute Y = transpose(Q)*B - var column = new double[rowsR]; - for (var j = 0; j < columnsB; j++) - { - var jm = j * rowsR; - CommonParallel.For(0, rowsR, k => column[k] = sol[jm + k]); - CommonParallel.For( - 0, - rowsR, - i => - { - var im = i * rowsR; - sol[jm + i] = CommonParallel.Aggregate(0, rowsR, k => q[im + k] * column[k]); - }); - } - - // Solve R*X = Y; - for (var k = columnsR - 1; k >= 0; k--) - { - var km = k * rowsR; - for (var j = 0; j < columnsB; j++) - { - sol[(j * rowsR) + k] /= r[km + k]; - } - - for (var i = 0; i < k; i++) - { - for (var j = 0; j < columnsB; j++) - { - var jm = j * rowsR; - sol[jm + i] -= sol[jm + k] * r[km + i]; - } - } - } - - // Fill result matrix - CommonParallel.For( - 0, - columnsR, - row => - { - for (var col = 0; col < columnsB; col++) - { - x[(col * columnsR) + row] = sol[row + (col * rowsR)]; - } - }); - } - - /// - /// Computes the singular value decomposition of A. - /// - /// Compute the singular U and VT vectors or not. - /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The singular values of A in ascending value. - /// If is true, on exit U contains the left - /// singular vectors. - /// If is true, on exit VT contains the transposed - /// right singular vectors. - /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, double[] a, int rowsA, int columnsA, double[] s, double[] u, double[] vt) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (u == null) - { - throw new ArgumentNullException("u"); - } - - if (vt == null) - { - throw new ArgumentNullException("vt"); - } - - if (u.Length != rowsA * rowsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); - } - - if (vt.Length != columnsA * columnsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); - } - - if (s.Length != Math.Min(rowsA, columnsA)) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); - } - - // Actually "work = new double[aRows]" is acceptable size of work array. I set size proposed in method description - var work = new double[Math.Max((3 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA), 5 * Math.Min(rowsA, columnsA))]; - SingularValueDecomposition(computeVectors, a, rowsA, columnsA, s, u, vt, work); - } - - /// - /// Computes the singular value decomposition of A. - /// - /// Compute the singular U and VT vectors or not. - /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The singular values of A in ascending value. - /// If is true, on exit U contains the left - /// singular vectors. - /// If is true, on exit VT contains the transposed - /// right singular vectors. - /// The work array. For real matrices, the work array should be at least - /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). - /// On exit, work[0] contains the optimal work size value. - /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, double[] a, int rowsA, int columnsA, double[] s, double[] u, double[] vt, double[] work) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (u == null) - { - throw new ArgumentNullException("u"); - } - - if (vt == null) - { - throw new ArgumentNullException("vt"); - } - - if (work == null) - { - throw new ArgumentNullException("work"); - } - - if (u.Length != rowsA * rowsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); - } - - if (vt.Length != columnsA * columnsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); - } - - if (s.Length != Math.Min(rowsA, columnsA)) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); - } - - if (work.Length == 0) - { - throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); - } - - if (work.Length < rowsA) - { - work[0] = rowsA; - throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); - } - - const int Maxiter = 1000; - - var e = new double[columnsA]; - var v = new double[vt.Length]; - var stemp = new double[Math.Min(rowsA + 1, columnsA)]; - - int i, j, l, lp1; - - var cs = 0.0; - var sn = 0.0; - double t; - - var ncu = rowsA; - - // Reduce matrix to bidiagonal form, storing the diagonal elements - // in "s" and the super-diagonal elements in "e". - var nct = Math.Min(rowsA - 1, columnsA); - var nrt = Math.Max(0, Math.Min(columnsA - 2, rowsA)); - 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 vector s[l]. - var l1 = l; - stemp[l] = Math.Sqrt(CommonParallel.Aggregate(l, rowsA, i1 => (a[(l1 * rowsA) + i1] * a[(l1 * rowsA) + i1]))); - - if (stemp[l] != 0.0) - { - if (a[(l * rowsA) + l] != 0.0) - { - stemp[l] = Math.Abs(stemp[l]) * (a[(l * rowsA) + l] / Math.Abs(a[(l * rowsA) + l])); - } - - // A part of column "l" of Matrix A from row "l" to end multiply by 1.0 / s[l] - for (i = l; i < rowsA; i++) - { - a[(l * rowsA) + i] = a[(l * rowsA) + i] * (1.0 / stemp[l]); - } - - a[(l * rowsA) + l] = 1.0 + a[(l * rowsA) + l]; - } - - stemp[l] = -stemp[l]; - } - - for (j = lp1; j < columnsA; j++) - { - if (l < nct) - { - if (stemp[l] != 0.0) - { - // Apply the transformation. - t = 0.0; - for (i = l; i < rowsA; i++) - { - t += a[(j * rowsA) + i] * a[(l * rowsA) + i]; - } - - t = -t / a[(l * rowsA) + l]; - - for (var ii = l; ii < rowsA; ii++) - { - a[(j * rowsA) + ii] += t * a[(l * rowsA) + ii]; - } - } - } - - // Place the l-th row of matrix into "e" for the - // subsequent calculation of the row transformation. - e[j] = a[(j * rowsA) + l]; - } - - if (computeVectors && l < nct) - { - // Place the transformation in "u" for subsequent back multiplication. - for (i = l; i < rowsA; i++) - { - u[(l * rowsA) + i] = a[(l * rowsA) + i]; - } - } - - if (l >= nrt) - { - continue; - } - - // Compute the l-th row transformation and place the l-th super-diagonal in e(l). - var enorm = 0.0; - for (i = lp1; i < e.Length; i++) - { - enorm += e[i] * e[i]; - } - - e[l] = Math.Sqrt(enorm); - if (e[l] != 0.0) - { - if (e[lp1] != 0.0) - { - e[l] = Math.Abs(e[l]) * (e[lp1] / Math.Abs(e[lp1])); - } - - // Scale vector "e" from "lp1" by 1.0 / e[l] - for (i = lp1; i < e.Length; i++) - { - e[i] = e[i] * (1.0 / e[l]); - } - - e[lp1] = 1.0 + e[lp1]; - } - - e[l] = -e[l]; - - if (lp1 < rowsA && e[l] != 0.0) - { - // Apply the transformation. - for (i = lp1; i < rowsA; i++) - { - work[i] = 0.0; - } - - for (j = lp1; j < columnsA; j++) - { - for (var ii = lp1; ii < rowsA; ii++) - { - work[ii] += e[j] * a[(j * rowsA) + ii]; - } - } - - for (j = lp1; j < columnsA; j++) - { - var ww = -e[j] / e[lp1]; - for (var ii = lp1; ii < rowsA; ii++) - { - a[(j * rowsA) + ii] += ww * work[ii]; - } - } - } - - if (!computeVectors) - { - continue; - } - - // Place the transformation in v for subsequent back multiplication. - for (i = lp1; i < columnsA; i++) - { - v[(l * columnsA) + i] = e[i]; - } - } - - // Set up the final bidiagonal matrix or order m. - var m = Math.Min(columnsA, rowsA + 1); - var nctp1 = nct + 1; - var nrtp1 = nrt + 1; - if (nct < columnsA) - { - stemp[nctp1 - 1] = a[((nctp1 - 1) * rowsA) + (nctp1 - 1)]; - } - - if (rowsA < m) - { - stemp[m - 1] = 0.0; - } - - if (nrtp1 < m) - { - e[nrtp1 - 1] = a[((m - 1) * rowsA) + (nrtp1 - 1)]; - } - - e[m - 1] = 0.0; - - // If required, generate "u". - if (computeVectors) - { - for (j = nctp1 - 1; j < ncu; j++) - { - for (i = 0; i < rowsA; i++) - { - u[(j * rowsA) + i] = 0.0; - } - - u[(j * rowsA) + j] = 1.0; - } - - for (l = nct - 1; l >= 0; l--) - { - if (stemp[l] != 0.0) - { - for (j = l + 1; j < ncu; j++) - { - t = 0.0; - for (i = l; i < rowsA; i++) - { - t += u[(j * rowsA) + i] * u[(l * rowsA) + i]; - } - - t = -t / u[(l * rowsA) + l]; - - for (var ii = l; ii < rowsA; ii++) - { - u[(j * rowsA) + ii] += t * u[(l * rowsA) + ii]; - } - } - - // A part of column "l" of matrix A from row "l" to end multiply by -1.0 - for (i = l; i < rowsA; i++) - { - u[(l * rowsA) + i] = u[(l * rowsA) + i] * -1.0; - } - - u[(l * rowsA) + l] = 1.0 + u[(l * rowsA) + l]; - for (i = 0; i < l; i++) - { - u[(l * rowsA) + i] = 0.0; - } - } - else - { - for (i = 0; i < rowsA; i++) - { - u[(l * rowsA) + i] = 0.0; - } - - u[(l * rowsA) + l] = 1.0; - } - } - } - - // If it is required, generate v. - if (computeVectors) - { - for (l = columnsA - 1; l >= 0; l--) - { - lp1 = l + 1; - if (l < nrt) - { - if (e[l] != 0.0) - { - for (j = lp1; j < columnsA; j++) - { - t = 0.0; - for (i = lp1; i < columnsA; i++) - { - t += v[(j * columnsA) + i] * v[(l * columnsA) + i]; - } - - t = -t / v[(l * columnsA) + lp1]; - for (var ii = l; ii < columnsA; ii++) - { - v[(j * columnsA) + ii] += t * v[(l * columnsA) + ii]; - } - } - } - } - - for (i = 0; i < columnsA; i++) - { - v[(l * columnsA) + i] = 0.0; - } - - v[(l * columnsA) + l] = 1.0; - } - } - - // Transform "s" and "e" so that they are double - for (i = 0; i < m; i++) - { - double r; - if (stemp[i] != 0.0) - { - t = stemp[i]; - r = stemp[i] / t; - stemp[i] = t; - if (i < m - 1) - { - e[i] = e[i] / r; - } - - if (computeVectors) - { - // A part of column "i" of matrix U from row 0 to end multiply by r - for (j = 0; j < rowsA; j++) - { - u[(i * rowsA) + j] = u[(i * rowsA) + j] * r; - } - } - } - - // Exit - if (i == m - 1) - { - break; - } - - if (e[i] == 0.0) - { - continue; - } - - t = e[i]; - r = t / e[i]; - e[i] = t; - stemp[i + 1] = stemp[i + 1] * r; - if (!computeVectors) - { - continue; - } - - // A part of column "i+1" of matrix VT from row 0 to end multiply by r - for (j = 0; j < columnsA; j++) - { - v[((i + 1) * columnsA) + j] = v[((i + 1) * columnsA) + j] * 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. - 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 mS[m] and e[l-1] are negligible and l < m - // kase = 2: if mS[l] is negligible and l < m - // kase = 3: if e[l-1] is negligible, l < m, and mS[l, ..., mS[m] are not negligible (qr step). - // kase = 4: if e[m-1] is negligible (convergence). - double ztest; - double test; - for (l = m - 2; l >= 0; l--) - { - test = Math.Abs(stemp[l]) + Math.Abs(stemp[l + 1]); - ztest = test + Math.Abs(e[l]); - if (ztest.AlmostEqualInDecimalPlaces(test, 15)) - { - e[l] = 0.0; - break; - } - } - - int kase; - if (l == m - 2) - { - kase = 4; - } - else - { - int ls; - for (ls = m - 1; ls > l; ls--) - { - test = 0.0; - 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(stemp[ls]); - if (ztest.AlmostEqualInDecimalPlaces(test, 15)) - { - stemp[ls] = 0.0; - 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; - double f; - switch (kase) - { - // Deflate negligible s[m]. - case 1: - f = e[m - 2]; - e[m - 2] = 0.0; - double t1; - for (var kk = l; kk < m - 1; kk++) - { - k = m - 2 - kk + l; - t1 = stemp[k]; - - Drotg(ref t1, ref f, ref cs, ref sn); - stemp[k] = t1; - if (k != l) - { - f = -sn * e[k - 1]; - e[k - 1] = cs * e[k - 1]; - } - - if (computeVectors) - { - // Rotate - for (i = 0; i < columnsA; i++) - { - var z = (cs * v[(k * columnsA) + i]) + (sn * v[((m - 1) * columnsA) + i]); - v[((m - 1) * columnsA) + i] = (cs * v[((m - 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); - v[(k * columnsA) + i] = z; - } - } - } - - break; - - // Split at negligible s[l]. - case 2: - f = e[l - 1]; - e[l - 1] = 0.0; - for (k = l; k < m; k++) - { - t1 = stemp[k]; - Drotg(ref t1, ref f, ref cs, ref sn); - stemp[k] = t1; - f = -sn * e[k]; - e[k] = cs * e[k]; - if (computeVectors) - { - // Rotate - for (i = 0; i < rowsA; i++) - { - var z = (cs * u[(k * rowsA) + i]) + (sn * u[((l - 1) * rowsA) + i]); - u[((l - 1) * rowsA) + i] = (cs * u[((l - 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); - u[(k * rowsA) + i] = z; - } - } - } - - break; - - // Perform one qr step. - case 3: - - // calculate the shift. - var scale = 0.0; - scale = Math.Max(scale, Math.Abs(stemp[m - 1])); - scale = Math.Max(scale, Math.Abs(stemp[m - 2])); - scale = Math.Max(scale, Math.Abs(e[m - 2])); - scale = Math.Max(scale, Math.Abs(stemp[l])); - scale = Math.Max(scale, Math.Abs(e[l])); - var sm = stemp[m - 1] / scale; - var smm1 = stemp[m - 2] / scale; - var emm1 = e[m - 2] / scale; - var sl = stemp[l] / scale; - var el = e[l] / scale; - var b = (((smm1 + sm) * (smm1 - sm)) + (emm1 * emm1)) / 2.0; - var c = (sm * emm1) * (sm * emm1); - var shift = 0.0; - if (b != 0.0 || c != 0.0) - { - shift = 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 * stemp[k]) + (sn * e[k]); - e[k] = (cs * e[k]) - (sn * stemp[k]); - g = sn * stemp[k + 1]; - stemp[k + 1] = cs * stemp[k + 1]; - if (computeVectors) - { - for (i = 0; i < columnsA; i++) - { - var z = (cs * v[(k * columnsA) + i]) + (sn * v[((k + 1) * columnsA) + i]); - v[((k + 1) * columnsA) + i] = (cs * v[((k + 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); - v[(k * columnsA) + i] = z; - } - } - - Drotg(ref f, ref g, ref cs, ref sn); - stemp[k] = f; - f = (cs * e[k]) + (sn * stemp[k + 1]); - stemp[k + 1] = -(sn * e[k]) + (cs * stemp[k + 1]); - g = sn * e[k + 1]; - e[k + 1] = cs * e[k + 1]; - if (computeVectors && k < rowsA) - { - for (i = 0; i < rowsA; i++) - { - var z = (cs * u[(k * rowsA) + i]) + (sn * u[((k + 1) * rowsA) + i]); - u[((k + 1) * rowsA) + i] = (cs * u[((k + 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); - u[(k * rowsA) + i] = z; - } - } - } - - e[m - 2] = f; - iter = iter + 1; - break; - - // Convergence - case 4: - - // Make the singular value positive - if (stemp[l] < 0.0) - { - stemp[l] = -stemp[l]; - if (computeVectors) - { - // A part of column "l" of matrix VT from row 0 to end multiply by -1 - for (i = 0; i < columnsA; i++) - { - v[(l * columnsA) + i] = v[(l * columnsA) + i] * -1.0; - } - } - } - - // Order the singular value. - while (l != mn - 1) - { - if (stemp[l] >= stemp[l + 1]) - { - break; - } - - t = stemp[l]; - stemp[l] = stemp[l + 1]; - stemp[l + 1] = t; - if (computeVectors && l < columnsA) - { - // Swap columns l, l + 1 - for (i = 0; i < columnsA; i++) - { - var z = v[(l * columnsA) + i]; - v[(l * columnsA) + i] = v[((l + 1) * columnsA) + i]; - v[((l + 1) * columnsA) + i] = z; - } - } - - if (computeVectors && l < rowsA) - { - // Swap columns l, l + 1 - for (i = 0; i < rowsA; i++) - { - var z = u[(l * rowsA) + i]; - u[(l * rowsA) + i] = u[((l + 1) * rowsA) + i]; - u[((l + 1) * rowsA) + i] = z; - } - } - - l = l + 1; - } - - iter = 0; - m = m - 1; - break; - } - } - - if (computeVectors) - { - // Finally transpose "v" to get "vt" matrix - for (i = 0; i < columnsA; i++) - { - for (j = 0; j < columnsA; j++) - { - vt[(j * columnsA) + i] = v[(i * columnsA) + j]; - } - } - } - - // Copy stemp to s with size adjustment. We are using ported copy of linpack's svd code and it uses - // a singular vector of length rows+1 when rows < columns. The last element is not used and needs to be removed. - // We should port lapack's svd routine to remove this problem. - Buffer.BlockCopy(stemp, 0, s, 0, Math.Min(rowsA, columnsA) * Constants.SizeOfDouble); - - // On return the first element of the work array stores the min size of the work array could have been - // work[0] = Math.Max(3 * Math.Min(aRows, aColumns) + Math.Max(aRows, aColumns), 5 * Math.Min(aRows, aColumns)); - work[0] = rowsA; - } - - /// - /// Given the Cartesian coordinates (da, db) of a point p, these fucntion return the parameters da, db, c, and s - /// associated with the Givens rotation that zeros the y-coordinate of the point. - /// - /// Provides the x-coordinate of the point p. On exit contains the parameter r associated with the Givens rotation - /// Provides the y-coordinate of the point p. On exit contains the parameter z associated with the Givens rotation - /// Contains the parameter c associated with the Givens rotation - /// Contains the parameter s associated with the Givens rotation - /// This is equivalent to the DROTG LAPACK routine. - private static void Drotg(ref double da, ref double db, ref double c, ref double s) - { - double r, z; - - var roe = db; - var absda = Math.Abs(da); - var absdb = Math.Abs(db); - if (absda > absdb) - { - roe = da; - } - - var scale = absda + absdb; - if (scale == 0.0) - { - c = 1.0; - s = 0.0; - r = 0.0; - z = 0.0; - } - else - { - var sda = da / scale; - var sdb = db / scale; - r = scale * Math.Sqrt((sda * sda) + (sdb * sdb)); - if (roe < 0.0) - { - r = -r; - } - - c = da / r; - s = db / r; - z = 1.0; - if (absda > absdb) - { - z = s; - } - - if (absdb >= absda && c != 0.0) - { - z = 1.0 / c; - } - } - - da = r; - db = z; - } - - /// - /// Solves A*X=B for X using the singular value decomposition of A. - /// - /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The singular values of A in ascending value. - /// On exit U contains the left singular vectors. - /// On exit VT contains the transposed right singular vectors. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - public void SvdSolve(double[] a, int rowsA, int columnsA, double[] s, double[] u, double[] vt, double[] b, int columnsB, double[] x) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (u == null) - { - throw new ArgumentNullException("u"); - } - - if (vt == null) - { - throw new ArgumentNullException("vt"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (u.Length != rowsA * rowsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); - } - - if (vt.Length != columnsA * columnsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); - } - - if (s.Length != Math.Min(rowsA, columnsA)) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); - } - - if (b.Length != rowsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - // Actually "work = new double[aRows]" is acceptable size of work array. I set size proposed in method description - var work = new double[Math.Max((3 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA), 5 * Math.Min(rowsA, columnsA))]; - SvdSolve(a, rowsA, columnsA, s, u, vt, b, columnsB, x, work); - } - - /// - /// Solves A*X=B for X using the singular value decomposition of A. - /// - /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The singular values of A in ascending value. - /// On exit U contains the left singular vectors. - /// On exit VT contains the transposed right singular vectors. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - /// The work array. For real matrices, the work array should be at least - /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). - /// On exit, work[0] contains the optimal work size value. - public void SvdSolve(double[] a, int rowsA, int columnsA, double[] s, double[] u, double[] vt, double[] b, int columnsB, double[] x, double[] work) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (u == null) - { - throw new ArgumentNullException("u"); - } - - if (vt == null) - { - throw new ArgumentNullException("vt"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (u.Length != rowsA * rowsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); - } - - if (vt.Length != columnsA * columnsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); - } - - if (s.Length != Math.Min(rowsA, columnsA)) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); - } - - if (b.Length != rowsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (work.Length == 0) - { - throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); - } - - if (work.Length < rowsA) - { - work[0] = rowsA; - throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); - } - - SingularValueDecomposition(true, a, rowsA, columnsA, s, u, vt, work); - SvdSolveFactored(rowsA, columnsA, s, u, vt, b, columnsB, x); - } - - /// - /// Solves A*X=B for X using a previously SVD decomposed matrix. - /// - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The s values returned by . - /// The left singular vectors returned by . - /// The right singular vectors returned by . - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - public void SvdSolveFactored(int rowsA, int columnsA, double[] s, double[] u, double[] vt, double[] b, int columnsB, double[] x) - { - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (u == null) - { - throw new ArgumentNullException("u"); - } - - if (vt == null) - { - throw new ArgumentNullException("vt"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (u.Length != rowsA * rowsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); - } - - if (vt.Length != columnsA * columnsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); - } - - if (s.Length != Math.Min(rowsA, columnsA)) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); - } - - if (b.Length != rowsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - var mn = Math.Min(rowsA, columnsA); - var tmp = new double[columnsA]; - - for (var k = 0; k < columnsB; k++) - { - for (var j = 0; j < columnsA; j++) - { - double value = 0; - if (j < mn) - { - for (var i = 0; i < rowsA; i++) - { - value += u[(j * rowsA) + i] * b[(k * rowsA) + i]; - } - - value /= s[j]; - } - - tmp[j] = value; - } - - for (var j = 0; j < columnsA; j++) - { - double value = 0; - for (var i = 0; i < columnsA; i++) - { - value += vt[(j * columnsA) + i] * tmp[i]; - } - - x[(k * columnsA) + j] = value; - } - } - } - - #endregion - - #region ILinearAlgebraProvider Members - - /// - /// Adds a scaled vector to another: y += alpha*x. - /// - /// The vector to update. - /// The value to scale by. - /// The vector to add to . - /// This equivalent to the AXPY BLAS routine. - public void AddVectorToScaledVector(float[] y, float alpha, float[] x) - { - 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 (alpha == 0.0) - { - return; - } - - if (alpha == 1.0) - { - CommonParallel.For(0, y.Length, i => y[i] += x[i]); - } - else - { - CommonParallel.For(0, y.Length, i => y[i] += alpha * x[i]); - } - } - - /// - /// Scales an array. Can be used to scale a vector and a matrix. - /// - /// The scalar. - /// The values to scale. - /// This is equivalent to the SCAL BLAS routine. - public void ScaleArray(float alpha, float[] x) - { - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (alpha == 1.0) - { - return; - } - - CommonParallel.For(0, x.Length, i => x[i] = alpha * x[i]); - } - - /// - /// Computes the dot product of x and y. - /// - /// The vector x. - /// The vector y. - /// The dot product of x and y. - /// This is equivalent to the DOT BLAS routine. - public float DotProduct(float[] x, float[] y) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (y.Length != x.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } - - float sum = 0; - CommonParallel.For(0, y.Length, index => sum += y[index] * x[index]); - return sum; - } - - /// - /// Does a point wise add of two arrays z = x + y. This can be used - /// to add vectors or matrices. - /// - /// The array x. - /// The array y. - /// The result of the addition. - /// There is no equivalent BLAS routine, but many libraries - /// provide optimized (parallel and/or vectorized) versions of this - /// routine. - public void AddArrays(float[] x, float[] y, float[] result) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (y.Length != x.Length || y.Length != result.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } - - CommonParallel.For(0, y.Length, i => result[i] = x[i] + y[i]); - } - - /// - /// Does a point wise subtraction of two arrays z = x - y. This can be used - /// to subtract vectors or matrices. - /// - /// The array x. - /// The array y. - /// The result of the subtraction. - /// There is no equivalent BLAS routine, but many libraries - /// provide optimized (parallel and/or vectorized) versions of this - /// routine. - public void SubtractArrays(float[] x, float[] y, float[] result) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (y.Length != x.Length || y.Length != result.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } - - CommonParallel.For(0, y.Length, i => result[i] = x[i] - y[i]); - } - - /// - /// Does a point wise multiplication of two arrays z = x * y. This can be used - /// to multiple elements of vectors or matrices. - /// - /// The array x. - /// The array y. - /// The result of the point wise multiplication. - /// There is no equivalent BLAS routine, but many libraries - /// provide optimized (parallel and/or vectorized) versions of this - /// routine. - public void PointWiseMultiplyArrays(float[] x, float[] y, float[] result) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (y.Length != x.Length || y.Length != result.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } - - CommonParallel.For(0, y.Length, i => result[i] = x[i] * y[i]); - } - - /// - /// Does a point wise division of two arrays z = x / y. This can be used - /// to divide elements of vectors or matrices. - /// - /// The array x. - /// The array y. - /// The result of the point wise division. - /// There is no equivalent BLAS routine, but many libraries - /// provide optimized (parallel and/or vectorized) versions of this - /// routine. - public void PointWiseDivideArrays(float[] x, float[] y, float[] result) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (y.Length != x.Length || y.Length != result.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } - - CommonParallel.For(0, y.Length, index => { result[index] = x[index] / y[index]; }); - } - - /// - /// Computes the requested of the matrix. - /// - /// The type of norm to compute. - /// The number of rows. - /// The number of columns. - /// The matrix to compute the norm from. - /// - /// The requested of the matrix. - /// - public float MatrixNorm(Norm norm, int rows, int columns, float[] matrix) - { - throw new NotImplementedException(); - } - - /// - /// Computes the requested of the matrix. - /// - /// The type of norm to compute. - /// The number of rows. - /// The number of columns. - /// The matrix to compute the norm from. - /// The work array. Only used when - /// and needs to be have a length of at least M (number of rows of . - /// - /// The requested of the matrix. - /// - public float MatrixNorm(Norm norm, int rows, int columns, float[] matrix, float[] work) - { - throw new NotImplementedException(); - } - - /// - /// Multiples two matrices. result = x * y - /// - /// The x matrix. - /// The number of rows in the x matrix. - /// The number of columns in the x matrix. - /// The y matrix. - /// The number of rows in the y matrix. - /// The number of columns in the y matrix. - /// Where to store the result of the multiplication. - /// This is a simplified version of the BLAS GEMM routine with alpha - /// set to 1.0 and beta set to 0.0, and x and y are not transposed. - public void MatrixMultiply(float[] x, int rowsX, int columnsX, float[] y, int rowsY, int columnsY, float[] result) - { - // First check some basic requirement on the parameters of the matrix multiplication. - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (rowsX * columnsX != x.Length) - { - throw new ArgumentException("x.Length != xRows * xColumns"); - } - - if (rowsY * columnsY != y.Length) - { - throw new ArgumentException("y.Length != yRows * yColumns"); - } - - if (columnsX != rowsY) - { - throw new ArgumentException("xColumns != yRows"); - } - - if (rowsX * columnsY != result.Length) - { - throw new ArgumentException("xRows * yColumns != result.Length"); - } - - // Check whether we will be overwriting any of our inputs and make copies if necessary. - // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory - // as result, we can do it on a row wise basis. We should investigate this. - float[] xdata; - if (ReferenceEquals(x, result)) - { - xdata = (float[])x.Clone(); - } - else - { - xdata = x; - } - - float[] ydata; - if (ReferenceEquals(y, result)) - { - ydata = (float[])y.Clone(); - } - else - { - ydata = y; - } - - // Start the actual matrix multiplication. - // TODO - For small matrices we should get rid of the parallelism because of startup costs. - // Perhaps the following implementations would be a good one - // http://blog.feradz.com/2009/01/cache-efficient-matrix-multiplication/ - MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 1.0f, xdata, rowsX, columnsX, ydata, rowsY, columnsY, 0.0f, result); - } - - /// - /// Multiplies two matrices and updates another with the result. c = alpha*op(a)*op(b) + beta*c - /// - /// How to transpose the matrix. - /// How to transpose the matrix. - /// The value to scale matrix. - /// The a matrix. - /// The number of rows in the matrix. - /// The number of columns in the matrix. - /// The b matrix - /// The number of rows in the matrix. - /// The number of columns in the matrix. - /// The value to scale the matrix. - /// The c matrix. - public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, float alpha, float[] a, int rowsA, int columnsA, float[] b, int rowsB, int columnsB, float beta, float[] c) - { - // Choose nonsensical values for the number of rows in c; fill them in depending - // on the operations on a and b. - int rowsC; - - // First check some basic requirement on the parameters of the matrix multiplication. - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if ((int)transposeA > 111 && (int)transposeB > 111) - { - if (rowsA != columnsB) - { - throw new ArgumentOutOfRangeException(); - } - - if (columnsA * rowsB != c.Length) - { - throw new ArgumentOutOfRangeException(); - } - - rowsC = columnsA; - } - else if ((int)transposeA > 111) - { - if (rowsA != rowsB) - { - throw new ArgumentOutOfRangeException(); - } - - if (columnsA * columnsB != c.Length) - { - throw new ArgumentOutOfRangeException(); - } - - rowsC = columnsA; - } - else if ((int)transposeB > 111) - { - if (columnsA != columnsB) - { - throw new ArgumentOutOfRangeException(); - } - - if (rowsA * rowsB != c.Length) - { - throw new ArgumentOutOfRangeException(); - } - - rowsC = rowsA; - } - else - { - if (columnsA != rowsB) - { - throw new ArgumentOutOfRangeException(); - } - - if (rowsA * columnsB != c.Length) - { - throw new ArgumentOutOfRangeException(); - } - - rowsC = rowsA; - } - - if (alpha == 0.0 && beta == 0.0) - { - Array.Clear(c, 0, c.Length); - return; - } - - // Check whether we will be overwriting any of our inputs and make copies if necessary. - // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory - // as result, we can do it on a row wise basis. We should investigate this. - float[] adata; - if (ReferenceEquals(a, c)) - { - adata = (float[])a.Clone(); - } - else - { - adata = a; - } - - float[] bdata; - if (ReferenceEquals(b, c)) - { - bdata = (float[])b.Clone(); - } - else - { - bdata = b; - } - - if (alpha == 1.0) - { - if (beta == 0.0) - { - if ((int)transposeA > 111 && (int)transposeB > 111) - { - CommonParallel.For( - 0, - columnsA, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsB; i++) - { - var iIndex = i * rowsA; - float s = 0; - for (var l = 0; l != columnsB; l++) - { - s += adata[iIndex + l] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = s; - } - }); - } - else if ((int)transposeA > 111) - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != columnsA; i++) - { - var iIndex = i * rowsA; - float s = 0; - for (var l = 0; l != rowsA; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s; - } - }); - } - else if ((int)transposeB > 111) - { - CommonParallel.For( - 0, - rowsB, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsA; i++) - { - float s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = s; - } - }); - } - else - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != rowsA; i++) - { - float s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s; - } - }); - } - } - else - { - if ((int)transposeA > 111 && (int)transposeB > 111) - { - CommonParallel.For( - 0, - columnsA, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsB; i++) - { - var iIndex = i * rowsA; - float s = 0; - for (var l = 0; l != columnsB; l++) - { - s += adata[iIndex + l] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = (c[jIndex + i] * beta) + s; - } - }); - } - else if ((int)transposeA > 111) - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != columnsA; i++) - { - var iIndex = i * rowsA; - float s = 0; - for (var l = 0; l != rowsA; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s + (c[jcIndex + i] * beta); - } - }); - } - else if ((int)transposeB > 111) - { - CommonParallel.For( - 0, - rowsB, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsA; i++) - { - float s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = s + (c[jIndex + i] * beta); - } - }); - } - else - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != rowsA; i++) - { - float s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s + (c[jcIndex + i] * beta); - } - }); - } - } - } - else - { - if ((int)transposeA > 111 && (int)transposeB > 111) - { - CommonParallel.For( - 0, - columnsA, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsB; i++) - { - var iIndex = i * rowsA; - float s = 0; - for (var l = 0; l != columnsB; l++) - { - s += adata[iIndex + l] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = (c[jIndex + i] * beta) + (alpha * s); - } - }); - } - else if ((int)transposeA > 111) - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != columnsA; i++) - { - var iIndex = i * rowsA; - float s = 0; - for (var l = 0; l != rowsA; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); - } - }); - } - else if ((int)transposeB > 111) - { - CommonParallel.For( - 0, - rowsB, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsA; i++) - { - float s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = (alpha * s) + (c[jIndex + i] * beta); - } - }); - } - else - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != rowsA; i++) - { - float s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); - } - }); - } - } - } - - /// - /// Computes the LUP factorization of A. P*A = L*U. - /// - /// An by matrix. The matrix is overwritten with the - /// the LU factorization on exit. The lower triangular factor L is stored in under the diagonal of (the diagonal is always 1.0 - /// for the L factor). The upper triangular factor U is stored on and above the diagonal of . - /// The order of the square matrix . - /// On exit, it contains the pivot indices. The size of the array must be . - /// This is equivalent to the GETRF LAPACK routine. - public void LUFactor(float[] data, int order, int[] ipiv) - { - if (data == null) - { - throw new ArgumentNullException("data"); - } - - if (ipiv == null) - { - throw new ArgumentNullException("ipiv"); - } - - if (data.Length != order * order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "data"); - } - - if (ipiv.Length != order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); - } - - // Initialize the pivot matrix to the identity permutation. - for (var i = 0; i < order; i++) - { - ipiv[i] = i; - } - - var vecLUcolj = new float[order]; - - // Outer loop. - for (var j = 0; j < order; j++) - { - var indexj = j * order; - var indexjj = indexj + j; - - // Make a copy of the j-th column to localize references. - for (var i = 0; i < order; i++) - { - vecLUcolj[i] = data[indexj + i]; - } - - // Apply previous transformations. - for (var i = 0; i < order; i++) - { - // Most of the time is spent in the following dot product. - var kmax = Math.Min(i, j); - var s = 0.0f; - for (var k = 0; k < kmax; k++) - { - s += data[(k * order) + i] * vecLUcolj[k]; - } - - data[indexj + i] = vecLUcolj[i] -= s; - } - - // Find pivot and exchange if necessary. - var p = j; - for (var i = j + 1; i < order; i++) - { - if (Math.Abs(vecLUcolj[i]) > Math.Abs(vecLUcolj[p])) - { - p = i; - } - } - - if (p != j) - { - for (var k = 0; k < order; k++) - { - var indexk = k * order; - var indexkp = indexk + p; - var indexkj = indexk + j; - var temp = data[indexkp]; - data[indexkp] = data[indexkj]; - data[indexkj] = temp; - } - - ipiv[j] = p; - } - - // Compute multipliers. - if (j < order & data[indexjj] != 0.0) - { - for (var i = j + 1; i < order; i++) - { - data[indexj + i] /= data[indexjj]; - } - } - } - } - - /// - /// Computes the inverse of matrix using LU factorization. - /// - /// The N by N matrix to invert. Contains the inverse On exit. - /// The order of the square matrix . - /// This is equivalent to the GETRF and GETRI LAPACK routines. - public void LUInverse(float[] a, int order) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (a.Length != order * order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); - } - - var ipiv = new int[order]; - LUFactor(a, order, ipiv); - LUInverseFactored(a, order, ipiv); - } - - /// - /// Computes the inverse of a previously factored matrix. - /// - /// The LU factored N by N matrix. Contains the inverse On exit. - /// The order of the square matrix . - /// The pivot indices of . - /// This is equivalent to the GETRI LAPACK routine. - public void LUInverseFactored(float[] 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 float[a.Length]; - for (var i = 0; i < order; i++) - { - inverse[i + (order * i)] = 1.0f; - } - - LUSolveFactored(order, a, order, ipiv, inverse); - Buffer.BlockCopy(inverse, 0, a, 0, a.Length * Constants.SizeOfFloat); - } - - /// - /// Computes the inverse of matrix using LU factorization. - /// - /// The N by N matrix to invert. Contains the inverse On exit. - /// The order of the square matrix . - /// 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. - /// This is equivalent to the GETRF and GETRI LAPACK routines. - public void LUInverse(float[] a, int order, float[] work) - { - LUInverse(a, order); - } - - /// - /// Computes the inverse of a previously factored matrix. - /// - /// The LU factored N by N matrix. Contains the inverse On exit. - /// The order of the square matrix . - /// The pivot indices of . - /// 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. - /// This is equivalent to the GETRI LAPACK routine. - public void LUInverseFactored(float[] a, int order, int[] ipiv, float[] work) - { - LUInverseFactored(a, order, ipiv); - } - - /// - /// Solves A*X=B for X using LU factorization. - /// - /// The number of columns of B. - /// The square matrix A. - /// The order of the square matrix . - /// The B matrix. - /// This is equivalent to the GETRF and GETRS LAPACK routines. - public void LUSolve(int columnsOfB, float[] a, int order, float[] 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"); - } - - var ipiv = new int[order]; - LUFactor(a, order, ipiv); - LUSolveFactored(columnsOfB, a, order, ipiv, b); - } - - /// - /// Solves A*X=B for X using a previously factored A matrix. - /// - /// The number of columns of B. - /// The factored A matrix. - /// The order of the square matrix . - /// The pivot indices of . - /// The B matrix. - /// This is equivalent to the GETRS LAPACK routine. - public void LUSolveFactored(int columnsOfB, float[] a, int order, int[] ipiv, float[] b) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (ipiv == null) - { - throw new ArgumentNullException("ipiv"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (a.Length != order * order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); - } - - if (ipiv.Length != order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); - } - - if (b.Length != order * columnsOfB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - // Compute the column vector P*B - for (var i = 0; i < ipiv.Length; i++) - { - if (ipiv[i] == i) - { - continue; - } - - var p = ipiv[i]; - for (var j = 0; j < columnsOfB; j++) - { - var indexk = j * order; - var indexkp = indexk + p; - var indexkj = indexk + i; - var temp = b[indexkp]; - b[indexkp] = b[indexkj]; - b[indexkj] = temp; - } - } - - // Solve L*Y = P*B - for (var k = 0; k < order; k++) - { - var korder = k * order; - for (var i = k + 1; i < order; i++) - { - for (var j = 0; j < columnsOfB; j++) - { - var index = j * order; - b[i + index] -= b[k + index] * a[i + korder]; - } - } - } - - // Solve U*X = Y; - for (var k = order - 1; k >= 0; k--) - { - var korder = k + (k * order); - for (var j = 0; j < columnsOfB; j++) - { - b[k + (j * order)] /= a[korder]; - } - - korder = k * order; - for (var i = 0; i < k; i++) - { - for (var j = 0; j < columnsOfB; j++) - { - var index = j * order; - b[i + index] -= b[k + index] * a[i + korder]; - } - } - } - } - - /// - /// Solves A*X=B for X using LU factorization. - /// - /// How to transpose the matrix. - /// The number of columns of B. - /// The square matrix A. - /// The order of the square matrix . - /// The B matrix. - /// This is equivalent to the GETRF and GETRS LAPACK routines. - public void LUSolve(Transpose transposeA, int columnsOfB, float[] a, int order, float[] 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"); - } - - var ipiv = new int[order]; - LUFactor(a, order, ipiv); - LUSolveFactored(transposeA, columnsOfB, a, order, ipiv, b); - } - - /// - /// Solves A*X=B for X using a previously factored A matrix. - /// - /// How to transpose the matrix. - /// The number of columns of B. - /// The factored A matrix. - /// The order of the square matrix . - /// The pivot indices of . - /// The B matrix. - /// This is equivalent to the GETRS LAPACK routine. - public void LUSolveFactored(Transpose transposeA, int columnsOfB, float[] a, int order, int[] ipiv, float[] b) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (ipiv == null) - { - throw new ArgumentNullException("ipiv"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (a.Length != order * order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); - } - - if (ipiv.Length != order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); - } - - if (b.Length != order * columnsOfB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if ((transposeA == Transpose.Transpose) || (transposeA == Transpose.ConjugateTranspose)) - { - var aT = new float[a.Length]; - for (var i = 0; i < order; i++) - { - for (var j = 0; j < order; j++) - { - aT[(j * order) + i] = a[(i * order) + j]; - } - } - - LUSolveFactored(columnsOfB, aT, order, ipiv, b); - } - else - { - LUSolveFactored(columnsOfB, a, order, ipiv, b); - } - } - - /// - /// Computes the Cholesky factorization of A. - /// - /// On entry, a square, positive definite matrix. On exit, the matrix is overwritten with the - /// the Cholesky factorization. - /// The number of rows or columns in the matrix. - /// This is equivalent to the POTRF LAPACK routine. - public void CholeskyFactor(float[] a, int order) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - var tmpColumn = new float[order]; - - // Main loop - along the diagonal - for (var ij = 0; ij < order; ij++) - { - // "Pivot" element - var tmpVal = a[(ij * order) + ij]; - - if (tmpVal > 0.0) - { - tmpVal = (float)Math.Sqrt(tmpVal); - a[(ij * order) + ij] = tmpVal; - tmpColumn[ij] = tmpVal; - - // Calculate multipliers and copy to local column - // Current column, below the diagonal - for (var i = ij + 1; i < order; i++) - { - a[(ij * order) + i] /= tmpVal; - tmpColumn[i] = a[(ij * order) + i]; - } - - // Remaining columns, below the diagonal - DoCholeskyStep(a, order, ij + 1, order, tmpColumn, Control.NumberOfParallelWorkerThreads); - } - else - { - throw new ArgumentException(Resources.ArgumentMatrixPositiveDefinite); - } - - for (int i = ij + 1; i < order; i++) - { - a[(i * order) + ij] = 0.0f; - } - } - } - - /// - /// Calculate Cholesky step - /// - /// Factor matrix - /// Number of rows - /// Column start - /// Total columns - /// Multipliears calculated previously - /// Number of available processors - private static void DoCholeskyStep(float[] data, int rowDim, int firstCol, int colLimit, float[] multipliers, int availableCores) - { - var tmpColCount = colLimit - firstCol; - - if ((availableCores > 1) && (tmpColCount > 200)) - { - var tmpSplit = firstCol + (tmpColCount / 3); - var tmpCores = availableCores / 2; - - CommonParallel.Invoke( - () => DoCholeskyStep(data, rowDim, firstCol, tmpSplit, multipliers, tmpCores), - () => DoCholeskyStep(data, rowDim, tmpSplit, colLimit, multipliers, tmpCores)); - } - else - { - for (var j = firstCol; j < colLimit; j++) - { - var tmpVal = multipliers[j]; - for (var i = j; i < rowDim; i++) - { - data[(j * rowDim) + i] -= multipliers[i] * tmpVal; - } - } - } - } - - /// - /// Solves A*X=B for X using Cholesky factorization. - /// - /// The square, positive definite matrix A. - /// The number of rows and columns in A. - /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. - /// This is equivalent to the POTRF add POTRS LAPACK routines. - public void CholeskySolve(float[] a, int orderA, float[] b, int rowsB, int columnsB) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (orderA != rowsB) - { - throw new ArgumentException(Resources.ArgumentMatrixDimensions); - } - - if (ReferenceEquals(a, b)) - { - throw new ArgumentException(Resources.ArgumentReferenceDifferent); - } - - CholeskyFactor(a, orderA); - CholeskySolveFactored(a, orderA, b, rowsB, columnsB); - } - - /// - /// Solves A*X=B for X using a previously factored A matrix. - /// - /// The square, positive definite matrix A. - /// The number of rows and columns in A. - /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. - /// This is equivalent to the POTRS LAPACK routine. - public void CholeskySolveFactored(float[] a, int orderA, float[] b, int rowsB, int columnsB) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (orderA != rowsB) - { - throw new ArgumentException(Resources.ArgumentMatrixDimensions); - } - - if (ReferenceEquals(a, b)) - { - throw new ArgumentException(Resources.ArgumentReferenceDifferent); - } - - CommonParallel.For( - 0, - columnsB, - c => - { - var cindex = c * orderA; - - // Solve L*Y = B; - float sum; - for (var i = 0; i < orderA; i++) - { - sum = b[cindex + i]; - for (var k = i - 1; k >= 0; k--) - { - sum -= a[(k * orderA) + i] * b[cindex + k]; - } - - b[cindex + i] = sum / a[(i * orderA) + i]; - } - - // Solve L'*X = Y; - for (var i = orderA - 1; i >= 0; i--) - { - sum = b[cindex + i]; - var iindex = i * orderA; - for (var k = i + 1; k < orderA; k++) - { - sum -= a[iindex + k] * b[cindex + k]; - } - - b[cindex + i] = sum / a[iindex + i]; - } - }); - } - - /// - /// Computes the QR factorization of A. - /// - /// On entry, it is the M by N A matrix to factor. On exit, - /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// On exit, A M by M matrix that holds the Q matrix of the - /// QR factorization. - /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(float[] r, int rowsR, int columnsR, float[] q) - { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (r.Length != rowsR * columnsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); - } - - if (q.Length != rowsR * rowsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); - } - - var work = new float[rowsR * rowsR]; - QRFactor(r, rowsR, columnsR, q, work); - } - - /// - /// Computes the QR factorization of A. - /// - /// On entry, it is the M by N A matrix to factor. On exit, - /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// On exit, A M by M matrix that holds the Q matrix of the - /// QR factorization. - /// 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. - /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(float[] r, int rowsR, int columnsR, float[] q, float[] work) - { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (work == null) - { - throw new ArgumentNullException("q"); - } - - if (r.Length != rowsR * columnsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); - } - - if (q.Length != rowsR * rowsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); - } - - if (work.Length < rowsR * rowsR) - { - work[0] = rowsR * rowsR; - throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); - } - - CommonParallel.For(0, rowsR, i => q[(i * rowsR) + i] = 1.0f); - - var minmn = Math.Min(rowsR, columnsR); - for (var i = 0; i < minmn; i++) - { - GenerateColumn(work, r, rowsR, i, i); - ComputeQR(work, i, r, i, rowsR, i + 1, columnsR, Control.NumberOfParallelWorkerThreads); - } - - for (var i = minmn - 1; i >= 0; i--) - { - ComputeQR(work, i, q, i, rowsR, i, rowsR, Control.NumberOfParallelWorkerThreads); - } - - work[0] = rowsR * rowsR; - } - - #region QR Factor Helper functions - - /// - /// Perform calculation of Q or R - /// - /// Work array - /// Index of colunn in work array - /// Q or R matrices - /// The first row in - /// The last row - /// The first column - /// The last column - /// Number of available CPUs - private static void ComputeQR(float[] work, int workIndex, float[] a, int rowStart, int rowCount, int columnStart, int columnCount, int availableCores) - { - if (rowStart > rowCount || columnStart > columnCount) - { - return; - } - - var tmpColCount = columnCount - columnStart; - - if ((availableCores > 1) && (tmpColCount > 200)) - { - var tmpSplit = columnStart + (tmpColCount / 2); - var tmpCores = availableCores / 2; - - CommonParallel.Invoke( - () => ComputeQR(work, workIndex, a, rowStart, rowCount, columnStart, tmpSplit, tmpCores), - () => ComputeQR(work, workIndex, a, rowStart, rowCount, tmpSplit, columnCount, tmpCores)); - } - else - { - for (var j = columnStart; j < columnCount; j++) - { - var scale = 0.0f; - for (var i = rowStart; i < rowCount; i++) - { - scale += work[(workIndex * rowCount) + i - rowStart] * a[(j * rowCount) + i]; - } - - for (var i = rowStart; i < rowCount; i++) - { - a[(j * rowCount) + i] -= work[(workIndex * rowCount) + i - rowStart] * scale; - } - } - } - } - - /// - /// Generate column from initial matrix to work array - /// - /// Work array - /// Initial matrix - /// The number of rows in matrix - /// The firts row - /// Column index - private static void GenerateColumn(float[] work, float[] a, int rowCount, int row, int column) - { - var tmp = column * rowCount; - var index = tmp + row; - - CommonParallel.For( - row, - rowCount, - i => - { - var iIndex = tmp + i; - work[iIndex - row] = a[iIndex]; - a[iIndex] = 0.0f; - }); - - var norm = 0.0; - for (var i = 0; i < rowCount - row; ++i) - { - var iindex = tmp + i; - norm += work[iindex] * work[iindex]; - } - - norm = Math.Sqrt(norm); - if (row == rowCount - 1 || norm == 0) - { - a[index] = -work[tmp]; - work[tmp] = (float)Math.Sqrt(2.0); - return; - } - - var scale = 1.0f / (float)norm; - if (work[tmp] < 0.0) - { - scale *= -1.0f; - } - - a[index] = -1.0f / scale; - CommonParallel.For(0, rowCount - row, i => work[tmp + i] *= scale); - work[tmp] += 1.0f; - - var s = (float)Math.Sqrt(1.0 / work[tmp]); - CommonParallel.For(0, rowCount - row, i => work[tmp + i] *= s); - } - - #endregion - - /// - /// Solves A*X=B for X using QR factorization of A. - /// - /// On entry, it is the M by N A matrix to factor. On exit, - /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// On exit, A M by M matrix that holds the Q matrix of the - /// QR factorization. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - public void QRSolve(float[] r, int rowsR, int columnsR, float[] q, float[] b, int columnsB, float[] x) - { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (b == null) - { - throw new ArgumentNullException("q"); - } - - if (x == null) - { - throw new ArgumentNullException("q"); - } - - if (r.Length != rowsR * columnsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); - } - - if (q.Length != rowsR * rowsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); - } - - if (b.Length != rowsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); - } - - var work = new float[rowsR * rowsR]; - QRSolve(r, rowsR, columnsR, q, b, columnsB, x, work); - } - - /// - /// Solves A*X=B for X using QR factorization of A. - /// - /// On entry, it is the M by N A matrix to factor. On exit, - /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// On exit, A M by M matrix that holds the Q matrix of the - /// QR factorization. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - /// 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. - public void QRSolve(float[] r, int rowsR, int columnsR, float[] q, float[] b, int columnsB, float[] x, float[] work) - { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (b == null) - { - throw new ArgumentNullException("q"); - } - - if (x == null) - { - throw new ArgumentNullException("q"); - } - - if (r.Length != rowsR * columnsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); - } - - if (q.Length != rowsR * rowsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); - } - - if (b.Length != rowsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); - } - - if (work.Length < rowsR * rowsR) - { - work[0] = rowsR * rowsR; - throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); - } - - QRFactor(r, rowsR, columnsR, q, work); - QRSolveFactored(q, r, rowsR, columnsR, b, columnsB, x); - - work[0] = rowsR * rowsR; - } - - /// - /// Solves A*X=B for X using a previously QR factored matrix. - /// - /// The Q matrix obtained by calling . - /// The R matrix obtained by calling . - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - public void QRSolveFactored(float[] q, float[] r, int rowsR, int columnsR, float[] b, int columnsB, float[] x) - { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (b == null) - { - throw new ArgumentNullException("q"); - } - - if (x == null) - { - throw new ArgumentNullException("q"); - } - - if (r.Length != rowsR * columnsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); - } - - if (q.Length != rowsR * rowsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); - } - - if (b.Length != rowsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); - } - - var sol = new float[b.Length]; - - // Copy B matrix to "sol", so B data will not be changed - Buffer.BlockCopy(b, 0, sol, 0, b.Length * Constants.SizeOfFloat); - - // Compute Y = transpose(Q)*B - var column = new float[rowsR]; - for (var j = 0; j < columnsB; j++) - { - var jm = j * rowsR; - CommonParallel.For(0, rowsR, k => column[k] = sol[jm + k]); - CommonParallel.For( - 0, - rowsR, - i => - { - var im = i * rowsR; - sol[jm + i] = CommonParallel.Aggregate(0, rowsR, k => q[im + k] * column[k]); - }); - } - - // Solve R*X = Y; - for (var k = columnsR - 1; k >= 0; k--) - { - var km = k * rowsR; - for (var j = 0; j < columnsB; j++) - { - sol[(j * rowsR) + k] /= r[km + k]; - } - - for (var i = 0; i < k; i++) - { - for (var j = 0; j < columnsB; j++) - { - var jm = j * rowsR; - sol[jm + i] -= sol[jm + k] * r[km + i]; - } - } - } - - // Fill result matrix - CommonParallel.For( - 0, - columnsR, - row => - { - for (var col = 0; col < columnsB; col++) - { - x[(col * columnsR) + row] = sol[row + (col * rowsR)]; - } - }); - } - - /// - /// Computes the singular value decomposition of A. - /// - /// Compute the singular U and VT vectors or not. - /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The singular values of A in ascending value. - /// If is true, on exit U contains the left - /// singular vectors. - /// If is true, on exit VT contains the transposed - /// right singular vectors. - /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (u == null) - { - throw new ArgumentNullException("u"); - } - - if (vt == null) - { - throw new ArgumentNullException("vt"); - } - - if (u.Length != rowsA * rowsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); - } - - if (vt.Length != columnsA * columnsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); - } - - if (s.Length != Math.Min(rowsA, columnsA)) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); - } - - // Actually "work = new float[aRows]" is acceptable size of work array. I set size proposed in method description - var work = new float[Math.Max((3 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA), 5 * Math.Min(rowsA, columnsA))]; - SingularValueDecomposition(computeVectors, a, rowsA, columnsA, s, u, vt, work); - } - - /// - /// Computes the singular value decomposition of A. - /// - /// Compute the singular U and VT vectors or not. - /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The singular values of A in ascending value. - /// If is true, on exit U contains the left - /// singular vectors. - /// If is true, on exit VT contains the transposed - /// right singular vectors. - /// The work array. For real matrices, the work array should be at least - /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). - /// On exit, work[0] contains the optimal work size value. - /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt, float[] work) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (u == null) - { - throw new ArgumentNullException("u"); - } - - if (vt == null) - { - throw new ArgumentNullException("vt"); - } - - if (work == null) - { - throw new ArgumentNullException("work"); - } - - if (u.Length != rowsA * rowsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); - } - - if (vt.Length != columnsA * columnsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); - } - - if (s.Length != Math.Min(rowsA, columnsA)) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); - } - - if (work.Length == 0) - { - throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); - } - - if (work.Length < rowsA) - { - work[0] = rowsA; - throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); - } - - const int Maxiter = 1000; - - var e = new float[columnsA]; - var v = new float[vt.Length]; - var stemp = new float[Math.Min(rowsA + 1, columnsA)]; - - int i, j, l, lp1; - - var cs = 0.0f; - var sn = 0.0f; - float t; - - var ncu = rowsA; - - // Reduce matrix to bidiagonal form, storing the diagonal elements - // in "s" and the super-diagonal elements in "e". - var nct = Math.Min(rowsA - 1, columnsA); - var nrt = Math.Max(0, Math.Min(columnsA - 2, rowsA)); - 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 vector s[l]. - var l1 = l; - stemp[l] = (float)Math.Sqrt(CommonParallel.Aggregate(l, rowsA, i1 => (a[(l1 * rowsA) + i1] * a[(l1 * rowsA) + i1]))); - - if (stemp[l] != 0.0) - { - if (a[(l * rowsA) + l] != 0.0) - { - stemp[l] = Math.Abs(stemp[l]) * (a[(l * rowsA) + l] / Math.Abs(a[(l * rowsA) + l])); - } - - // A part of column "l" of Matrix A from row "l" to end multiply by 1.0 / s[l] - for (i = l; i < rowsA; i++) - { - a[(l * rowsA) + i] = a[(l * rowsA) + i] * (1.0f / stemp[l]); - } - - a[(l * rowsA) + l] = 1.0f + a[(l * rowsA) + l]; - } - - stemp[l] = -stemp[l]; - } - - for (j = lp1; j < columnsA; j++) - { - if (l < nct) - { - if (stemp[l] != 0.0) - { - // Apply the transformation. - t = 0.0f; - for (i = l; i < rowsA; i++) - { - t += a[(j * rowsA) + i] * a[(l * rowsA) + i]; - } - - t = -t / a[(l * rowsA) + l]; - - for (var ii = l; ii < rowsA; ii++) - { - a[(j * rowsA) + ii] += t * a[(l * rowsA) + ii]; - } - } - } - - // Place the l-th row of matrix into "e" for the - // subsequent calculation of the row transformation. - e[j] = a[(j * rowsA) + l]; - } - - if (computeVectors && l < nct) - { - // Place the transformation in "u" for subsequent back multiplication. - for (i = l; i < rowsA; i++) - { - u[(l * rowsA) + i] = a[(l * rowsA) + i]; - } - } - - if (l >= nrt) - { - continue; - } - - // Compute the l-th row transformation and place the l-th super-diagonal in e(l). - var enorm = 0.0; - for (i = lp1; i < e.Length; i++) - { - enorm += e[i] * e[i]; - } - - e[l] = (float)Math.Sqrt(enorm); - if (e[l] != 0.0) - { - if (e[lp1] != 0.0) - { - e[l] = Math.Abs(e[l]) * (e[lp1] / Math.Abs(e[lp1])); - } - - // Scale vector "e" from "lp1" by 1.0 / e[l] - for (i = lp1; i < e.Length; i++) - { - e[i] = e[i] * (1.0f / e[l]); - } - - e[lp1] = 1.0f + e[lp1]; - } - - e[l] = -e[l]; - - if (lp1 < rowsA && e[l] != 0.0) - { - // Apply the transformation. - for (i = lp1; i < rowsA; i++) - { - work[i] = 0.0f; - } - - for (j = lp1; j < columnsA; j++) - { - for (var ii = lp1; ii < rowsA; ii++) - { - work[ii] += e[j] * a[(j * rowsA) + ii]; - } - } - - for (j = lp1; j < columnsA; j++) - { - var ww = -e[j] / e[lp1]; - for (var ii = lp1; ii < rowsA; ii++) - { - a[(j * rowsA) + ii] += ww * work[ii]; - } - } - } - - if (!computeVectors) - { - continue; - } - - // Place the transformation in v for subsequent back multiplication. - for (i = lp1; i < columnsA; i++) - { - v[(l * columnsA) + i] = e[i]; - } - } - - // Set up the final bidiagonal matrix or order m. - var m = Math.Min(columnsA, rowsA + 1); - var nctp1 = nct + 1; - var nrtp1 = nrt + 1; - if (nct < columnsA) - { - stemp[nctp1 - 1] = a[((nctp1 - 1) * rowsA) + (nctp1 - 1)]; - } - - if (rowsA < m) - { - stemp[m - 1] = 0.0f; - } - - if (nrtp1 < m) - { - e[nrtp1 - 1] = a[((m - 1) * rowsA) + (nrtp1 - 1)]; - } - - e[m - 1] = 0.0f; - - // If required, generate "u". - if (computeVectors) - { - for (j = nctp1 - 1; j < ncu; j++) - { - for (i = 0; i < rowsA; i++) - { - u[(j * rowsA) + i] = 0.0f; - } - - u[(j * rowsA) + j] = 1.0f; - } - - for (l = nct - 1; l >= 0; l--) - { - if (stemp[l] != 0.0) - { - for (j = l + 1; j < ncu; j++) - { - t = 0.0f; - for (i = l; i < rowsA; i++) - { - t += u[(j * rowsA) + i] * u[(l * rowsA) + i]; - } - - t = -t / u[(l * rowsA) + l]; - - for (var ii = l; ii < rowsA; ii++) - { - u[(j * rowsA) + ii] += t * u[(l * rowsA) + ii]; - } - } - - // A part of column "l" of matrix A from row "l" to end multiply by -1.0 - for (i = l; i < rowsA; i++) - { - u[(l * rowsA) + i] = u[(l * rowsA) + i] * -1.0f; - } - - u[(l * rowsA) + l] = 1.0f + u[(l * rowsA) + l]; - for (i = 0; i < l; i++) - { - u[(l * rowsA) + i] = 0.0f; - } - } - else - { - for (i = 0; i < rowsA; i++) - { - u[(l * rowsA) + i] = 0.0f; - } - - u[(l * rowsA) + l] = 1.0f; - } - } - } - - // If it is required, generate v. - if (computeVectors) - { - for (l = columnsA - 1; l >= 0; l--) - { - lp1 = l + 1; - if (l < nrt) - { - if (e[l] != 0.0) - { - for (j = lp1; j < columnsA; j++) - { - t = 0.0f; - for (i = lp1; i < columnsA; i++) - { - t += v[(j * columnsA) + i] * v[(l * columnsA) + i]; - } - - t = -t / v[(l * columnsA) + lp1]; - for (var ii = l; ii < columnsA; ii++) - { - v[(j * columnsA) + ii] += t * v[(l * columnsA) + ii]; - } - } - } - } - - for (i = 0; i < columnsA; i++) - { - v[(l * columnsA) + i] = 0.0f; - } - - v[(l * columnsA) + l] = 1.0f; - } - } - - // Transform "s" and "e" so that they are double - for (i = 0; i < m; i++) - { - float r; - if (stemp[i] != 0.0) - { - t = stemp[i]; - r = stemp[i] / t; - stemp[i] = t; - if (i < m - 1) - { - e[i] = e[i] / r; - } - - if (computeVectors) - { - // A part of column "i" of matrix U from row 0 to end multiply by r - for (j = 0; j < rowsA; j++) - { - u[(i * rowsA) + j] = u[(i * rowsA) + j] * r; - } - } - } - - // Exit - if (i == m - 1) - { - break; - } - - if (e[i] == 0.0) - { - continue; - } - - t = e[i]; - r = t / e[i]; - e[i] = t; - stemp[i + 1] = stemp[i + 1] * r; - if (!computeVectors) - { - continue; - } - - // A part of column "i+1" of matrix VT from row 0 to end multiply by r - for (j = 0; j < columnsA; j++) - { - v[((i + 1) * columnsA) + j] = v[((i + 1) * columnsA) + j] * 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. - 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 mS[m] and e[l-1] are negligible and l < m - // kase = 2: if mS[l] is negligible and l < m - // kase = 3: if e[l-1] is negligible, l < m, and mS[l, ..., mS[m] are not negligible (qr step). - // kase = 4: if e[m-1] is negligible (convergence). - double ztest; - double test; - for (l = m - 2; l >= 0; l--) - { - test = Math.Abs(stemp[l]) + Math.Abs(stemp[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.0; - 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(stemp[ls]); - if (ztest.AlmostEqualInDecimalPlaces(test, 7)) - { - stemp[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 s[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 = stemp[k]; - - Drotg(ref t1, ref f, ref cs, ref sn); - stemp[k] = t1; - if (k != l) - { - f = -sn * e[k - 1]; - e[k - 1] = cs * e[k - 1]; - } - - if (computeVectors) - { - // Rotate - for (i = 0; i < columnsA; i++) - { - var z = (cs * v[(k * columnsA) + i]) + (sn * v[((m - 1) * columnsA) + i]); - v[((m - 1) * columnsA) + i] = (cs * v[((m - 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); - v[(k * columnsA) + i] = z; - } - } - } - - break; - - // Split at negligible s[l]. - case 2: - f = e[l - 1]; - e[l - 1] = 0.0f; - for (k = l; k < m; k++) - { - t1 = stemp[k]; - Drotg(ref t1, ref f, ref cs, ref sn); - stemp[k] = t1; - f = -sn * e[k]; - e[k] = cs * e[k]; - if (computeVectors) - { - // Rotate - for (i = 0; i < rowsA; i++) - { - var z = (cs * u[(k * rowsA) + i]) + (sn * u[((l - 1) * rowsA) + i]); - u[((l - 1) * rowsA) + i] = (cs * u[((l - 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); - u[(k * rowsA) + i] = z; - } - } - } - - break; - - // Perform one qr step. - case 3: - - // calculate the shift. - var scale = 0.0f; - scale = Math.Max(scale, Math.Abs(stemp[m - 1])); - scale = Math.Max(scale, Math.Abs(stemp[m - 2])); - scale = Math.Max(scale, Math.Abs(e[m - 2])); - scale = Math.Max(scale, Math.Abs(stemp[l])); - scale = Math.Max(scale, Math.Abs(e[l])); - var sm = stemp[m - 1] / scale; - var smm1 = stemp[m - 2] / scale; - var emm1 = e[m - 2] / scale; - var sl = stemp[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 * stemp[k]) + (sn * e[k]); - e[k] = (cs * e[k]) - (sn * stemp[k]); - g = sn * stemp[k + 1]; - stemp[k + 1] = cs * stemp[k + 1]; - if (computeVectors) - { - for (i = 0; i < columnsA; i++) - { - var z = (cs * v[(k * columnsA) + i]) + (sn * v[((k + 1) * columnsA) + i]); - v[((k + 1) * columnsA) + i] = (cs * v[((k + 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); - v[(k * columnsA) + i] = z; - } - } - - Drotg(ref f, ref g, ref cs, ref sn); - stemp[k] = f; - f = (cs * e[k]) + (sn * stemp[k + 1]); - stemp[k + 1] = -(sn * e[k]) + (cs * stemp[k + 1]); - g = sn * e[k + 1]; - e[k + 1] = cs * e[k + 1]; - if (computeVectors && k < rowsA) - { - for (i = 0; i < rowsA; i++) - { - var z = (cs * u[(k * rowsA) + i]) + (sn * u[((k + 1) * rowsA) + i]); - u[((k + 1) * rowsA) + i] = (cs * u[((k + 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); - u[(k * rowsA) + i] = z; - } - } - } - - e[m - 2] = f; - iter = iter + 1; - break; - - // Convergence - case 4: - - // Make the singular value positive - if (stemp[l] < 0.0) - { - stemp[l] = -stemp[l]; - if (computeVectors) - { - // A part of column "l" of matrix VT from row 0 to end multiply by -1 - for (i = 0; i < columnsA; i++) - { - v[(l * columnsA) + i] = v[(l * columnsA) + i] * -1.0f; - } - } - } - - // Order the singular value. - while (l != mn - 1) - { - if (stemp[l] >= stemp[l + 1]) - { - break; - } - - t = stemp[l]; - stemp[l] = stemp[l + 1]; - stemp[l + 1] = t; - if (computeVectors && l < columnsA) - { - // Swap columns l, l + 1 - for (i = 0; i < columnsA; i++) - { - var z = v[(l * columnsA) + i]; - v[(l * columnsA) + i] = v[((l + 1) * columnsA) + i]; - v[((l + 1) * columnsA) + i] = z; - } - } - - if (computeVectors && l < rowsA) - { - // Swap columns l, l + 1 - for (i = 0; i < rowsA; i++) - { - var z = u[(l * rowsA) + i]; - u[(l * rowsA) + i] = u[((l + 1) * rowsA) + i]; - u[((l + 1) * rowsA) + i] = z; - } - } - - l = l + 1; - } - - iter = 0; - m = m - 1; - break; - } - } - - if (computeVectors) - { - // Finally transpose "v" to get "vt" matrix - for (i = 0; i < columnsA; i++) - { - for (j = 0; j < columnsA; j++) - { - vt[(j * columnsA) + i] = v[(i * columnsA) + j]; - } - } - } - - // Copy stemp to s with size adjustment. We are using ported copy of linpack's svd code and it uses - // a singular vector of length rows+1 when rows < columns. The last element is not used and needs to be removed. - // We should port lapack's svd routine to remove this problem. - Buffer.BlockCopy(stemp, 0, s, 0, Math.Min(rowsA, columnsA) * Constants.SizeOfFloat); - - // On return the first element of the work array stores the min size of the work array could have been - // work[0] = Math.Max(3 * Math.Min(aRows, aColumns) + Math.Max(aRows, aColumns), 5 * Math.Min(aRows, aColumns)); - work[0] = rowsA; - } - - /// - /// Given the Cartesian coordinates (da, db) of a point p, these fucntion return the parameters da, db, c, and s - /// associated with the Givens rotation that zeros the y-coordinate of the point. - /// - /// Provides the x-coordinate of the point p. On exit contains the parameter r associated with the Givens rotation - /// Provides the y-coordinate of the point p. On exit contains the parameter z associated with the Givens rotation - /// Contains the parameter c associated with the Givens rotation - /// Contains the parameter s associated with the Givens rotation - /// This is equivalent to the DROTG LAPACK routine. - private static void Drotg(ref float da, ref float db, ref float c, ref float s) - { - float r, z; - - var roe = db; - var absda = Math.Abs(da); - var absdb = Math.Abs(db); - if (absda > absdb) - { - roe = da; - } - - var scale = absda + absdb; - if (scale == 0.0) - { - c = 1.0f; - s = 0.0f; - r = 0.0f; - z = 0.0f; - } - else - { - var sda = da / scale; - var sdb = db / scale; - r = scale * (float)Math.Sqrt((sda * sda) + (sdb * sdb)); - if (roe < 0.0) - { - r = -r; - } - - c = da / r; - s = db / r; - z = 1.0f; - if (absda > absdb) - { - z = s; - } - - if (absdb >= absda && c != 0.0) - { - z = 1.0f / c; - } - } - - da = r; - db = z; - } - - /// - /// Solves A*X=B for X using the singular value decomposition of A. - /// - /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The singular values of A in ascending value. - /// On exit U contains the left singular vectors. - /// On exit VT contains the transposed right singular vectors. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - public void SvdSolve(float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt, float[] b, int columnsB, float[] x) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (u == null) - { - throw new ArgumentNullException("u"); - } - - if (vt == null) - { - throw new ArgumentNullException("vt"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (u.Length != rowsA * rowsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); - } - - if (vt.Length != columnsA * columnsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); - } - - if (s.Length != Math.Min(rowsA, columnsA)) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); - } - - if (b.Length != rowsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - // Actually "work = new float[aRows]" is acceptable size of work array. I set size proposed in method description - var work = new float[Math.Max((3 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA), 5 * Math.Min(rowsA, columnsA))]; - SvdSolve(a, rowsA, columnsA, s, u, vt, b, columnsB, x, work); - } - - /// - /// Solves A*X=B for X using the singular value decomposition of A. - /// - /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The singular values of A in ascending value. - /// On exit U contains the left singular vectors. - /// On exit VT contains the transposed right singular vectors. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - /// The work array. For real matrices, the work array should be at least - /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). - /// On exit, work[0] contains the optimal work size value. - public void SvdSolve(float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt, float[] b, int columnsB, float[] x, float[] work) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (u == null) - { - throw new ArgumentNullException("u"); - } - - if (vt == null) - { - throw new ArgumentNullException("vt"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (u.Length != rowsA * rowsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); - } - - if (vt.Length != columnsA * columnsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); - } - - if (s.Length != Math.Min(rowsA, columnsA)) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); - } - - if (b.Length != rowsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (work.Length == 0) - { - throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); - } - - if (work.Length < rowsA) - { - work[0] = rowsA; - throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); - } - - SingularValueDecomposition(true, a, rowsA, columnsA, s, u, vt, work); - SvdSolveFactored(rowsA, columnsA, s, u, vt, b, columnsB, x); - } - - /// - /// Solves A*X=B for X using a previously SVD decomposed matrix. - /// - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The s values returned by . - /// The left singular vectors returned by . - /// The right singular vectors returned by . - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - public void SvdSolveFactored(int rowsA, int columnsA, float[] s, float[] u, float[] vt, float[] b, int columnsB, float[] x) - { - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (u == null) - { - throw new ArgumentNullException("u"); - } - - if (vt == null) - { - throw new ArgumentNullException("vt"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (u.Length != rowsA * rowsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); - } - - if (vt.Length != columnsA * columnsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); - } - - if (s.Length != Math.Min(rowsA, columnsA)) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); - } - - if (b.Length != rowsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - var mn = Math.Min(rowsA, columnsA); - var tmp = new float[columnsA]; - - for (var k = 0; k < columnsB; k++) - { - for (var j = 0; j < columnsA; j++) - { - float value = 0; - if (j < mn) - { - for (var i = 0; i < rowsA; i++) - { - value += u[(j * rowsA) + i] * b[(k * rowsA) + i]; - } - - value /= s[j]; - } - - tmp[j] = value; - } - - for (var j = 0; j < columnsA; j++) - { - float value = 0; - for (var i = 0; i < columnsA; i++) - { - value += vt[(j * columnsA) + i] * tmp[i]; - } - - x[(k * columnsA) + j] = value; - } - } - } - - #endregion - - #region ILinearAlgebraProvider Members - - /// - /// Adds a scaled vector to another: y += alpha*x. - /// - /// The vector to update. - /// The value to scale by. - /// The vector to add to . - /// This equivalent to the AXPY BLAS routine. - public void AddVectorToScaledVector(Complex[] y, Complex alpha, Complex[] x) - { - 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 (alpha == 0.0) - { - return; - } - - if (alpha == 1.0) - { - CommonParallel.For(0, y.Length, i => y[i] += x[i]); - } - else - { - CommonParallel.For(0, y.Length, i => y[i] += alpha * x[i]); - } - } - - /// - /// Scales an array. Can be used to scale a vector and a matrix. - /// - /// The scalar. - /// The values to scale. - /// This is equivalent to the SCAL BLAS routine. - public void ScaleArray(Complex alpha, Complex[] x) - { - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (alpha.IsOne()) - { - return; - } - - CommonParallel.For(0, x.Length, i => x[i] = alpha * x[i]); - } - - /// - /// Computes the dot product of x and y. - /// - /// The vector x. - /// The vector y. - /// The dot product of x and y. - /// This is equivalent to the DOT BLAS routine. - public Complex DotProduct(Complex[] x, Complex[] y) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (y.Length != x.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } - - return CommonParallel.Aggregate(0, y.Length, index => y[index] * x[index]); - } - - /// - /// Does a point wise add of two arrays z = x + y. This can be used - /// to add vectors or matrices. - /// - /// The array x. - /// The array y. - /// The result of the addition. - /// There is no equivalent BLAS routine, but many libraries - /// provide optimized (parallel and/or vectorized) versions of this - /// routine. - public void AddArrays(Complex[] x, Complex[] y, Complex[] result) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (y.Length != x.Length || y.Length != result.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } - - CommonParallel.For(0, y.Length, i => result[i] = x[i] + y[i]); - } - - /// - /// Does a point wise subtraction of two arrays z = x - y. This can be used - /// to subtract vectors or matrices. - /// - /// The array x. - /// The array y. - /// The result of the subtraction. - /// There is no equivalent BLAS routine, but many libraries - /// provide optimized (parallel and/or vectorized) versions of this - /// routine. - public void SubtractArrays(Complex[] x, Complex[] y, Complex[] result) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (y.Length != x.Length || y.Length != result.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } - - CommonParallel.For(0, y.Length, i => result[i] = x[i] - y[i]); - } - - /// - /// Does a point wise multiplication of two arrays z = x * y. This can be used - /// to multiple elements of vectors or matrices. - /// - /// The array x. - /// The array y. - /// The result of the point wise multiplication. - /// There is no equivalent BLAS routine, but many libraries - /// provide optimized (parallel and/or vectorized) versions of this - /// routine. - public void PointWiseMultiplyArrays(Complex[] x, Complex[] y, Complex[] result) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (y.Length != x.Length || y.Length != result.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } - - CommonParallel.For(0, y.Length, i => result[i] = x[i] * y[i]); - } - - /// - /// Does a point wise division of two arrays z = x / y. This can be used - /// to divide elements of vectors or matrices. - /// - /// The array x. - /// The array y. - /// The result of the point wise division. - /// There is no equivalent BLAS routine, but many libraries - /// provide optimized (parallel and/or vectorized) versions of this - /// routine. - public void PointWiseDivideArrays(Complex[] x, Complex[] y, Complex[] result) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (y.Length != x.Length || y.Length != result.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } - - CommonParallel.For(0, y.Length, index => { result[index] = x[index] / y[index]; }); - } - - /// - /// Computes the requested of the matrix. - /// - /// The type of norm to compute. - /// The number of rows. - /// The number of columns. - /// The matrix to compute the norm from. - /// - /// The requested of the matrix. - /// - public Complex MatrixNorm(Norm norm, int rows, int columns, Complex[] matrix) - { - throw new NotImplementedException(); - } - - /// - /// Computes the requested of the matrix. - /// - /// The type of norm to compute. - /// The number of rows. - /// The number of columns. - /// The matrix to compute the norm from. - /// The work array. Only used when - /// and needs to be have a length of at least M (number of rows of . - /// - /// The requested of the matrix. - /// - public Complex MatrixNorm(Norm norm, int rows, int columns, Complex[] matrix, Complex[] work) - { - throw new NotImplementedException(); - } - - /// - /// Multiples two matrices. result = x * y - /// - /// The x matrix. - /// The number of rows in the x matrix. - /// The number of columns in the x matrix. - /// The y matrix. - /// The number of rows in the y matrix. - /// The number of columns in the y matrix. - /// Where to store the result of the multiplication. - /// This is a simplified version of the BLAS GEMM routine with alpha - /// set to 1.0 and beta set to 0.0, and x and y are not transposed. - public void MatrixMultiply(Complex[] x, int rowsX, int columnsX, Complex[] y, int rowsY, int columnsY, Complex[] result) - { - // First check some basic requirement on the parameters of the matrix multiplication. - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (rowsX * columnsX != x.Length) - { - throw new ArgumentException("x.Length != xRows * xColumns"); - } - - if (rowsY * columnsY != y.Length) - { - throw new ArgumentException("y.Length != yRows * yColumns"); - } - - if (columnsX != rowsY) - { - throw new ArgumentException("xColumns != yRows"); - } - - if (rowsX * columnsY != result.Length) - { - throw new ArgumentException("xRows * yColumns != result.Length"); - } - - // Check whether we will be overwriting any of our inputs and make copies if necessary. - // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory - // as result, we can do it on a row wise basis. We should investigate this. - Complex[] xdata; - if (ReferenceEquals(x, result)) - { - xdata = (Complex[])x.Clone(); - } - else - { - xdata = x; - } - - Complex[] ydata; - if (ReferenceEquals(y, result)) - { - ydata = (Complex[])y.Clone(); - } - else - { - ydata = y; - } - - // Start the actual matrix multiplication. - // TODO - For small matrices we should get rid of the parallelism because of startup costs. - // Perhaps the following implementations would be a good one - // http://blog.feradz.com/2009/01/cache-efficient-matrix-multiplication/ - MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, Complex.One, xdata, rowsX, columnsX, ydata, rowsY, columnsY, Complex.Zero, result); - } - - /// - /// Multiplies two matrices and updates another with the result. c = alpha*op(a)*op(b) + beta*c - /// - /// How to transpose the matrix. - /// How to transpose the matrix. - /// The value to scale matrix. - /// The a matrix. - /// The number of rows in the matrix. - /// The number of columns in the matrix. - /// The b matrix - /// The number of rows in the matrix. - /// The number of columns in the matrix. - /// The value to scale the matrix. - /// The c matrix. - public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, Complex alpha, Complex[] a, int rowsA, int columnsA, Complex[] b, int rowsB, int columnsB, Complex beta, Complex[] c) - { - // Choose nonsensical values for the number of rows in c; fill them in depending - // on the operations on a and b. - int rowsC; - - // First check some basic requirement on the parameters of the matrix multiplication. - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if ((int)transposeA > 111 && (int)transposeB > 111) - { - if (rowsA != columnsB) - { - throw new ArgumentOutOfRangeException(); - } - - if (columnsA * rowsB != c.Length) - { - throw new ArgumentOutOfRangeException(); - } - - rowsC = columnsA; - } - else if ((int)transposeA > 111) - { - if (rowsA != rowsB) - { - throw new ArgumentOutOfRangeException(); - } - - if (columnsA * columnsB != c.Length) - { - throw new ArgumentOutOfRangeException(); - } - - rowsC = columnsA; - } - else if ((int)transposeB > 111) - { - if (columnsA != columnsB) - { - throw new ArgumentOutOfRangeException(); - } - - if (rowsA * rowsB != c.Length) - { - throw new ArgumentOutOfRangeException(); - } - - rowsC = rowsA; - } - else - { - if (columnsA != rowsB) - { - throw new ArgumentOutOfRangeException(); - } - - if (rowsA * columnsB != c.Length) - { - throw new ArgumentOutOfRangeException(); - } - - rowsC = rowsA; - } - - if (alpha.IsZero() && beta.IsZero()) - { - Array.Clear(c, 0, c.Length); - return; - } - - // Check whether we will be overwriting any of our inputs and make copies if necessary. - // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory - // as result, we can do it on a row wise basis. We should investigate this. - Complex[] adata; - if (ReferenceEquals(a, c)) - { - adata = (Complex[])a.Clone(); - } - else - { - adata = a; - } - - Complex[] bdata; - if (ReferenceEquals(b, c)) - { - bdata = (Complex[])b.Clone(); - } - else - { - bdata = b; - } - - if (alpha.IsOne()) - { - if (beta.IsZero()) - { - if ((int)transposeA > 111 && (int)transposeB > 111) - { - CommonParallel.For( - 0, - columnsA, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsB; i++) - { - var iIndex = i * rowsA; - Complex s = 0; - for (var l = 0; l != columnsB; l++) - { - s += adata[iIndex + l] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = s; - } - }); - } - else if ((int)transposeA > 111) - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != columnsA; i++) - { - var iIndex = i * rowsA; - Complex s = 0; - for (var l = 0; l != rowsA; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s; - } - }); - } - else if ((int)transposeB > 111) - { - CommonParallel.For( - 0, - rowsB, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsA; i++) - { - Complex s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = s; - } - }); - } - else - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != rowsA; i++) - { - Complex s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s; - } - }); - } - } - else - { - if ((int)transposeA > 111 && (int)transposeB > 111) - { - CommonParallel.For( - 0, - columnsA, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsB; i++) - { - var iIndex = i * rowsA; - Complex s = 0; - for (var l = 0; l != columnsB; l++) - { - s += adata[iIndex + l] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = (c[jIndex + i] * beta) + s; - } - }); - } - else if ((int)transposeA > 111) - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != columnsA; i++) - { - var iIndex = i * rowsA; - Complex s = 0; - for (var l = 0; l != rowsA; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s + (c[jcIndex + i] * beta); - } - }); - } - else if ((int)transposeB > 111) - { - CommonParallel.For( - 0, - rowsB, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsA; i++) - { - Complex s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = s + (c[jIndex + i] * beta); - } - }); - } - else - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != rowsA; i++) - { - Complex s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s + (c[jcIndex + i] * beta); - } - }); - } - } - } - else - { - if ((int)transposeA > 111 && (int)transposeB > 111) - { - CommonParallel.For( - 0, - columnsA, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsB; i++) - { - var iIndex = i * rowsA; - Complex s = 0; - for (var l = 0; l != columnsB; l++) - { - s += adata[iIndex + l] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = (c[jIndex + i] * beta) + (alpha * s); - } - }); - } - else if ((int)transposeA > 111) - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != columnsA; i++) - { - var iIndex = i * rowsA; - Complex s = 0; - for (var l = 0; l != rowsA; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); - } - }); - } - else if ((int)transposeB > 111) - { - CommonParallel.For( - 0, - rowsB, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsA; i++) - { - Complex s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = (alpha * s) + (c[jIndex + i] * beta); - } - }); - } - else - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != rowsA; i++) - { - Complex s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); - } - }); - } - } - } - - /// - /// Computes the LUP factorization of A. P*A = L*U. - /// - /// An by matrix. The matrix is overwritten with the - /// the LU factorization on exit. The lower triangular factor L is stored in under the diagonal of (the diagonal is always 1.0 - /// for the L factor). The upper triangular factor U is stored on and above the diagonal of . - /// The order of the square matrix . - /// On exit, it contains the pivot indices. The size of the array must be . - /// This is equivalent to the GETRF LAPACK routine. - public void LUFactor(Complex[] data, int order, int[] ipiv) - { - if (data == null) - { - throw new ArgumentNullException("data"); - } - - if (ipiv == null) - { - throw new ArgumentNullException("ipiv"); - } - - if (data.Length != order * order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "data"); - } - - if (ipiv.Length != order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); - } - - // Initialize the pivot matrix to the identity permutation. - for (var i = 0; i < order; i++) - { - ipiv[i] = i; - } - - var vecLUcolj = new Complex[order]; - - // Outer loop. - for (var j = 0; j < order; j++) - { - var indexj = j * order; - var indexjj = indexj + j; - - // Make a copy of the j-th column to localize references. - for (var i = 0; i < order; i++) - { - vecLUcolj[i] = data[indexj + i]; - } - - // Apply previous transformations. - for (var i = 0; i < order; i++) - { - // Most of the time is spent in the following dot product. - var kmax = Math.Min(i, j); - var s = Complex.Zero; - for (var k = 0; k < kmax; k++) - { - s += data[(k * order) + i] * vecLUcolj[k]; - } - - data[indexj + i] = vecLUcolj[i] -= s; - } - - // Find pivot and exchange if necessary. - var p = j; - for (var i = j + 1; i < order; i++) - { - if (vecLUcolj[i].Magnitude > vecLUcolj[p].Magnitude) - { - p = i; - } - } - - if (p != j) - { - for (var k = 0; k < order; k++) - { - var indexk = k * order; - var indexkp = indexk + p; - var indexkj = indexk + j; - var temp = data[indexkp]; - data[indexkp] = data[indexkj]; - data[indexkj] = temp; - } - - ipiv[j] = p; - } - - // Compute multipliers. - if (j < order & data[indexjj] != 0.0) - { - for (var i = j + 1; i < order; i++) - { - data[indexj + i] /= data[indexjj]; - } - } - } - } - - /// - /// Computes the inverse of matrix using LU factorization. - /// - /// The N by N matrix to invert. Contains the inverse On exit. - /// The order of the square matrix . - /// This is equivalent to the GETRF and GETRI LAPACK routines. - public void LUInverse(Complex[] a, int order) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (a.Length != order * order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); - } - - var ipiv = new int[order]; - LUFactor(a, order, ipiv); - LUInverseFactored(a, order, ipiv); - } - - /// - /// Computes the inverse of a previously factored matrix. - /// - /// The LU factored N by N matrix. Contains the inverse On exit. - /// The order of the square matrix . - /// The pivot indices of . - /// This is equivalent to the GETRI LAPACK routine. - public 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); - CommonParallel.For(0, a.Length, index => a[index] = inverse[index]); - } - - /// - /// Computes the inverse of matrix using LU factorization. - /// - /// The N by N matrix to invert. Contains the inverse On exit. - /// The order of the square matrix . - /// 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. - /// This is equivalent to the GETRF and GETRI LAPACK routines. - public void LUInverse(Complex[] a, int order, Complex[] work) - { - LUInverse(a, order); - } - - /// - /// Computes the inverse of a previously factored matrix. - /// - /// The LU factored N by N matrix. Contains the inverse On exit. - /// The order of the square matrix . - /// The pivot indices of . - /// 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. - /// This is equivalent to the GETRI LAPACK routine. - public void LUInverseFactored(Complex[] a, int order, int[] ipiv, Complex[] work) - { - LUInverseFactored(a, order, ipiv); - } - - /// - /// Solves A*X=B for X using LU factorization. - /// - /// The number of columns of B. - /// The square matrix A. - /// The order of the square matrix . - /// The B matrix. - /// This is equivalent to the GETRF and GETRS LAPACK routines. - public 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"); - } - - var ipiv = new int[order]; - LUFactor(a, order, ipiv); - LUSolveFactored(columnsOfB, a, order, ipiv, b); - } - - /// - /// Solves A*X=B for X using a previously factored A matrix. - /// - /// The number of columns of B. - /// The factored A matrix. - /// The order of the square matrix . - /// The pivot indices of . - /// The B matrix. - /// This is equivalent to the GETRS LAPACK routine. - public void LUSolveFactored(int columnsOfB, Complex[] a, int order, int[] ipiv, Complex[] b) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (ipiv == null) - { - throw new ArgumentNullException("ipiv"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (a.Length != order * order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); - } - - if (ipiv.Length != order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); - } - - if (b.Length != order * columnsOfB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - // Compute the column vector P*B - for (var i = 0; i < ipiv.Length; i++) - { - if (ipiv[i] == i) - { - continue; - } - - var p = ipiv[i]; - for (var j = 0; j < columnsOfB; j++) - { - var indexk = j * order; - var indexkp = indexk + p; - var indexkj = indexk + i; - var temp = b[indexkp]; - b[indexkp] = b[indexkj]; - b[indexkj] = temp; - } - } - - // Solve L*Y = P*B - for (var k = 0; k < order; k++) - { - var korder = k * order; - for (var i = k + 1; i < order; i++) - { - for (var j = 0; j < columnsOfB; j++) - { - var index = j * order; - b[i + index] -= b[k + index] * a[i + korder]; - } - } - } - - // Solve U*X = Y; - for (var k = order - 1; k >= 0; k--) - { - var korder = k + (k * order); - for (var j = 0; j < columnsOfB; j++) - { - b[k + (j * order)] /= a[korder]; - } - - korder = k * order; - for (var i = 0; i < k; i++) - { - for (var j = 0; j < columnsOfB; j++) - { - var index = j * order; - b[i + index] -= b[k + index] * a[i + korder]; - } - } - } - } - - /// - /// Solves A*X=B for X using LU factorization. - /// - /// How to transpose the matrix. - /// The number of columns of B. - /// The square matrix A. - /// The order of the square matrix . - /// The B matrix. - /// This is equivalent to the GETRF and GETRS LAPACK routines. - public void LUSolve(Transpose transposeA, 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"); - } - - var ipiv = new int[order]; - LUFactor(a, order, ipiv); - LUSolveFactored(transposeA, columnsOfB, a, order, ipiv, b); - } - - /// - /// Solves A*X=B for X using a previously factored A matrix. - /// - /// How to transpose the matrix. - /// The number of columns of B. - /// The factored A matrix. - /// The order of the square matrix . - /// The pivot indices of . - /// The B matrix. - /// This is equivalent to the GETRS LAPACK routine. - public void LUSolveFactored(Transpose transposeA, int columnsOfB, Complex[] a, int order, int[] ipiv, Complex[] b) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (ipiv == null) - { - throw new ArgumentNullException("ipiv"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (a.Length != order * order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); - } - - if (ipiv.Length != order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); - } - - if (b.Length != order * columnsOfB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (transposeA == Transpose.Transpose) - { - var aT = new Complex[a.Length]; - for (var i = 0; i < order; i++) - { - for (var j = 0; j < order; j++) - { - aT[(j * order) + i] = a[(i * order) + j]; - } - } - - LUSolveFactored(columnsOfB, aT, order, ipiv, b); - } - else if (transposeA == Transpose.ConjugateTranspose) - { - var acT = new Complex[a.Length]; - for (var i = 0; i < order; i++) - { - for (var j = 0; j < order; j++) - { - acT[(j * order) + i] = a[(i * order) + j].Conjugate(); - } - } - - LUSolveFactored(columnsOfB, acT, order, ipiv, b); - } - else - { - LUSolveFactored(columnsOfB, a, order, ipiv, b); - } - } - - /// - /// Computes the Cholesky factorization of A. - /// - /// On entry, a square, positive definite matrix. On exit, the matrix is overwritten with the - /// the Cholesky factorization. - /// The number of rows or columns in the matrix. - /// This is equivalent to the POTRF LAPACK routine. - public void CholeskyFactor(Complex[] a, int order) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - var tmpColumn = new Complex[order]; - - // Main loop - along the diagonal - for (var ij = 0; ij < order; ij++) - { - // "Pivot" element - var tmpVal = a[(ij * order) + ij]; - - if (tmpVal.Real > 0.0) - { - tmpVal = tmpVal.SquareRoot(); - a[(ij * order) + ij] = tmpVal; - tmpColumn[ij] = tmpVal; - - // Calculate multipliers and copy to local column - // Current column, below the diagonal - for (var i = ij + 1; i < order; i++) - { - a[(ij * order) + i] /= tmpVal; - tmpColumn[i] = a[(ij * order) + i]; - } - - // Remaining columns, below the diagonal - DoCholeskyStep(a, order, ij + 1, order, tmpColumn, Control.NumberOfParallelWorkerThreads); - } - else - { - throw new ArgumentException(Resources.ArgumentMatrixPositiveDefinite); - } - - for (var i = ij + 1; i < order; i++) - { - a[(i * order) + ij] = 0.0; - } - } - } - - /// - /// Calculate Cholesky step - /// - /// Factor matrix - /// Number of rows - /// Column start - /// Total columns - /// Multipliears calculated previously - /// Number of available processors - private static void DoCholeskyStep(Complex[] data, int rowDim, int firstCol, int colLimit, Complex[] multipliers, int availableCores) - { - var tmpColCount = colLimit - firstCol; - - if ((availableCores > 1) && (tmpColCount > 200)) - { - var tmpSplit = firstCol + (tmpColCount / 3); - var tmpCores = availableCores / 2; - - CommonParallel.Invoke( - () => DoCholeskyStep(data, rowDim, firstCol, tmpSplit, multipliers, tmpCores), - () => DoCholeskyStep(data, rowDim, tmpSplit, colLimit, multipliers, tmpCores)); - } - else - { - for (var j = firstCol; j < colLimit; j++) - { - var tmpVal = multipliers[j]; - for (var i = j; i < rowDim; i++) - { - data[(j * rowDim) + i] -= multipliers[i] * tmpVal.Conjugate(); - } - } - } - } - - /// - /// Solves A*X=B for X using Cholesky factorization. - /// - /// The square, positive definite matrix A. - /// The number of rows and columns in A. - /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. - /// This is equivalent to the POTRF add POTRS LAPACK routines. - public void CholeskySolve(Complex[] a, int orderA, Complex[] b, int rowsB, int columnsB) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (orderA != rowsB) - { - throw new ArgumentException(Resources.ArgumentMatrixDimensions); - } - - if (ReferenceEquals(a, b)) - { - throw new ArgumentException(Resources.ArgumentReferenceDifferent); - } - - CholeskyFactor(a, orderA); - CholeskySolveFactored(a, orderA, b, rowsB, columnsB); - } - - /// - /// Solves A*X=B for X using a previously factored A matrix. - /// - /// The square, positive definite matrix A. - /// The number of rows and columns in A. - /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. - /// This is equivalent to the POTRS LAPACK routine. - public void CholeskySolveFactored(Complex[] a, int orderA, Complex[] b, int rowsB, int columnsB) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (orderA != rowsB) - { - throw new ArgumentException(Resources.ArgumentMatrixDimensions); - } - - if (ReferenceEquals(a, b)) - { - throw new ArgumentException(Resources.ArgumentReferenceDifferent); - } - - CommonParallel.For( - 0, - columnsB, - c => - { - var cindex = c * orderA; - - // Solve L*Y = B; - Complex sum; - for (var i = 0; i < orderA; i++) - { - sum = b[cindex + i]; - for (var k = i - 1; k >= 0; k--) - { - sum -= a[(k * orderA) + i] * b[cindex + k]; - } - - b[cindex + i] = sum / a[(i * orderA) + i]; - } - - // Solve L'*X = Y; - for (var i = orderA - 1; i >= 0; i--) - { - sum = b[cindex + i]; - var iindex = i * orderA; - for (var k = i + 1; k < orderA; k++) - { - sum -= a[iindex + k].Conjugate() * b[cindex + k]; - } - - b[cindex + i] = sum / a[iindex + i]; - } - }); - } - - /// - /// Computes the QR factorization of A. - /// - /// On entry, it is the M by N A matrix to factor. On exit, - /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// On exit, A M by M matrix that holds the Q matrix of the - /// QR factorization. - /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(Complex[] r, int rowsR, int columnsR, Complex[] q) - { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (r.Length != rowsR * columnsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); - } - - if (q.Length != rowsR * rowsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); - } - - var work = new Complex[rowsR * rowsR]; - QRFactor(r, rowsR, columnsR, q, work); - } - - /// - /// Computes the QR factorization of A. - /// - /// On entry, it is the M by N A matrix to factor. On exit, - /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// On exit, A M by M matrix that holds the Q matrix of the - /// QR factorization. - /// 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. - /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(Complex[] r, int rowsR, int columnsR, Complex[] q, Complex[] work) - { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (work == null) - { - throw new ArgumentNullException("q"); - } - - if (r.Length != rowsR * columnsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); - } - - if (q.Length != rowsR * rowsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); - } - - if (work.Length < rowsR * rowsR) - { - work[0] = rowsR * rowsR; - throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); - } - - CommonParallel.For(0, rowsR, i => q[(i * rowsR) + i] = Complex.One); - - var minmn = Math.Min(rowsR, columnsR); - for (var i = 0; i < minmn; i++) - { - GenerateColumn(work, r, rowsR, i, i); - ComputeQR(work, i, r, i, rowsR, i + 1, columnsR, Control.NumberOfParallelWorkerThreads); - } - - for (var i = minmn - 1; i >= 0; i--) - { - ComputeQR(work, i, q, i, rowsR, i, rowsR, Control.NumberOfParallelWorkerThreads); - } - - work[0] = rowsR * rowsR; - } - - #region QR Factor Helper functions - - /// - /// Perform calculation of Q or R - /// - /// Work array - /// Index of colunn in work array - /// Q or R matrices - /// The first row in - /// The last row - /// The first column - /// The last column - /// Number of available CPUs - private static void ComputeQR(Complex[] work, int workIndex, Complex[] a, int rowStart, int rowCount, int columnStart, int columnCount, int availableCores) - { - if (rowStart > rowCount || columnStart > columnCount) - { - return; - } - - var tmpColCount = columnCount - columnStart; - - if ((availableCores > 1) && (tmpColCount > 200)) - { - var tmpSplit = columnStart + (tmpColCount / 2); - var tmpCores = availableCores / 2; - - CommonParallel.Invoke( - () => ComputeQR(work, workIndex, a, rowStart, rowCount, columnStart, tmpSplit, tmpCores), - () => ComputeQR(work, workIndex, a, rowStart, rowCount, tmpSplit, columnCount, tmpCores)); - } - else - { - for (var j = columnStart; j < columnCount; j++) - { - var scale = Complex.Zero; - for (var i = rowStart; i < rowCount; i++) - { - scale += work[(workIndex * rowCount) + i - rowStart] * a[(j * rowCount) + i]; - } - - for (var i = rowStart; i < rowCount; i++) - { - a[(j * rowCount) + i] -= work[(workIndex * rowCount) + i - rowStart].Conjugate() * scale; - } - } - } - } - - /// - /// Generate column from initial matrix to work array - /// - /// Work array - /// Initial matrix - /// The number of rows in matrix - /// The firts row - /// Column index - private static void GenerateColumn(Complex[] work, Complex[] a, int rowCount, int row, int column) - { - var tmp = column * rowCount; - var index = tmp + row; - - CommonParallel.For( - row, - rowCount, - i => - { - var iIndex = tmp + i; - work[iIndex - row] = a[iIndex]; - a[iIndex] = Complex.Zero; - }); - - var norm = Complex.Zero; - for (var i = 0; i < rowCount - row; ++i) - { - var index1 = tmp + i; - norm += work[index1].Magnitude * work[index1].Magnitude; - } - - norm = norm.SquareRoot(); - if (row == rowCount - 1 || norm.Magnitude == 0) - { - a[index] = -work[tmp]; - work[tmp] = new Complex(2.0, 0).SquareRoot(); - return; - } - - if (work[tmp].Magnitude != 0.0) - { - norm = norm.Magnitude * (work[tmp] / work[tmp].Magnitude); - } - - a[index] = -norm; - CommonParallel.For(0, rowCount - row, i => work[tmp + i] /= norm); - work[tmp] += 1.0; - - var s = (1.0 / work[tmp]).SquareRoot(); - CommonParallel.For(0, rowCount - row, i => work[tmp + i] = work[tmp + i].Conjugate() * s); - } - - #endregion - - /// - /// Solves A*X=B for X using QR factorization of A. - /// - /// On entry, it is the M by N A matrix to factor. On exit, - /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// On exit, A M by M matrix that holds the Q matrix of the - /// QR factorization. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - public void QRSolve(Complex[] r, int rowsR, int columnsR, Complex[] q, Complex[] b, int columnsB, Complex[] x) - { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (b == null) - { - throw new ArgumentNullException("q"); - } - - if (x == null) - { - throw new ArgumentNullException("q"); - } - - if (r.Length != rowsR * columnsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); - } - - if (q.Length != rowsR * rowsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); - } - - if (b.Length != rowsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); - } - - var work = new Complex[rowsR * rowsR]; - QRSolve(r, rowsR, columnsR, q, b, columnsB, x, work); - } - - /// - /// Solves A*X=B for X using QR factorization of A. - /// - /// On entry, it is the M by N A matrix to factor. On exit, - /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// On exit, A M by M matrix that holds the Q matrix of the - /// QR factorization. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - /// 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. - public void QRSolve(Complex[] r, int rowsR, int columnsR, Complex[] q, Complex[] b, int columnsB, Complex[] x, Complex[] work) - { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (b == null) - { - throw new ArgumentNullException("q"); - } - - if (x == null) - { - throw new ArgumentNullException("q"); - } - - if (r.Length != rowsR * columnsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); - } - - if (q.Length != rowsR * rowsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); - } - - if (b.Length != rowsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); - } - - if (work.Length < rowsR * rowsR) - { - work[0] = rowsR * rowsR; - throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); - } - - QRFactor(r, rowsR, columnsR, q, work); - QRSolveFactored(q, r, rowsR, columnsR, b, columnsB, x); - - work[0] = rowsR * rowsR; - } - - /// - /// Solves A*X=B for X using a previously QR factored matrix. - /// - /// The Q matrix obtained by calling . - /// The R matrix obtained by calling . - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - public void QRSolveFactored(Complex[] q, Complex[] r, int rowsR, int columnsR, Complex[] b, int columnsB, Complex[] x) - { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (b == null) - { - throw new ArgumentNullException("q"); - } - - if (x == null) - { - throw new ArgumentNullException("q"); - } - - if (r.Length != rowsR * columnsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); - } - - if (q.Length != rowsR * rowsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); - } - - if (b.Length != rowsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); - } - - var sol = new Complex[b.Length]; - - // Copy B matrix to "sol", so B data will not be changed - CommonParallel.For(0, b.Length, index => sol[index] = b[index]); - - // Compute Y = transpose(Q)*B - var column = new Complex[rowsR]; - for (var j = 0; j < columnsB; j++) - { - var jm = j * rowsR; - CommonParallel.For(0, rowsR, k => column[k] = sol[jm + k]); - CommonParallel.For( - 0, - rowsR, - i => - { - var im = i * rowsR; - sol[jm + i] = CommonParallel.Aggregate(0, rowsR, k => q[im + k].Conjugate() * column[k]); - }); - } - - // Solve R*X = Y; - for (var k = columnsR - 1; k >= 0; k--) - { - var km = k * rowsR; - for (var j = 0; j < columnsB; j++) - { - sol[(j * rowsR) + k] /= r[km + k]; - } - - for (var i = 0; i < k; i++) - { - for (var j = 0; j < columnsB; j++) - { - var jm = j * rowsR; - sol[jm + i] -= sol[jm + k] * r[km + i]; - } - } - } - - // Fill result matrix - CommonParallel.For( - 0, - columnsR, - row => - { - for (var col = 0; col < columnsB; col++) - { - x[(col * columnsR) + row] = sol[row + (col * rowsR)]; - } - }); - } - - /// - /// Computes the singular value decomposition of A. - /// - /// Compute the singular U and VT vectors or not. - /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The singular values of A in ascending value. - /// If is true, on exit U contains the left - /// singular vectors. - /// If is true, on exit VT contains the transposed - /// right singular vectors. - /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, Complex[] a, int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (u == null) - { - throw new ArgumentNullException("u"); - } - - if (vt == null) - { - throw new ArgumentNullException("vt"); - } - - if (u.Length != rowsA * rowsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); - } - - if (vt.Length != columnsA * columnsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); - } - - if (s.Length != Math.Min(rowsA, columnsA)) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); - } - - // Actually "work = new Complex[aRows]" is acceptable size of work array. I set size proposed in method description - var work = new Complex[(2 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA)]; - SingularValueDecomposition(computeVectors, a, rowsA, columnsA, s, u, vt, work); - } - - /// - /// Computes the singular value decomposition of A. - /// - /// Compute the singular U and VT vectors or not. - /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The singular values of A in ascending value. - /// If is true, on exit U contains the left - /// singular vectors. - /// If is true, on exit VT contains the transposed - /// right singular vectors. - /// The work array. For real matrices, the work array should be at least - /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). - /// On exit, work[0] contains the optimal work size value. - /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, Complex[] a, int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt, Complex[] work) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (u == null) - { - throw new ArgumentNullException("u"); - } - - if (vt == null) - { - throw new ArgumentNullException("vt"); - } - - if (work == null) - { - throw new ArgumentNullException("work"); - } - - if (u.Length != rowsA * rowsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); - } - - if (vt.Length != columnsA * columnsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); - } - - if (s.Length != Math.Min(rowsA, columnsA)) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); - } - - if (work.Length == 0) - { - throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); - } - - if (work.Length < rowsA) - { - work[0] = rowsA; - throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); - } - - const int Maxiter = 1000; - - var e = new Complex[columnsA]; - var v = new Complex[vt.Length]; - var stemp = new Complex[Math.Min(rowsA + 1, columnsA)]; - - int i, j, l, lp1; - - var cs = 0.0; - var sn = 0.0; - Complex t; - - var ncu = rowsA; - - // Reduce matrix to bidiagonal form, storing the diagonal elements - // in "s" and the super-diagonal elements in "e". - var nct = Math.Min(rowsA - 1, columnsA); - var nrt = Math.Max(0, Math.Min(columnsA - 2, rowsA)); - 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 vector s[l]. - var sum = 0.0; - for (i = l; i < rowsA; i++) - { - sum += a[(l * rowsA) + i].Magnitude * a[(l * rowsA) + i].Magnitude; - } - - stemp[l] = Math.Sqrt(sum); - if (stemp[l] != 0.0) - { - if (a[(l * rowsA) + l] != 0.0) - { - stemp[l] = stemp[l].Magnitude * (a[(l * rowsA) + l] / a[(l * rowsA) + l].Magnitude); - } - - // A part of column "l" of Matrix A from row "l" to end multiply by 1.0 / s[l] - for (i = l; i < rowsA; i++) - { - a[(l * rowsA) + i] = a[(l * rowsA) + i] * (1.0 / stemp[l]); - } - - a[(l * rowsA) + l] = 1.0 + a[(l * rowsA) + l]; - } - - stemp[l] = -stemp[l]; - } - - for (j = lp1; j < columnsA; j++) - { - if (l < nct) - { - if (stemp[l] != 0.0) - { - // Apply the transformation. - t = 0.0; - for (i = l; i < rowsA; i++) - { - t += a[(l * rowsA) + i].Conjugate() * a[(j * rowsA) + i]; - } - - t = -t / a[(l * rowsA) + l]; - - for (var ii = l; ii < rowsA; ii++) - { - a[(j * rowsA) + ii] += t * a[(l * rowsA) + ii]; - } - } - } - - // Place the l-th row of matrix into "e" for the - // subsequent calculation of the row transformation. - e[j] = a[(j * rowsA) + l].Conjugate(); - } - - if (computeVectors && l < nct) - { - // Place the transformation in "u" for subsequent back multiplication. - for (i = l; i < rowsA; i++) - { - u[(l * rowsA) + i] = a[(l * rowsA) + i]; - } - } - - if (l >= nrt) - { - continue; - } - - // Compute the l-th row transformation and place the l-th super-diagonal in e(l). - var enorm = 0.0; - for (i = lp1; i < e.Length; i++) - { - enorm += e[i].Magnitude * e[i].Magnitude; - } - - e[l] = Math.Sqrt(enorm); - if (e[l] != 0.0) - { - if (e[lp1] != 0.0) - { - e[l] = e[l].Magnitude * (e[lp1] / e[lp1].Magnitude); - } - - // Scale vector "e" from "lp1" by 1.0 / e[l] - for (i = lp1; i < e.Length; i++) - { - e[i] = e[i] * (1.0 / e[l]); - } - - e[lp1] = 1.0 + e[lp1]; - } - - e[l] = -e[l].Conjugate(); - - if (lp1 < rowsA && e[l] != 0.0) - { - // Apply the transformation. - for (i = lp1; i < rowsA; i++) - { - work[i] = 0.0; - } - - for (j = lp1; j < columnsA; j++) - { - for (var ii = lp1; ii < rowsA; ii++) - { - work[ii] += e[j] * a[(j * rowsA) + ii]; - } - } - - for (j = lp1; j < columnsA; j++) - { - var ww = (-e[j] / e[lp1]).Conjugate(); - for (var ii = lp1; ii < rowsA; ii++) - { - a[(j * rowsA) + ii] += ww * work[ii]; - } - } - } - - if (!computeVectors) - { - continue; - } - - // Place the transformation in v for subsequent back multiplication. - for (i = lp1; i < columnsA; i++) - { - v[(l * columnsA) + i] = e[i]; - } - } - - // Set up the final bidiagonal matrix or order m. - var m = Math.Min(columnsA, rowsA + 1); - var nctp1 = nct + 1; - var nrtp1 = nrt + 1; - if (nct < columnsA) - { - stemp[nctp1 - 1] = a[((nctp1 - 1) * rowsA) + (nctp1 - 1)]; - } - - if (rowsA < m) - { - stemp[m - 1] = 0.0; - } - - if (nrtp1 < m) - { - e[nrtp1 - 1] = a[((m - 1) * rowsA) + (nrtp1 - 1)]; - } - - e[m - 1] = 0.0; - - // If required, generate "u". - if (computeVectors) - { - for (j = nctp1 - 1; j < ncu; j++) - { - for (i = 0; i < rowsA; i++) - { - u[(j * rowsA) + i] = 0.0; - } - - u[(j * rowsA) + j] = 1.0; - } - - for (l = nct - 1; l >= 0; l--) - { - if (stemp[l] != 0.0) - { - for (j = l + 1; j < ncu; j++) - { - t = 0.0; - for (i = l; i < rowsA; i++) - { - t += u[(l * rowsA) + i].Conjugate() * u[(j * rowsA) + i]; - } - - t = -t / u[(l * rowsA) + l]; - for (var ii = l; ii < rowsA; ii++) - { - u[(j * rowsA) + ii] += t * u[(l * rowsA) + ii]; - } - } - - // A part of column "l" of matrix A from row "l" to end multiply by -1.0 - for (i = l; i < rowsA; i++) - { - u[(l * rowsA) + i] = u[(l * rowsA) + i] * -1.0; - } - - u[(l * rowsA) + l] = 1.0 + u[(l * rowsA) + l]; - for (i = 0; i < l; i++) - { - u[(l * rowsA) + i] = 0.0; - } - } - else - { - for (i = 0; i < rowsA; i++) - { - u[(l * rowsA) + i] = 0.0; - } - - u[(l * rowsA) + l] = 1.0; - } - } - } - - // If it is required, generate v. - if (computeVectors) - { - for (l = columnsA - 1; l >= 0; l--) - { - lp1 = l + 1; - if (l < nrt) - { - if (e[l] != 0.0) - { - for (j = lp1; j < columnsA; j++) - { - t = 0.0; - for (i = lp1; i < columnsA; i++) - { - t += v[(l * columnsA) + i].Conjugate() * v[(j * columnsA) + i]; - } - - t = -t / v[(l * columnsA) + lp1]; - for (var ii = l; ii < columnsA; ii++) - { - v[(j * columnsA) + ii] += t * v[(l * columnsA) + ii]; - } - } - } - } - - for (i = 0; i < columnsA; i++) - { - v[(l * columnsA) + i] = 0.0; - } - - v[(l * columnsA) + l] = 1.0; - } - } - - // Transform "s" and "e" so that they are double - for (i = 0; i < m; i++) - { - Complex r; - if (stemp[i] != 0.0) - { - t = stemp[i].Magnitude; - r = stemp[i] / t; - stemp[i] = t; - if (i < m - 1) - { - e[i] = e[i] / r; - } - - if (computeVectors) - { - // A part of column "i" of matrix U from row 0 to end multiply by r - for (j = 0; j < rowsA; j++) - { - u[(i * rowsA) + j] = u[(i * rowsA) + j] * r; - } - } - } - - // Exit - if (i == m - 1) - { - break; - } - - if (e[i] == 0.0) - { - continue; - } - - t = e[i].Magnitude; - r = t / e[i]; - e[i] = t; - stemp[i + 1] = stemp[i + 1] * r; - if (!computeVectors) - { - continue; - } - - // A part of column "i+1" of matrix VT from row 0 to end multiply by r - for (j = 0; j < columnsA; j++) - { - v[((i + 1) * columnsA) + j] = v[((i + 1) * columnsA) + j] * 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. - 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 mS[m] and e[l-1] are negligible and l < m - // kase = 2: if mS[l] is negligible and l < m - // kase = 3: if e[l-1] is negligible, l < m, and mS[l, ..., mS[m] are not negligible (qr step). - // kase = 4: if e[m-1] is negligible (convergence). - double ztest; - double test; - for (l = m - 2; l >= 0; l--) - { - test = stemp[l].Magnitude + stemp[l + 1].Magnitude; - ztest = test + e[l].Magnitude; - if (ztest.AlmostEqualInDecimalPlaces(test, 15)) - { - e[l] = 0.0; - break; - } - } - - int kase; - if (l == m - 2) - { - kase = 4; - } - else - { - int ls; - for (ls = m - 1; ls > l; ls--) - { - test = 0.0; - if (ls != m - 1) - { - test = test + e[ls].Magnitude; - } - - if (ls != l + 1) - { - test = test + e[ls - 1].Magnitude; - } - - ztest = test + stemp[ls].Magnitude; - if (ztest.AlmostEqualInDecimalPlaces(test, 15)) - { - stemp[ls] = 0.0; - 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; - double f; - switch (kase) - { - // Deflate negligible s[m]. - case 1: - f = e[m - 2].Real; - e[m - 2] = 0.0; - double t1; - for (var kk = l; kk < m - 1; kk++) - { - k = m - 2 - kk + l; - t1 = stemp[k].Real; - Drotg(ref t1, ref f, ref cs, ref sn); - stemp[k] = t1; - if (k != l) - { - f = -sn * e[k - 1].Real; - e[k - 1] = cs * e[k - 1]; - } - - if (computeVectors) - { - // Rotate - for (i = 0; i < columnsA; i++) - { - var z = (cs * v[(k * columnsA) + i]) + (sn * v[((m - 1) * columnsA) + i]); - v[((m - 1) * columnsA) + i] = (cs * v[((m - 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); - v[(k * columnsA) + i] = z; - } - } - } - - break; - - // Split at negligible s[l]. - case 2: - f = e[l - 1].Real; - e[l - 1] = 0.0; - for (k = l; k < m; k++) - { - t1 = stemp[k].Real; - Drotg(ref t1, ref f, ref cs, ref sn); - stemp[k] = t1; - f = -sn * e[k].Real; - e[k] = cs * e[k]; - if (computeVectors) - { - // Rotate - for (i = 0; i < rowsA; i++) - { - var z = (cs * u[(k * rowsA) + i]) + (sn * u[((l - 1) * rowsA) + i]); - u[((l - 1) * rowsA) + i] = (cs * u[((l - 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); - u[(k * rowsA) + i] = z; - } - } - } - - break; - - // Perform one qr step. - case 3: - // calculate the shift. - var scale = 0.0; - scale = Math.Max(scale, stemp[m - 1].Magnitude); - scale = Math.Max(scale, stemp[m - 2].Magnitude); - scale = Math.Max(scale, e[m - 2].Magnitude); - scale = Math.Max(scale, stemp[l].Magnitude); - scale = Math.Max(scale, e[l].Magnitude); - var sm = stemp[m - 1].Real / scale; - var smm1 = stemp[m - 2].Real / scale; - var emm1 = e[m - 2].Real / scale; - var sl = stemp[l].Real / scale; - var el = e[l].Real / scale; - var b = (((smm1 + sm) * (smm1 - sm)) + (emm1 * emm1)) / 2.0; - var c = (sm * emm1) * (sm * emm1); - var shift = 0.0; - if (b != 0.0 || c != 0.0) - { - shift = 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 * stemp[k].Real) + (sn * e[k].Real); - e[k] = (cs * e[k]) - (sn * stemp[k]); - g = sn * stemp[k + 1].Real; - stemp[k + 1] = cs * stemp[k + 1]; - if (computeVectors) - { - for (i = 0; i < columnsA; i++) - { - var z = (cs * v[(k * columnsA) + i]) + (sn * v[((k + 1) * columnsA) + i]); - v[((k + 1) * columnsA) + i] = (cs * v[((k + 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); - v[(k * columnsA) + i] = z; - } - } - - Drotg(ref f, ref g, ref cs, ref sn); - stemp[k] = f; - f = (cs * e[k].Real) + (sn * stemp[k + 1].Real); - stemp[k + 1] = -(sn * e[k]) + (cs * stemp[k + 1]); - g = sn * e[k + 1].Real; - e[k + 1] = cs * e[k + 1]; - if (computeVectors && k < rowsA) - { - for (i = 0; i < rowsA; i++) - { - var z = (cs * u[(k * rowsA) + i]) + (sn * u[((k + 1) * rowsA) + i]); - u[((k + 1) * rowsA) + i] = (cs * u[((k + 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); - u[(k * rowsA) + i] = z; - } - } - } - - e[m - 2] = f; - iter = iter + 1; - break; - - // Convergence - case 4: - - // Make the singular value positive - if (stemp[l].Real < 0.0) - { - stemp[l] = -stemp[l]; - if (computeVectors) - { - // A part of column "l" of matrix VT from row 0 to end multiply by -1 - for (i = 0; i < columnsA; i++) - { - v[(l * columnsA) + i] = v[(l * columnsA) + i] * -1.0; - } - } - } - - // Order the singular value. - while (l != mn - 1) - { - if (stemp[l].Real >= stemp[l + 1].Real) - { - break; - } - - t = stemp[l]; - stemp[l] = stemp[l + 1]; - stemp[l + 1] = t; - if (computeVectors && l < columnsA) - { - // Swap columns l, l + 1 - for (i = 0; i < columnsA; i++) - { - var z = v[(l * columnsA) + i]; - v[(l * columnsA) + i] = v[((l + 1) * columnsA) + i]; - v[((l + 1) * columnsA) + i] = z; - } - } - - if (computeVectors && l < rowsA) - { - // Swap columns l, l + 1 - for (i = 0; i < rowsA; i++) - { - var z = u[(l * rowsA) + i]; - u[(l * rowsA) + i] = u[((l + 1) * rowsA) + i]; - u[((l + 1) * rowsA) + i] = z; - } - } - - l = l + 1; - } - - iter = 0; - m = m - 1; - break; - } - } - - if (computeVectors) - { - // Finally transpose "v" to get "vt" matrix - for (i = 0; i < columnsA; i++) - { - for (j = 0; j < columnsA; j++) - { - vt[(j * columnsA) + i] = v[(i * columnsA) + j].Conjugate(); - } - } - } - - // Copy stemp to s with size adjustment. We are using ported copy of linpack's svd code and it uses - // a singular vector of length rows+1 when rows < columns. The last element is not used and needs to be removed. - // We should port lapack's svd routine to remove this problem. - CommonParallel.For(0, Math.Min(rowsA, columnsA), index => s[index] = stemp[index]); - - // On return the first element of the work array stores the min size of the work array could have been - // work[0] = Math.Max(3 * Math.Min(aRows, aColumns) + Math.Max(aRows, aColumns), 5 * Math.Min(aRows, aColumns)); - work[0] = rowsA; - } - - /// - /// Solves A*X=B for X using the singular value decomposition of A. - /// - /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The singular values of A in ascending value. - /// On exit U contains the left singular vectors. - /// On exit VT contains the transposed right singular vectors. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - public void SvdSolve(Complex[] a, int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int columnsB, Complex[] x) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (u == null) - { - throw new ArgumentNullException("u"); - } - - if (vt == null) - { - throw new ArgumentNullException("vt"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (u.Length != rowsA * rowsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); - } - - if (vt.Length != columnsA * columnsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); - } - - if (s.Length != Math.Min(rowsA, columnsA)) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); - } - - if (b.Length != rowsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - // Actually "work = new Complex[aRows]" is acceptable size of work array. I set size proposed in method description - var work = new Complex[(2 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA)]; - SvdSolve(a, rowsA, columnsA, s, u, vt, b, columnsB, x, work); - } - - /// - /// Solves A*X=B for X using the singular value decomposition of A. - /// - /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The singular values of A in ascending value. - /// On exit U contains the left singular vectors. - /// On exit VT contains the transposed right singular vectors. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - /// The work array. For real matrices, the work array should be at least - /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). - /// On exit, work[0] contains the optimal work size value. - public void SvdSolve(Complex[] a, int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int columnsB, Complex[] x, Complex[] work) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (u == null) - { - throw new ArgumentNullException("u"); - } - - if (vt == null) - { - throw new ArgumentNullException("vt"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (u.Length != rowsA * rowsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); - } - - if (vt.Length != columnsA * columnsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); - } - - if (s.Length != Math.Min(rowsA, columnsA)) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); - } - - if (b.Length != rowsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (work.Length == 0) - { - throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); - } - - if (work.Length < rowsA) - { - work[0] = rowsA; - throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); - } - - SingularValueDecomposition(true, a, rowsA, columnsA, s, u, vt, work); - SvdSolveFactored(rowsA, columnsA, s, u, vt, b, columnsB, x); - } - - /// - /// Solves A*X=B for X using a previously SVD decomposed matrix. - /// - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The s values returned by . - /// The left singular vectors returned by . - /// The right singular vectors returned by . - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - public void SvdSolveFactored(int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] vt, Complex[] b, int columnsB, Complex[] x) - { - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (u == null) - { - throw new ArgumentNullException("u"); - } - - if (vt == null) - { - throw new ArgumentNullException("vt"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (u.Length != rowsA * rowsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); - } - - if (vt.Length != columnsA * columnsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); - } - - if (s.Length != Math.Min(rowsA, columnsA)) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); - } - - if (b.Length != rowsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - var mn = Math.Min(rowsA, columnsA); - var tmp = new Complex[columnsA]; - - for (var k = 0; k < columnsB; k++) - { - for (var j = 0; j < columnsA; j++) - { - var value = Complex.Zero; - if (j < mn) - { - for (var i = 0; i < rowsA; i++) - { - value += u[(j * rowsA) + i].Conjugate() * b[(k * rowsA) + i]; - } - - value /= s[j]; - } - - tmp[j] = value; - } - - for (var j = 0; j < columnsA; j++) - { - var value = Complex.Zero; - for (var i = 0; i < columnsA; i++) - { - value += vt[(j * columnsA) + i].Conjugate() * tmp[i]; - } - - x[(k * columnsA) + j] = value; - } - } - } - - #endregion - - #region ILinearAlgebraProvider Members - - /// - /// Adds a scaled vector to another: y += alpha*x. - /// - /// The vector to update. - /// The value to scale by. - /// The vector to add to . - /// This equivalent to the AXPY BLAS routine. - public void AddVectorToScaledVector(Complex32[] y, Complex32 alpha, Complex32[] x) - { - 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 (alpha == 0.0F) - { - return; - } - - if (alpha == 1.0F) - { - CommonParallel.For(0, y.Length, i => y[i] += x[i]); - } - else - { - CommonParallel.For(0, y.Length, i => y[i] += alpha * x[i]); - } - } - - /// - /// Scales an array. Can be used to scale a vector and a matrix. - /// - /// The scalar. - /// The values to scale. - /// This is equivalent to the SCAL BLAS routine. - public void ScaleArray(Complex32 alpha, Complex32[] x) - { - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (alpha.IsOne()) - { - return; - } - - CommonParallel.For(0, x.Length, i => x[i] = alpha * x[i]); - } - - /// - /// Computes the dot product of x and y. - /// - /// The vector x. - /// The vector y. - /// The dot product of x and y. - /// This is equivalent to the DOT BLAS routine. - public Complex32 DotProduct(Complex32[] x, Complex32[] y) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (y.Length != x.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } - - var d = new Complex32(0.0F, 0.0F); - - for (var i = 0; i < y.Length; i++) - { - d += y[i] * x[i]; - } - - return d; - } - - /// - /// Does a point wise add of two arrays z = x + y. This can be used - /// to add vectors or matrices. - /// - /// The array x. - /// The array y. - /// The result of the addition. - /// There is no equivalent BLAS routine, but many libraries - /// provide optimized (parallel and/or vectorized) versions of this - /// routine. - public void AddArrays(Complex32[] x, Complex32[] y, Complex32[] result) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (y.Length != x.Length || y.Length != result.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } - - CommonParallel.For(0, y.Length, i => result[i] = x[i] + y[i]); - } - - /// - /// Does a point wise subtraction of two arrays z = x - y. This can be used - /// to subtract vectors or matrices. - /// - /// The array x. - /// The array y. - /// The result of the subtraction. - /// There is no equivalent BLAS routine, but many libraries - /// provide optimized (parallel and/or vectorized) versions of this - /// routine. - public void SubtractArrays(Complex32[] x, Complex32[] y, Complex32[] result) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (y.Length != x.Length || y.Length != result.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } - - CommonParallel.For(0, y.Length, i => result[i] = x[i] - y[i]); - } - - /// - /// Does a point wise multiplication of two arrays z = x * y. This can be used - /// to multiple elements of vectors or matrices. - /// - /// The array x. - /// The array y. - /// The result of the point wise multiplication. - /// There is no equivalent BLAS routine, but many libraries - /// provide optimized (parallel and/or vectorized) versions of this - /// routine. - public void PointWiseMultiplyArrays(Complex32[] x, Complex32[] y, Complex32[] result) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (y.Length != x.Length || y.Length != result.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } - - CommonParallel.For(0, y.Length, i => result[i] = x[i] * y[i]); - } - - /// - /// Does a point wise division of two arrays z = x / y. This can be used - /// to divide elements of vectors or matrices. - /// - /// The array x. - /// The array y. - /// The result of the point wise division. - /// There is no equivalent BLAS routine, but many libraries - /// provide optimized (parallel and/or vectorized) versions of this - /// routine. - public void PointWiseDivideArrays(Complex32[] x, Complex32[] y, Complex32[] result) - { - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (y.Length != x.Length || y.Length != result.Length) - { - throw new ArgumentException(Resources.ArgumentVectorsSameLength); - } - - CommonParallel.For(0, y.Length, index => { result[index] = x[index] / y[index]; }); - } - - /// - /// Computes the requested of the matrix. - /// - /// The type of norm to compute. - /// The number of rows. - /// The number of columns. - /// The matrix to compute the norm from. - /// - /// The requested of the matrix. - /// - public Complex32 MatrixNorm(Norm norm, int rows, int columns, Complex32[] matrix) - { - throw new NotImplementedException(); - } - - /// - /// Computes the requested of the matrix. - /// - /// The type of norm to compute. - /// The number of rows. - /// The number of columns. - /// The matrix to compute the norm from. - /// The work array. Only used when - /// and needs to be have a length of at least M (number of rows of . - /// - /// The requested of the matrix. - /// - public Complex32 MatrixNorm(Norm norm, int rows, int columns, Complex32[] matrix, Complex32[] work) - { - throw new NotImplementedException(); - } - - /// - /// Multiples two matrices. result = x * y - /// - /// The x matrix. - /// The number of rows in the x matrix. - /// The number of columns in the x matrix. - /// The y matrix. - /// The number of rows in the y matrix. - /// The number of columns in the y matrix. - /// Where to store the result of the multiplication. - /// This is a simplified version of the BLAS GEMM routine with alpha - /// set to 1.0 and beta set to 0.0, and x and y are not transposed. - public void MatrixMultiply(Complex32[] x, int rowsX, int columnsX, Complex32[] y, int rowsY, int columnsY, Complex32[] result) - { - // First check some basic requirement on the parameters of the matrix multiplication. - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (y == null) - { - throw new ArgumentNullException("y"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (rowsX * columnsX != x.Length) - { - throw new ArgumentException("x.Length != xRows * xColumns"); - } - - if (rowsY * columnsY != y.Length) - { - throw new ArgumentException("y.Length != yRows * yColumns"); - } - - if (columnsX != rowsY) - { - throw new ArgumentException("xColumns != yRows"); - } - - if (rowsX * columnsY != result.Length) - { - throw new ArgumentException("xRows * yColumns != result.Length"); - } - - // Check whether we will be overwriting any of our inputs and make copies if necessary. - // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory - // as result, we can do it on a row wise basis. We should investigate this. - Complex32[] xdata; - if (ReferenceEquals(x, result)) - { - xdata = (Complex32[])x.Clone(); - } - else - { - xdata = x; - } - - Complex32[] ydata; - if (ReferenceEquals(y, result)) - { - ydata = (Complex32[])y.Clone(); - } - else - { - ydata = y; - } - - // Start the actual matrix multiplication. - // TODO - For small matrices we should get rid of the parallelism because of startup costs. - // Perhaps the following implementations would be a good one - // http://blog.feradz.com/2009/01/cache-efficient-matrix-multiplication/ - MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, Complex32.One, xdata, rowsX, columnsX, ydata, rowsY, columnsY, Complex32.Zero, result); - } - - /// - /// Multiplies two matrices and updates another with the result. c = alpha*op(a)*op(b) + beta*c - /// - /// How to transpose the matrix. - /// How to transpose the matrix. - /// The value to scale matrix. - /// The a matrix. - /// The number of rows in the matrix. - /// The number of columns in the matrix. - /// The b matrix - /// The number of rows in the matrix. - /// The number of columns in the matrix. - /// The value to scale the matrix. - /// The c matrix. - public void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, Complex32 alpha, Complex32[] a, int rowsA, int columnsA, Complex32[] b, int rowsB, int columnsB, Complex32 beta, Complex32[] c) - { - // Choose nonsensical values for the number of rows in c; fill them in depending - // on the operations on a and b. - int rowsC; - - // First check some basic requirement on the parameters of the matrix multiplication. - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if ((int)transposeA > 111 && (int)transposeB > 111) - { - if (rowsA != columnsB) - { - throw new ArgumentOutOfRangeException(); - } - - if (columnsA * rowsB != c.Length) - { - throw new ArgumentOutOfRangeException(); - } - - rowsC = columnsA; - } - else if ((int)transposeA > 111) - { - if (rowsA != rowsB) - { - throw new ArgumentOutOfRangeException(); - } - - if (columnsA * columnsB != c.Length) - { - throw new ArgumentOutOfRangeException(); - } - - rowsC = columnsA; - } - else if ((int)transposeB > 111) - { - if (columnsA != columnsB) - { - throw new ArgumentOutOfRangeException(); - } - - if (rowsA * rowsB != c.Length) - { - throw new ArgumentOutOfRangeException(); - } - - rowsC = rowsA; - } - else - { - if (columnsA != rowsB) - { - throw new ArgumentOutOfRangeException(); - } - - if (rowsA * columnsB != c.Length) - { - throw new ArgumentOutOfRangeException(); - } - - rowsC = rowsA; - } - - if (alpha.IsZero() && beta.IsZero()) - { - Array.Clear(c, 0, c.Length); - return; - } - - // Check whether we will be overwriting any of our inputs and make copies if necessary. - // TODO - we can don't have to allocate a completely new matrix when x or y point to the same memory - // as result, we can do it on a row wise basis. We should investigate this. - Complex32[] adata; - if (ReferenceEquals(a, c)) - { - adata = (Complex32[])a.Clone(); - } - else - { - adata = a; - } - - Complex32[] bdata; - if (ReferenceEquals(b, c)) - { - bdata = (Complex32[])b.Clone(); - } - else - { - bdata = b; - } - - if (alpha.IsOne()) - { - if (beta.IsZero()) - { - if ((int)transposeA > 111 && (int)transposeB > 111) - { - CommonParallel.For( - 0, - columnsA, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsB; i++) - { - var iIndex = i * rowsA; - Complex32 s = 0; - for (var l = 0; l != columnsB; l++) - { - s += adata[iIndex + l] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = s; - } - }); - } - else if ((int)transposeA > 111) - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != columnsA; i++) - { - var iIndex = i * rowsA; - Complex32 s = 0; - for (var l = 0; l != rowsA; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s; - } - }); - } - else if ((int)transposeB > 111) - { - CommonParallel.For( - 0, - rowsB, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsA; i++) - { - Complex32 s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = s; - } - }); - } - else - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != rowsA; i++) - { - Complex32 s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s; - } - }); - } - } - else - { - if ((int)transposeA > 111 && (int)transposeB > 111) - { - CommonParallel.For( - 0, - columnsA, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsB; i++) - { - var iIndex = i * rowsA; - Complex32 s = 0; - for (var l = 0; l != columnsB; l++) - { - s += adata[iIndex + l] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = (c[jIndex + i] * beta) + s; - } - }); - } - else if ((int)transposeA > 111) - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != columnsA; i++) - { - var iIndex = i * rowsA; - Complex32 s = 0; - for (var l = 0; l != rowsA; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s + (c[jcIndex + i] * beta); - } - }); - } - else if ((int)transposeB > 111) - { - CommonParallel.For( - 0, - rowsB, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsA; i++) - { - Complex32 s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = s + (c[jIndex + i] * beta); - } - }); - } - else - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != rowsA; i++) - { - Complex32 s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = s + (c[jcIndex + i] * beta); - } - }); - } - } - } - else - { - if ((int)transposeA > 111 && (int)transposeB > 111) - { - CommonParallel.For( - 0, - columnsA, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsB; i++) - { - var iIndex = i * rowsA; - Complex32 s = 0; - for (var l = 0; l != columnsB; l++) - { - s += adata[iIndex + l] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = (c[jIndex + i] * beta) + (alpha * s); - } - }); - } - else if ((int)transposeA > 111) - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != columnsA; i++) - { - var iIndex = i * rowsA; - Complex32 s = 0; - for (var l = 0; l != rowsA; l++) - { - s += adata[iIndex + l] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); - } - }); - } - else if ((int)transposeB > 111) - { - CommonParallel.For( - 0, - rowsB, - j => - { - var jIndex = j * rowsC; - for (var i = 0; i != rowsA; i++) - { - Complex32 s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[(l * rowsB) + j]; - } - - c[jIndex + i] = (alpha * s) + (c[jIndex + i] * beta); - } - }); - } - else - { - CommonParallel.For( - 0, - columnsB, - j => - { - var jcIndex = j * rowsC; - var jbIndex = j * rowsB; - for (var i = 0; i != rowsA; i++) - { - Complex32 s = 0; - for (var l = 0; l != columnsA; l++) - { - s += adata[(l * rowsA) + i] * bdata[jbIndex + l]; - } - - c[jcIndex + i] = (alpha * s) + (c[jcIndex + i] * beta); - } - }); - } - } - } - - /// - /// Computes the LUP factorization of A. P*A = L*U. - /// - /// An by matrix. The matrix is overwritten with the - /// the LU factorization on exit. The lower triangular factor L is stored in under the diagonal of (the diagonal is always 1.0 - /// for the L factor). The upper triangular factor U is stored on and above the diagonal of . - /// The order of the square matrix . - /// On exit, it contains the pivot indices. The size of the array must be . - /// This is equivalent to the GETRF LAPACK routine. - public void LUFactor(Complex32[] data, int order, int[] ipiv) - { - if (data == null) - { - throw new ArgumentNullException("data"); - } - - if (ipiv == null) - { - throw new ArgumentNullException("ipiv"); - } - - if (data.Length != order * order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "data"); - } - - if (ipiv.Length != order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); - } - - // Initialize the pivot matrix to the identity permutation. - for (var i = 0; i < order; i++) - { - ipiv[i] = i; - } - - var vecLUcolj = new Complex32[order]; - - // Outer loop. - for (var j = 0; j < order; j++) - { - var indexj = j * order; - var indexjj = indexj + j; - - // Make a copy of the j-th column to localize references. - for (var i = 0; i < order; i++) - { - vecLUcolj[i] = data[indexj + i]; - } - - // Apply previous transformations. - for (var i = 0; i < order; i++) - { - // Most of the time is spent in the following dot product. - var kmax = Math.Min(i, j); - var s = Complex32.Zero; - for (var k = 0; k < kmax; k++) - { - s += data[(k * order) + i] * vecLUcolj[k]; - } - - data[indexj + i] = vecLUcolj[i] -= s; - } - - // Find pivot and exchange if necessary. - var p = j; - for (var i = j + 1; i < order; i++) - { - if (vecLUcolj[i].Magnitude > vecLUcolj[p].Magnitude) - { - p = i; - } - } - - if (p != j) - { - for (var k = 0; k < order; k++) - { - var indexk = k * order; - var indexkp = indexk + p; - var indexkj = indexk + j; - var temp = data[indexkp]; - data[indexkp] = data[indexkj]; - data[indexkj] = temp; - } - - ipiv[j] = p; - } - - // Compute multipliers. - if (j < order & data[indexjj] != 0.0f) - { - for (var i = j + 1; i < order; i++) - { - data[indexj + i] /= data[indexjj]; - } - } - } - } - - /// - /// Computes the inverse of matrix using LU factorization. - /// - /// The N by N matrix to invert. Contains the inverse On exit. - /// The order of the square matrix . - /// This is equivalent to the GETRF and GETRI LAPACK routines. - public void LUInverse(Complex32[] a, int order) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (a.Length != order * order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); - } - - var ipiv = new int[order]; - LUFactor(a, order, ipiv); - LUInverseFactored(a, order, ipiv); - } - - /// - /// Computes the inverse of a previously factored matrix. - /// - /// The LU factored N by N matrix. Contains the inverse On exit. - /// The order of the square matrix . - /// The pivot indices of . - /// This is equivalent to the GETRI LAPACK routine. - public void LUInverseFactored(Complex32[] 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 Complex32[a.Length]; - for (var i = 0; i < order; i++) - { - inverse[i + (order * i)] = Complex32.One; - } - - LUSolveFactored(order, a, order, ipiv, inverse); - CommonParallel.For(0, a.Length, index => a[index] = inverse[index]); - } - - /// - /// Computes the inverse of matrix using LU factorization. - /// - /// The N by N matrix to invert. Contains the inverse On exit. - /// The order of the square matrix . - /// 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. - /// This is equivalent to the GETRF and GETRI LAPACK routines. - public void LUInverse(Complex32[] a, int order, Complex32[] work) - { - LUInverse(a, order); - } - - /// - /// Computes the inverse of a previously factored matrix. - /// - /// The LU factored N by N matrix. Contains the inverse On exit. - /// The order of the square matrix . - /// The pivot indices of . - /// 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. - /// This is equivalent to the GETRI LAPACK routine. - public void LUInverseFactored(Complex32[] a, int order, int[] ipiv, Complex32[] work) - { - LUInverseFactored(a, order, ipiv); - } - - /// - /// Solves A*X=B for X using LU factorization. - /// - /// The number of columns of B. - /// The square matrix A. - /// The order of the square matrix . - /// The B matrix. - /// This is equivalent to the GETRF and GETRS LAPACK routines. - public void LUSolve(int columnsOfB, Complex32[] a, int order, Complex32[] 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"); - } - - var ipiv = new int[order]; - LUFactor(a, order, ipiv); - LUSolveFactored(columnsOfB, a, order, ipiv, b); - } - - /// - /// Solves A*X=B for X using a previously factored A matrix. - /// - /// The number of columns of B. - /// The factored A matrix. - /// The order of the square matrix . - /// The pivot indices of . - /// The B matrix. - /// This is equivalent to the GETRS LAPACK routine. - public void LUSolveFactored(int columnsOfB, Complex32[] a, int order, int[] ipiv, Complex32[] b) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (ipiv == null) - { - throw new ArgumentNullException("ipiv"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (a.Length != order * order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); - } - - if (ipiv.Length != order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); - } - - if (b.Length != order * columnsOfB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - // Compute the column vector P*B - for (var i = 0; i < ipiv.Length; i++) - { - if (ipiv[i] == i) - { - continue; - } - - var p = ipiv[i]; - for (var j = 0; j < columnsOfB; j++) - { - var indexk = j * order; - var indexkp = indexk + p; - var indexkj = indexk + i; - var temp = b[indexkp]; - b[indexkp] = b[indexkj]; - b[indexkj] = temp; - } - } - - // Solve L*Y = P*B - for (var k = 0; k < order; k++) - { - var korder = k * order; - for (var i = k + 1; i < order; i++) - { - for (var j = 0; j < columnsOfB; j++) - { - var index = j * order; - b[i + index] -= b[k + index] * a[i + korder]; - } - } - } - - // Solve U*X = Y; - for (var k = order - 1; k >= 0; k--) - { - var korder = k + (k * order); - for (var j = 0; j < columnsOfB; j++) - { - b[k + (j * order)] /= a[korder]; - } - - korder = k * order; - for (var i = 0; i < k; i++) - { - for (var j = 0; j < columnsOfB; j++) - { - var index = j * order; - b[i + index] -= b[k + index] * a[i + korder]; - } - } - } - } - - /// - /// Solves A*X=B for X using LU factorization. - /// - /// How to transpose the matrix. - /// The number of columns of B. - /// The square matrix A. - /// The order of the square matrix . - /// The B matrix. - /// This is equivalent to the GETRF and GETRS LAPACK routines. - public void LUSolve(Transpose transposeA, int columnsOfB, Complex32[] a, int order, Complex32[] 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"); - } - - var ipiv = new int[order]; - LUFactor(a, order, ipiv); - LUSolveFactored(transposeA, columnsOfB, a, order, ipiv, b); - } - - /// - /// Solves A*X=B for X using a previously factored A matrix. - /// - /// How to transpose the matrix. - /// The number of columns of B. - /// The factored A matrix. - /// The order of the square matrix . - /// The pivot indices of . - /// The B matrix. - /// This is equivalent to the GETRS LAPACK routine. - public void LUSolveFactored(Transpose transposeA, int columnsOfB, Complex32[] a, int order, int[] ipiv, Complex32[] b) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (ipiv == null) - { - throw new ArgumentNullException("ipiv"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (a.Length != order * order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "a"); - } - - if (ipiv.Length != order) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "ipiv"); - } - - if (b.Length != order * columnsOfB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (transposeA == Transpose.Transpose) - { - var aT = new Complex32[a.Length]; - for (var i = 0; i < order; i++) - { - for (var j = 0; j < order; j++) - { - aT[(j * order) + i] = a[(i * order) + j]; - } - } - - LUSolveFactored(columnsOfB, aT, order, ipiv, b); - } - else if (transposeA == Transpose.ConjugateTranspose) - { - var acT = new Complex32[a.Length]; - for (var i = 0; i < order; i++) - { - for (var j = 0; j < order; j++) - { - acT[(j * order) + i] = a[(i * order) + j].Conjugate(); - } - } - - LUSolveFactored(columnsOfB, acT, order, ipiv, b); - } - else - { - LUSolveFactored(columnsOfB, a, order, ipiv, b); - } - } - - /// - /// Computes the Cholesky factorization of A. - /// - /// On entry, a square, positive definite matrix. On exit, the matrix is overwritten with the - /// the Cholesky factorization. - /// The number of rows or columns in the matrix. - /// This is equivalent to the POTRF LAPACK routine. - public void CholeskyFactor(Complex32[] a, int order) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - var tmpColumn = new Complex32[order]; - - // Main loop - along the diagonal - for (var ij = 0; ij < order; ij++) - { - // "Pivot" element - var tmpVal = a[(ij * order) + ij]; - - if (tmpVal.Real > 0.0) - { - tmpVal = tmpVal.SquareRoot(); - a[(ij * order) + ij] = tmpVal; - tmpColumn[ij] = tmpVal; - - // Calculate multipliers and copy to local column - // Current column, below the diagonal - for (var i = ij + 1; i < order; i++) - { - a[(ij * order) + i] /= tmpVal; - tmpColumn[i] = a[(ij * order) + i]; - } - - // Remaining columns, below the diagonal - DoCholeskyStep(a, order, ij + 1, order, tmpColumn, Control.NumberOfParallelWorkerThreads); - } - else - { - throw new ArgumentException(Resources.ArgumentMatrixPositiveDefinite); - } - - for (var i = ij + 1; i < order; i++) - { - a[(i * order) + ij] = 0.0f; - } - } - } - - /// - /// Calculate Cholesky step - /// - /// Factor matrix - /// Number of rows - /// Column start - /// Total columns - /// Multipliears calculated previously - /// Number of available processors - private static void DoCholeskyStep(Complex32[] data, int rowDim, int firstCol, int colLimit, Complex32[] multipliers, int availableCores) - { - var tmpColCount = colLimit - firstCol; - - if ((availableCores > 1) && (tmpColCount > 200)) - { - var tmpSplit = firstCol + (tmpColCount / 3); - var tmpCores = availableCores / 2; - - CommonParallel.Invoke( - () => DoCholeskyStep(data, rowDim, firstCol, tmpSplit, multipliers, tmpCores), - () => DoCholeskyStep(data, rowDim, tmpSplit, colLimit, multipliers, tmpCores)); - } - else - { - for (var j = firstCol; j < colLimit; j++) - { - var tmpVal = multipliers[j]; - for (var i = j; i < rowDim; i++) - { - data[(j * rowDim) + i] -= multipliers[i] * tmpVal.Conjugate(); - } - } - } - } - - /// - /// Solves A*X=B for X using Cholesky factorization. - /// - /// The square, positive definite matrix A. - /// The number of rows and columns in A. - /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. - /// This is equivalent to the POTRF add POTRS LAPACK routines. - public void CholeskySolve(Complex32[] a, int orderA, Complex32[] b, int rowsB, int columnsB) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (orderA != rowsB) - { - throw new ArgumentException(Resources.ArgumentMatrixDimensions); - } - - if (ReferenceEquals(a, b)) - { - throw new ArgumentException(Resources.ArgumentReferenceDifferent); - } - - CholeskyFactor(a, orderA); - CholeskySolveFactored(a, orderA, b, rowsB, columnsB); - } - - /// - /// Solves A*X=B for X using a previously factored A matrix. - /// - /// The square, positive definite matrix A. - /// The number of rows and columns in A. - /// The B matrix. - /// The number of rows in the B matrix. - /// The number of columns in the B matrix. - /// This is equivalent to the POTRS LAPACK routine. - public void CholeskySolveFactored(Complex32[] a, int orderA, Complex32[] b, int rowsB, int columnsB) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (orderA != rowsB) - { - throw new ArgumentException(Resources.ArgumentMatrixDimensions); - } - - if (ReferenceEquals(a, b)) - { - throw new ArgumentException(Resources.ArgumentReferenceDifferent); - } - - CommonParallel.For( - 0, - columnsB, - c => - { - var cindex = c * orderA; - - // Solve L*Y = B; - Complex32 sum; - for (var i = 0; i < orderA; i++) - { - sum = b[cindex + i]; - for (var k = i - 1; k >= 0; k--) - { - sum -= a[(k * orderA) + i] * b[cindex + k]; - } - - b[cindex + i] = sum / a[(i * orderA) + i]; - } - - // Solve L'*X = Y; - for (var i = orderA - 1; i >= 0; i--) - { - sum = b[cindex + i]; - var iindex = i * orderA; - for (var k = i + 1; k < orderA; k++) - { - sum -= a[iindex + k].Conjugate() * b[cindex + k]; - } - - b[cindex + i] = sum / a[iindex + i]; - } - }); - } - - /// - /// Computes the QR factorization of A. - /// - /// On entry, it is the M by N A matrix to factor. On exit, - /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// On exit, A M by M matrix that holds the Q matrix of the - /// QR factorization. - /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(Complex32[] r, int rowsR, int columnsR, Complex32[] q) - { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (r.Length != rowsR * columnsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); - } - - if (q.Length != rowsR * rowsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); - } - - var work = new Complex32[rowsR * rowsR]; - QRFactor(r, rowsR, columnsR, q, work); - } - - /// - /// Computes the QR factorization of A. - /// - /// On entry, it is the M by N A matrix to factor. On exit, - /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// On exit, A M by M matrix that holds the Q matrix of the - /// QR factorization. - /// 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. - /// This is similar to the GEQRF and ORGQR LAPACK routines. - public void QRFactor(Complex32[] r, int rowsR, int columnsR, Complex32[] q, Complex32[] work) - { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (work == null) - { - throw new ArgumentNullException("q"); - } - - if (r.Length != rowsR * columnsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); - } - - if (q.Length != rowsR * rowsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); - } - - if (work.Length < rowsR * rowsR) - { - work[0] = rowsR * rowsR; - throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); - } - - CommonParallel.For(0, rowsR, i => q[(i * rowsR) + i] = Complex32.One); - - var minmn = Math.Min(rowsR, columnsR); - for (var i = 0; i < minmn; i++) - { - GenerateColumn(work, r, rowsR, i, i); - ComputeQR(work, i, r, i, rowsR, i + 1, columnsR, Control.NumberOfParallelWorkerThreads); - } - - for (var i = minmn - 1; i >= 0; i--) - { - ComputeQR(work, i, q, i, rowsR, i, rowsR, Control.NumberOfParallelWorkerThreads); - } - - work[0] = rowsR * rowsR; - } - - #region QR Factor Helper functions - - /// - /// Perform calculation of Q or R - /// - /// Work array - /// Index of colunn in work array - /// Q or R matrices - /// The first row in - /// The last row - /// The first column - /// The last column - /// Number of available CPUs - private static void ComputeQR(Complex32[] work, int workIndex, Complex32[] a, int rowStart, int rowCount, int columnStart, int columnCount, int availableCores) - { - if (rowStart > rowCount || columnStart > columnCount) - { - return; - } - - var tmpColCount = columnCount - columnStart; - - if ((availableCores > 1) && (tmpColCount > 200)) - { - var tmpSplit = columnStart + (tmpColCount / 2); - var tmpCores = availableCores / 2; - - CommonParallel.Invoke( - () => ComputeQR(work, workIndex, a, rowStart, rowCount, columnStart, tmpSplit, tmpCores), - () => ComputeQR(work, workIndex, a, rowStart, rowCount, tmpSplit, columnCount, tmpCores)); - } - else - { - for (var j = columnStart; j < columnCount; j++) - { - var scale = Complex32.Zero; - for (var i = rowStart; i < rowCount; i++) - { - scale += work[(workIndex * rowCount) + i - rowStart] * a[(j * rowCount) + i]; - } - - for (var i = rowStart; i < rowCount; i++) - { - a[(j * rowCount) + i] -= work[(workIndex * rowCount) + i - rowStart].Conjugate() * scale; - } - } - } - } - - /// - /// Generate column from initial matrix to work array - /// - /// Work array - /// Initial matrix - /// The number of rows in matrix - /// The firts row - /// Column index - private static void GenerateColumn(Complex32[] work, Complex32[] a, int rowCount, int row, int column) - { - var tmp = column * rowCount; - var index = tmp + row; - - CommonParallel.For( - row, - rowCount, - i => - { - var iIndex = tmp + i; - work[iIndex - row] = a[iIndex]; - a[iIndex] = Complex32.Zero; - }); - - var norm = Complex32.Zero; - for (var i = 0; i < rowCount - row; ++i) - { - var index1 = tmp + i; - norm += work[index1].Magnitude * work[index1].Magnitude; - } - - norm = norm.SquareRoot(); - if (row == rowCount - 1 || norm.Magnitude == 0) - { - a[index] = -work[tmp]; - work[tmp] = new Complex32(2.0f, 0).SquareRoot(); - return; - } - - if (work[tmp].Magnitude != 0.0f) - { - norm = norm.Magnitude * (work[tmp] / work[tmp].Magnitude); - } - - a[index] = -norm; - CommonParallel.For(0, rowCount - row, i => work[tmp + i] /= norm); - work[tmp] += 1.0f; - - var s = (1.0f / work[tmp]).SquareRoot(); - CommonParallel.For(0, rowCount - row, i => work[tmp + i] = work[tmp + i].Conjugate() * s); - } - - #endregion - - /// - /// Solves A*X=B for X using QR factorization of A. - /// - /// On entry, it is the M by N A matrix to factor. On exit, - /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// On exit, A M by M matrix that holds the Q matrix of the - /// QR factorization. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - public void QRSolve(Complex32[] r, int rowsR, int columnsR, Complex32[] q, Complex32[] b, int columnsB, Complex32[] x) - { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (b == null) - { - throw new ArgumentNullException("q"); - } - - if (x == null) - { - throw new ArgumentNullException("q"); - } - - if (r.Length != rowsR * columnsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); - } - - if (q.Length != rowsR * rowsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); - } - - if (b.Length != rowsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); - } - - var work = new Complex32[rowsR * rowsR]; - QRSolve(r, rowsR, columnsR, q, b, columnsB, x, work); - } - - /// - /// Solves A*X=B for X using QR factorization of A. - /// - /// On entry, it is the M by N A matrix to factor. On exit, - /// it is overwritten with the R matrix of the QR factorization. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// On exit, A M by M matrix that holds the Q matrix of the - /// QR factorization. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - /// 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. - public void QRSolve(Complex32[] r, int rowsR, int columnsR, Complex32[] q, Complex32[] b, int columnsB, Complex32[] x, Complex32[] work) - { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (b == null) - { - throw new ArgumentNullException("q"); - } - - if (x == null) - { - throw new ArgumentNullException("q"); - } - - if (r.Length != rowsR * columnsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); - } - - if (q.Length != rowsR * rowsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); - } - - if (b.Length != rowsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); - } - - if (work.Length < rowsR * rowsR) - { - work[0] = rowsR * rowsR; - throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); - } - - QRFactor(r, rowsR, columnsR, q, work); - QRSolveFactored(q, r, rowsR, columnsR, b, columnsB, x); - - work[0] = rowsR * rowsR; - } - - /// - /// Solves A*X=B for X using a previously QR factored matrix. - /// - /// The Q matrix obtained by calling . - /// The R matrix obtained by calling . - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - public void QRSolveFactored(Complex32[] q, Complex32[] r, int rowsR, int columnsR, Complex32[] b, int columnsB, Complex32[] x) - { - if (r == null) - { - throw new ArgumentNullException("r"); - } - - if (q == null) - { - throw new ArgumentNullException("q"); - } - - if (b == null) - { - throw new ArgumentNullException("q"); - } - - if (x == null) - { - throw new ArgumentNullException("q"); - } - - if (r.Length != rowsR * columnsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "r"); - } - - if (q.Length != rowsR * rowsR) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "q"); - } - - if (b.Length != rowsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsR * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "x"); - } - - var sol = new Complex32[b.Length]; - - // Copy B matrix to "sol", so B data will not be changed - CommonParallel.For(0, b.Length, index => sol[index] = b[index]); - - // Compute Y = transpose(Q)*B - var column = new Complex32[rowsR]; - for (var j = 0; j < columnsB; j++) - { - var jm = j * rowsR; - CommonParallel.For(0, rowsR, k => column[k] = sol[jm + k]); - CommonParallel.For( - 0, - rowsR, - i => - { - var im = i * rowsR; - sol[jm + i] = CommonParallel.Aggregate(0, rowsR, k => q[im + k].Conjugate() * column[k]); - }); - } - - // Solve R*X = Y; - for (var k = columnsR - 1; k >= 0; k--) - { - var km = k * rowsR; - for (var j = 0; j < columnsB; j++) - { - sol[(j * rowsR) + k] /= r[km + k]; - } - - for (var i = 0; i < k; i++) - { - for (var j = 0; j < columnsB; j++) - { - var jm = j * rowsR; - sol[jm + i] -= sol[jm + k] * r[km + i]; - } - } - } - - // Fill result matrix - CommonParallel.For( - 0, - columnsR, - row => - { - for (var col = 0; col < columnsB; col++) - { - x[(col * columnsR) + row] = sol[row + (col * rowsR)]; - } - }); - } - - /// - /// Computes the singular value decomposition of A. - /// - /// Compute the singular U and VT vectors or not. - /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The singular values of A in ascending value. - /// If is true, on exit U contains the left - /// singular vectors. - /// If is true, on exit VT contains the transposed - /// right singular vectors. - /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, Complex32[] a, int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (u == null) - { - throw new ArgumentNullException("u"); - } - - if (vt == null) - { - throw new ArgumentNullException("vt"); - } - - if (u.Length != rowsA * rowsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); - } - - if (vt.Length != columnsA * columnsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); - } - - if (s.Length != Math.Min(rowsA, columnsA)) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); - } - - // Actually "work = new Complex32[aRows]" is acceptable size of work array. I set size proposed in method description - var work = new Complex32[(2 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA)]; - SingularValueDecomposition(computeVectors, a, rowsA, columnsA, s, u, vt, work); - } - - /// - /// Computes the singular value decomposition of A. - /// - /// Compute the singular U and VT vectors or not. - /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The singular values of A in ascending value. - /// If is true, on exit U contains the left - /// singular vectors. - /// If is true, on exit VT contains the transposed - /// right singular vectors. - /// The work array. For real matrices, the work array should be at least - /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). - /// On exit, work[0] contains the optimal work size value. - /// This is equivalent to the GESVD LAPACK routine. - public void SingularValueDecomposition(bool computeVectors, Complex32[] a, int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] work) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (u == null) - { - throw new ArgumentNullException("u"); - } - - if (vt == null) - { - throw new ArgumentNullException("vt"); - } - - if (work == null) - { - throw new ArgumentNullException("work"); - } - - if (u.Length != rowsA * rowsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); - } - - if (vt.Length != columnsA * columnsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); - } - - if (s.Length != Math.Min(rowsA, columnsA)) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); - } - - if (work.Length == 0) - { - throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); - } - - if (work.Length < rowsA) - { - work[0] = rowsA; - throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); - } - - const int Maxiter = 1000; - - var e = new Complex32[columnsA]; - var v = new Complex32[vt.Length]; - var stemp = new Complex32[Math.Min(rowsA + 1, columnsA)]; - - int i, j, l, lp1; - - var cs = 0.0f; - var sn = 0.0f; - Complex32 t; - - var ncu = rowsA; - - // Reduce matrix to bidiagonal form, storing the diagonal elements - // in "s" and the super-diagonal elements in "e". - var nct = Math.Min(rowsA - 1, columnsA); - var nrt = Math.Max(0, Math.Min(columnsA - 2, rowsA)); - 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 vector s[l]. - var sum = 0.0f; - for (i = l; i < rowsA; i++) - { - sum += a[(l * rowsA) + i].Magnitude * a[(l * rowsA) + i].Magnitude; - } - - stemp[l] = (float)Math.Sqrt(sum); - if (stemp[l] != 0.0f) - { - if (a[(l * rowsA) + l] != 0.0f) - { - stemp[l] = stemp[l].Magnitude * (a[(l * rowsA) + l] / a[(l * rowsA) + l].Magnitude); - } - - // A part of column "l" of Matrix A from row "l" to end multiply by 1.0f / s[l] - for (i = l; i < rowsA; i++) - { - a[(l * rowsA) + i] = a[(l * rowsA) + i] * (1.0f / stemp[l]); - } - - a[(l * rowsA) + l] = 1.0f + a[(l * rowsA) + l]; - } - - stemp[l] = -stemp[l]; - } - - for (j = lp1; j < columnsA; j++) - { - if (l < nct) - { - if (stemp[l] != 0.0f) - { - // Apply the transformation. - t = 0.0f; - for (i = l; i < rowsA; i++) - { - t += a[(l * rowsA) + i].Conjugate() * a[(j * rowsA) + i]; - } - - t = -t / a[(l * rowsA) + l]; - - for (var ii = l; ii < rowsA; ii++) - { - a[(j * rowsA) + ii] += t * a[(l * rowsA) + ii]; - } - } - } - - // Place the l-th row of matrix into "e" for the - // subsequent calculation of the row transformation. - e[j] = a[(j * rowsA) + l].Conjugate(); - } - - if (computeVectors && l < nct) - { - // Place the transformation in "u" for subsequent back multiplication. - for (i = l; i < rowsA; i++) - { - u[(l * rowsA) + i] = a[(l * rowsA) + i]; - } - } - - if (l >= nrt) - { - continue; - } - - // Compute the l-th row transformation and place the l-th super-diagonal in e(l). - var enorm = 0.0f; - for (i = lp1; i < e.Length; i++) - { - enorm += e[i].Magnitude * e[i].Magnitude; - } - - e[l] = (float)Math.Sqrt(enorm); - if (e[l] != 0.0f) - { - if (e[lp1] != 0.0f) - { - e[l] = e[l].Magnitude * (e[lp1] / e[lp1].Magnitude); - } - - // Scale vector "e" from "lp1" by 1.0f / e[l] - for (i = lp1; i < e.Length; i++) - { - e[i] = e[i] * (1.0f / e[l]); - } - - e[lp1] = 1.0f + e[lp1]; - } - - e[l] = -e[l].Conjugate(); - - if (lp1 < rowsA && e[l] != 0.0f) - { - // Apply the transformation. - for (i = lp1; i < rowsA; i++) - { - work[i] = 0.0f; - } - - for (j = lp1; j < columnsA; j++) - { - for (var ii = lp1; ii < rowsA; ii++) - { - work[ii] += e[j] * a[(j * rowsA) + ii]; - } - } - - for (j = lp1; j < columnsA; j++) - { - var ww = (-e[j] / e[lp1]).Conjugate(); - for (var ii = lp1; ii < rowsA; ii++) - { - a[(j * rowsA) + ii] += ww * work[ii]; - } - } - } - - if (!computeVectors) - { - continue; - } - - // Place the transformation in v for subsequent back multiplication. - for (i = lp1; i < columnsA; i++) - { - v[(l * columnsA) + i] = e[i]; - } - } - - // Set up the final bidiagonal matrix or order m. - var m = Math.Min(columnsA, rowsA + 1); - var nctp1 = nct + 1; - var nrtp1 = nrt + 1; - if (nct < columnsA) - { - stemp[nctp1 - 1] = a[((nctp1 - 1) * rowsA) + (nctp1 - 1)]; - } - - if (rowsA < m) - { - stemp[m - 1] = 0.0f; - } - - if (nrtp1 < m) - { - e[nrtp1 - 1] = a[((m - 1) * rowsA) + (nrtp1 - 1)]; - } - - e[m - 1] = 0.0f; - - // If required, generate "u". - if (computeVectors) - { - for (j = nctp1 - 1; j < ncu; j++) - { - for (i = 0; i < rowsA; i++) - { - u[(j * rowsA) + i] = 0.0f; - } - - u[(j * rowsA) + j] = 1.0f; - } - - for (l = nct - 1; l >= 0; l--) - { - if (stemp[l] != 0.0f) - { - for (j = l + 1; j < ncu; j++) - { - t = 0.0f; - for (i = l; i < rowsA; i++) - { - t += u[(l * rowsA) + i].Conjugate() * u[(j * rowsA) + i]; - } - - t = -t / u[(l * rowsA) + l]; - for (var ii = l; ii < rowsA; ii++) - { - u[(j * rowsA) + ii] += t * u[(l * rowsA) + ii]; - } - } - - // A part of column "l" of matrix A from row "l" to end multiply by -1.0f - for (i = l; i < rowsA; i++) - { - u[(l * rowsA) + i] = u[(l * rowsA) + i] * -1.0f; - } - - u[(l * rowsA) + l] = 1.0f + u[(l * rowsA) + l]; - for (i = 0; i < l; i++) - { - u[(l * rowsA) + i] = 0.0f; - } - } - else - { - for (i = 0; i < rowsA; i++) - { - u[(l * rowsA) + i] = 0.0f; - } - - u[(l * rowsA) + l] = 1.0f; - } - } - } - - // If it is required, generate v. - if (computeVectors) - { - for (l = columnsA - 1; l >= 0; l--) - { - lp1 = l + 1; - if (l < nrt) - { - if (e[l] != 0.0f) - { - for (j = lp1; j < columnsA; j++) - { - t = 0.0f; - for (i = lp1; i < columnsA; i++) - { - t += v[(l * columnsA) + i].Conjugate() * v[(j * columnsA) + i]; - } - - t = -t / v[(l * columnsA) + lp1]; - for (var ii = l; ii < columnsA; ii++) - { - v[(j * columnsA) + ii] += t * v[(l * columnsA) + ii]; - } - } - } - } - - for (i = 0; i < columnsA; i++) - { - v[(l * columnsA) + i] = 0.0f; - } - - v[(l * columnsA) + l] = 1.0f; - } - } - - // Transform "s" and "e" so that they are float - for (i = 0; i < m; i++) - { - Complex32 r; - if (stemp[i] != 0.0f) - { - t = stemp[i].Magnitude; - r = stemp[i] / t; - stemp[i] = t; - if (i < m - 1) - { - e[i] = e[i] / r; - } - - if (computeVectors) - { - // A part of column "i" of matrix U from row 0 to end multiply by r - for (j = 0; j < rowsA; j++) - { - u[(i * rowsA) + j] = u[(i * rowsA) + j] * r; - } - } - } - - // Exit - if (i == m - 1) - { - break; - } - - if (e[i] == 0.0f) - { - continue; - } - - t = e[i].Magnitude; - r = t / e[i]; - e[i] = t; - stemp[i + 1] = stemp[i + 1] * r; - if (!computeVectors) - { - continue; - } - - // A part of column "i+1" of matrix VT from row 0 to end multiply by r - for (j = 0; j < columnsA; j++) - { - v[((i + 1) * columnsA) + j] = v[((i + 1) * columnsA) + j] * 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. - 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 mS[m] and e[l-1] are negligible and l < m - // kase = 2: if mS[l] is negligible and l < m - // kase = 3: if e[l-1] is negligible, l < m, and mS[l, ..., mS[m] are not negligible (qr step). - // kase = 4: if e[m-1] is negligible (convergence). - float ztest; - float test; - for (l = m - 2; l >= 0; l--) - { - test = stemp[l].Magnitude + stemp[l + 1].Magnitude; - ztest = test + e[l].Magnitude; - 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 + e[ls].Magnitude; - } - - if (ls != l + 1) - { - test = test + e[ls - 1].Magnitude; - } - - ztest = test + stemp[ls].Magnitude; - if (ztest.AlmostEqualInDecimalPlaces(test, 7)) - { - stemp[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 s[m]. - case 1: - f = e[m - 2].Real; - e[m - 2] = 0.0f; - float t1; - for (var kk = l; kk < m - 1; kk++) - { - k = m - 2 - kk + l; - t1 = stemp[k].Real; - Drotg(ref t1, ref f, ref cs, ref sn); - stemp[k] = t1; - if (k != l) - { - f = -sn * e[k - 1].Real; - e[k - 1] = cs * e[k - 1]; - } - - if (computeVectors) - { - // Rotate - for (i = 0; i < columnsA; i++) - { - var z = (cs * v[(k * columnsA) + i]) + (sn * v[((m - 1) * columnsA) + i]); - v[((m - 1) * columnsA) + i] = (cs * v[((m - 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); - v[(k * columnsA) + i] = z; - } - } - } - - break; - - // Split at negligible s[l]. - case 2: - f = e[l - 1].Real; - e[l - 1] = 0.0f; - for (k = l; k < m; k++) - { - t1 = stemp[k].Real; - Drotg(ref t1, ref f, ref cs, ref sn); - stemp[k] = t1; - f = -sn * e[k].Real; - e[k] = cs * e[k]; - if (computeVectors) - { - // Rotate - for (i = 0; i < rowsA; i++) - { - var z = (cs * u[(k * rowsA) + i]) + (sn * u[((l - 1) * rowsA) + i]); - u[((l - 1) * rowsA) + i] = (cs * u[((l - 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); - u[(k * rowsA) + i] = z; - } - } - } - - break; - - // Perform one qr step. - case 3: - // calculate the shift. - var scale = 0.0f; - scale = Math.Max(scale, stemp[m - 1].Magnitude); - scale = Math.Max(scale, stemp[m - 2].Magnitude); - scale = Math.Max(scale, e[m - 2].Magnitude); - scale = Math.Max(scale, stemp[l].Magnitude); - scale = Math.Max(scale, e[l].Magnitude); - var sm = stemp[m - 1].Real / scale; - var smm1 = stemp[m - 2].Real / scale; - var emm1 = e[m - 2].Real / scale; - var sl = stemp[l].Real / scale; - var el = e[l].Real / scale; - var b = (((smm1 + sm) * (smm1 - sm)) + (emm1 * emm1)) / 2.0f; - var c = (sm * emm1) * (sm * emm1); - var shift = 0.0f; - if (b != 0.0f || c != 0.0f) - { - shift = (float)Math.Sqrt((b * b) + c); - if (b < 0.0f) - { - 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 * stemp[k].Real) + (sn * e[k].Real); - e[k] = (cs * e[k]) - (sn * stemp[k]); - g = sn * stemp[k + 1].Real; - stemp[k + 1] = cs * stemp[k + 1]; - if (computeVectors) - { - for (i = 0; i < columnsA; i++) - { - var z = (cs * v[(k * columnsA) + i]) + (sn * v[((k + 1) * columnsA) + i]); - v[((k + 1) * columnsA) + i] = (cs * v[((k + 1) * columnsA) + i]) - (sn * v[(k * columnsA) + i]); - v[(k * columnsA) + i] = z; - } - } - - Drotg(ref f, ref g, ref cs, ref sn); - stemp[k] = f; - f = (cs * e[k].Real) + (sn * stemp[k + 1].Real); - stemp[k + 1] = -(sn * e[k]) + (cs * stemp[k + 1]); - g = sn * e[k + 1].Real; - e[k + 1] = cs * e[k + 1]; - if (computeVectors && k < rowsA) - { - for (i = 0; i < rowsA; i++) - { - var z = (cs * u[(k * rowsA) + i]) + (sn * u[((k + 1) * rowsA) + i]); - u[((k + 1) * rowsA) + i] = (cs * u[((k + 1) * rowsA) + i]) - (sn * u[(k * rowsA) + i]); - u[(k * rowsA) + i] = z; - } - } - } - - e[m - 2] = f; - iter = iter + 1; - break; - - // Convergence - case 4: - - // Make the singular value positive - if (stemp[l].Real < 0.0f) - { - stemp[l] = -stemp[l]; - if (computeVectors) - { - // A part of column "l" of matrix VT from row 0 to end multiply by -1 - for (i = 0; i < columnsA; i++) - { - v[(l * columnsA) + i] = v[(l * columnsA) + i] * -1.0f; - } - } - } - - // Order the singular value. - while (l != mn - 1) - { - if (stemp[l].Real >= stemp[l + 1].Real) - { - break; - } - - t = stemp[l]; - stemp[l] = stemp[l + 1]; - stemp[l + 1] = t; - if (computeVectors && l < columnsA) - { - // Swap columns l, l + 1 - for (i = 0; i < columnsA; i++) - { - var z = v[(l * columnsA) + i]; - v[(l * columnsA) + i] = v[((l + 1) * columnsA) + i]; - v[((l + 1) * columnsA) + i] = z; - } - } - - if (computeVectors && l < rowsA) - { - // Swap columns l, l + 1 - for (i = 0; i < rowsA; i++) - { - var z = u[(l * rowsA) + i]; - u[(l * rowsA) + i] = u[((l + 1) * rowsA) + i]; - u[((l + 1) * rowsA) + i] = z; - } - } - - l = l + 1; - } - - iter = 0; - m = m - 1; - break; - } - } - - if (computeVectors) - { - // Finally transpose "v" to get "vt" matrix - for (i = 0; i < columnsA; i++) - { - for (j = 0; j < columnsA; j++) - { - vt[(j * columnsA) + i] = v[(i * columnsA) + j].Conjugate(); - } - } - } - - // Copy stemp to s with size adjustment. We are using ported copy of linpack's svd code and it uses - // a singular vector of length rows+1 when rows < columns. The last element is not used and needs to be removed. - // We should port lapack's svd routine to remove this problem. - CommonParallel.For(0, Math.Min(rowsA, columnsA), index => s[index] = stemp[index]); - - // On return the first element of the work array stores the min size of the work array could have been - // work[0] = Math.Max(3 * Math.Min(aRows, aColumns) + Math.Max(aRows, aColumns), 5 * Math.Min(aRows, aColumns)); - work[0] = rowsA; - } - - /// - /// Solves A*X=B for X using the singular value decomposition of A. - /// - /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The singular values of A in ascending value. - /// On exit U contains the left singular vectors. - /// On exit VT contains the transposed right singular vectors. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - public void SvdSolve(Complex32[] a, int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int columnsB, Complex32[] x) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (u == null) - { - throw new ArgumentNullException("u"); - } - - if (vt == null) - { - throw new ArgumentNullException("vt"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (u.Length != rowsA * rowsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); - } - - if (vt.Length != columnsA * columnsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); - } - - if (s.Length != Math.Min(rowsA, columnsA)) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); - } - - if (b.Length != rowsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - // TODO: Actually "work = new double[aRows]" is acceptable size of work array. I set size proposed in method description - var work = new Complex32[(2 * Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA)]; - SvdSolve(a, rowsA, columnsA, s, u, vt, b, columnsB, x, work); - } - - /// - /// Solves A*X=B for X using the singular value decomposition of A. - /// - /// On entry, the M by N matrix to decompose. On exit, A may be overwritten. - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The singular values of A in ascending value. - /// On exit U contains the left singular vectors. - /// On exit VT contains the transposed right singular vectors. - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - /// The work array. For real matrices, the work array should be at least - /// Max(3*Min(M, N) + Max(M, N), 5*Min(M,N)). For complex matrices, 2*Min(M, N) + Max(M, N). - /// On exit, work[0] contains the optimal work size value. - public void SvdSolve(Complex32[] a, int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int columnsB, Complex32[] x, Complex32[] work) - { - if (a == null) - { - throw new ArgumentNullException("a"); - } - - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (u == null) - { - throw new ArgumentNullException("u"); - } - - if (vt == null) - { - throw new ArgumentNullException("vt"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (u.Length != rowsA * rowsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); - } - - if (vt.Length != columnsA * columnsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); - } - - if (s.Length != Math.Min(rowsA, columnsA)) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); - } - - if (b.Length != rowsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (work.Length == 0) - { - throw new ArgumentException(Resources.ArgumentSingleDimensionArray, "work"); - } - - if (work.Length < rowsA) - { - work[0] = rowsA; - throw new ArgumentException(Resources.WorkArrayTooSmall, "work"); - } - - SingularValueDecomposition(true, a, rowsA, columnsA, s, u, vt, work); - SvdSolveFactored(rowsA, columnsA, s, u, vt, b, columnsB, x); - } - - /// - /// Solves A*X=B for X using a previously SVD decomposed matrix. - /// - /// The number of rows in the A matrix. - /// The number of columns in the A matrix. - /// The s values returned by . - /// The left singular vectors returned by . - /// The right singular vectors returned by . - /// The B matrix. - /// The number of columns of B. - /// On exit, the solution matrix. - public void SvdSolveFactored(int rowsA, int columnsA, Complex32[] s, Complex32[] u, Complex32[] vt, Complex32[] b, int columnsB, Complex32[] x) - { - if (s == null) - { - throw new ArgumentNullException("s"); - } - - if (u == null) - { - throw new ArgumentNullException("u"); - } - - if (vt == null) - { - throw new ArgumentNullException("vt"); - } - - if (b == null) - { - throw new ArgumentNullException("b"); - } - - if (x == null) - { - throw new ArgumentNullException("x"); - } - - if (u.Length != rowsA * rowsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "u"); - } - - if (vt.Length != columnsA * columnsA) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "vt"); - } - - if (s.Length != Math.Min(rowsA, columnsA)) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "s"); - } - - if (b.Length != rowsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - if (x.Length != columnsA * columnsB) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "b"); - } - - var mn = Math.Min(rowsA, columnsA); - var tmp = new Complex32[columnsA]; - - for (var k = 0; k < columnsB; k++) - { - for (var j = 0; j < columnsA; j++) - { - var value = Complex32.Zero; - if (j < mn) - { - for (var i = 0; i < rowsA; i++) - { - value += u[(j * rowsA) + i].Conjugate() * b[(k * rowsA) + i]; - } - - value /= s[j]; - } - - tmp[j] = value; - } - - for (var j = 0; j < columnsA; j++) - { - var value = Complex32.Zero; - for (var i = 0; i < columnsA; i++) - { - value += vt[(j * columnsA) + i].Conjugate() * tmp[i]; - } - - x[(k * columnsA) + j] = value; - } - } - } - - #endregion - } -} diff --git a/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs index a5c15d38..a9ac1bd6 100644 --- a/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs @@ -27,6 +27,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double { using System; + using Algorithms.LinearAlgebra; using Generic; using Properties; using Threading; @@ -221,55 +222,21 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// The L1 norm of the matrix. public override double L1Norm() { - var norm = 0.0; - for (var j = 0; j < ColumnCount; j++) - { - var s = 0.0; - for (var i = 0; i < RowCount; i++) - { - s += Math.Abs(Data[(j * RowCount) + i]); - } - - norm = Math.Max(norm, s); - } - - return norm; + return Control.LinearAlgebraProvider.MatrixNorm(Norm.OneNorm, RowCount, ColumnCount, Data); } /// Calculates the Frobenius norm of this matrix. /// The Frobenius norm of this matrix. public override double FrobeniusNorm() { - var transpose = (DenseMatrix)Transpose(); - var aat = (DenseMatrix)(this * transpose); - - var norm = 0.0; - for (var i = 0; i < RowCount; i++) - { - norm += Math.Abs(aat.Data[(i * RowCount) + i]); - } - - norm = Math.Sqrt(norm); - return norm; + return Control.LinearAlgebraProvider.MatrixNorm(Norm.FrobeniusNorm, RowCount, ColumnCount, Data); } /// Calculates the infinity norm of this matrix. /// The infinity norm of this matrix. public override double InfinityNorm() { - var norm = 0.0; - for (var i = 0; i < RowCount; i++) - { - var s = 0.0; - for (var j = 0; j < ColumnCount; j++) - { - s += Math.Abs(Data[(j * RowCount) + i]); - } - - norm = Math.Max(norm, s); - } - - return norm; + return Control.LinearAlgebraProvider.MatrixNorm(Norm.InfinityNorm, RowCount, ColumnCount, Data); } #region Elementary operations diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj index 25b87f83..de6ffaeb 100644 --- a/src/Numerics/Numerics.csproj +++ b/src/Numerics/Numerics.csproj @@ -71,9 +71,12 @@ + + + - +