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 @@
+
+
+
-
+