// // Math.NET Numerics, part of the Math.NET Project // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // // Copyright (c) 2009-2011 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. // #if NATIVEACML using IStation.Numerics.LinearAlgebra.Factorization; using System; using System.Security; namespace IStation.Numerics.Providers.LinearAlgebra.Acml { /// /// AMD Core Math Library (ACML) linear algebra provider. /// internal partial class AcmlLinearAlgebraProvider { /// /// 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. [SecuritySafeCritical] public override float DotProduct(float[] x, float[] y) { if (y == null) { throw new ArgumentNullException(nameof(y)); } if (x == null) { throw new ArgumentNullException(nameof(x)); } if (x.Length != y.Length) { throw new ArgumentException("The array arguments must have the same length."); } return SafeNativeMethods.s_dot_product(x.Length, x, y); } /// /// Adds a scaled vector to another: result = y + alpha*x. /// /// The vector to update. /// The value to scale by. /// The vector to add to . /// The result of the addition. /// This is similar to the AXPY BLAS routine. [SecuritySafeCritical] public override void AddVectorToScaledVector(float[] y, float alpha, float[] x, float[] result) { if (y == null) { throw new ArgumentNullException(nameof(y)); } if (x == null) { throw new ArgumentNullException(nameof(x)); } if (y.Length != x.Length) { throw new ArgumentException("All vectors must have the same dimensionality."); } if (!ReferenceEquals(y, result)) { Array.Copy(y, 0, result, 0, y.Length); } if (alpha == 0.0f) { return; } SafeNativeMethods.s_axpy(y.Length, alpha, x, result); } /// /// Scales an array. Can be used to scale a vector and a matrix. /// /// The scalar. /// The values to scale. /// This result of the scaling. /// This is similar to the SCAL BLAS routine. [SecuritySafeCritical] public override void ScaleArray(float alpha, float[] x, float[] result) { if (x == null) { throw new ArgumentNullException(nameof(x)); } if (!ReferenceEquals(x, result)) { Array.Copy(x, 0, result, 0, x.Length); } if (alpha == 1.0f) { return; } SafeNativeMethods.s_scale(x.Length, alpha, result); } /// /// 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.0f and beta set to 0.0f, and x and y are not transposed. public override void MatrixMultiply(float[] x, int rowsX, int columnsX, float[] y, int rowsY, int columnsY, float[] result) { MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, 1.0f, x, rowsX, columnsX, y, 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. [SecuritySafeCritical] public override void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, float alpha, float[] a, int rowsA, int columnsA, float[] b, int rowsB, int columnsB, float beta, float[] c) { if (a == null) { throw new ArgumentNullException(nameof(a)); } if (b == null) { throw new ArgumentNullException(nameof(b)); } if (c == null) { throw new ArgumentNullException(nameof(c)); } var m = transposeA == Transpose.DontTranspose ? rowsA : columnsA; var n = transposeB == Transpose.DontTranspose ? columnsB : rowsB; var k = transposeA == Transpose.DontTranspose ? columnsA : rowsA; var l = transposeB == Transpose.DontTranspose ? rowsB : columnsB; if (c.Length != m*n) { throw new ArgumentException("Matrix dimensions must agree."); } if (k != l) { throw new ArgumentException("Matrix dimensions must agree."); } SafeNativeMethods.s_matrix_multiply(transposeA, transposeB, m, n, k, alpha, a, b, beta, c); } /// /// 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.0f /// 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. [SecuritySafeCritical] public override void LUFactor(float[] data, int order, int[] ipiv) { if (data == null) { throw new ArgumentNullException(nameof(data)); } if (ipiv == null) { throw new ArgumentNullException(nameof(ipiv)); } if (data.Length != order*order) { throw new ArgumentException("The array arguments must have the same length.", nameof(data)); } if (ipiv.Length != order) { throw new ArgumentException("The array arguments must have the same length.", nameof(ipiv)); } SafeNativeMethods.s_lu_factor(order, data, ipiv); } /// /// 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. [SecuritySafeCritical] public override void LUInverse(float[] a, int order) { if (a == null) { throw new ArgumentNullException(nameof(a)); } if (a.Length != order*order) { throw new ArgumentException("The array arguments must have the same length.", nameof(a)); } var work = new float[order]; SafeNativeMethods.s_lu_inverse(order, a, work, work.Length); } /// /// 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. [SecuritySafeCritical] public override void LUInverseFactored(float[] a, int order, int[] ipiv) { if (a == null) { throw new ArgumentNullException(nameof(a)); } if (ipiv == null) { throw new ArgumentNullException(nameof(ipiv)); } if (a.Length != order*order) { throw new ArgumentException("The array arguments must have the same length.", nameof(a)); } if (ipiv.Length != order) { throw new ArgumentException("The array arguments must have the same length.", nameof(ipiv)); } var work = new float[order]; SafeNativeMethods.s_lu_inverse_factored(order, a, ipiv, work, order); } /// /// 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. [SecuritySafeCritical] public override void LUInverse(float[] a, int order, float[] work) { if (a == null) { throw new ArgumentNullException(nameof(a)); } if (a.Length != order*order) { throw new ArgumentException("The array arguments must have the same length.", nameof(a)); } if (work == null) { throw new ArgumentNullException(nameof(work)); } if (work.Length < order) { throw new ArgumentException(Resources.WorkArrayTooSmall, nameof(work)); } SafeNativeMethods.s_lu_inverse(order, a, work, work.Length); } /// /// 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. [SecuritySafeCritical] public override void LUInverseFactored(float[] a, int order, int[] ipiv, float[] work) { if (a == null) { throw new ArgumentNullException(nameof(a)); } if (ipiv == null) { throw new ArgumentNullException(nameof(ipiv)); } if (a.Length != order*order) { throw new ArgumentException("The array arguments must have the same length.", nameof(a)); } if (ipiv.Length != order) { throw new ArgumentException("The array arguments must have the same length.", nameof(ipiv)); } if (work == null) { throw new ArgumentNullException(nameof(work)); } if (work.Length < order) { throw new ArgumentException(Resources.WorkArrayTooSmall, nameof(work)); } SafeNativeMethods.s_lu_inverse_factored(order, a, ipiv, work, order); } /// /// 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 . /// On entry the B matrix; on exit the X matrix. /// This is equivalent to the GETRF and GETRS LAPACK routines. [SecuritySafeCritical] public override void LUSolve(int columnsOfB, float[] a, int order, float[] b) { if (a == null) { throw new ArgumentNullException(nameof(a)); } if (a.Length != order*order) { throw new ArgumentException("The array arguments must have the same length.", nameof(a)); } if (b.Length != columnsOfB*order) { throw new ArgumentException("The array arguments must have the same length.", nameof(b)); } if (ReferenceEquals(a, b)) { throw new ArgumentException("Arguments must be different objects."); } SafeNativeMethods.s_lu_solve(order, columnsOfB, a, 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 . /// On entry the B matrix; on exit the X matrix. /// This is equivalent to the GETRS LAPACK routine. [SecuritySafeCritical] public override void LUSolveFactored(int columnsOfB, float[] a, int order, int[] ipiv, float[] b) { if (a == null) { throw new ArgumentNullException(nameof(a)); } if (ipiv == null) { throw new ArgumentNullException(nameof(ipiv)); } if (a.Length != order*order) { throw new ArgumentException("The array arguments must have the same length.", nameof(a)); } if (ipiv.Length != order) { throw new ArgumentException("The array arguments must have the same length.", nameof(ipiv)); } if (b.Length != columnsOfB*order) { throw new ArgumentException("The array arguments must have the same length.", nameof(b)); } if (ReferenceEquals(a, b)) { throw new ArgumentException("Arguments must be different objects."); } SafeNativeMethods.s_lu_solve_factored(order, columnsOfB, a, 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. [SecuritySafeCritical] public override void CholeskyFactor(float[] a, int order) { if (a == null) { throw new ArgumentNullException(nameof(a)); } if (order < 1) { throw new ArgumentException("Value must be positive.", nameof(order)); } if (a.Length != order*order) { throw new ArgumentException("The array arguments must have the same length.", nameof(a)); } var info = SafeNativeMethods.s_cholesky_factor(order, a); if (info > 0) { throw new ArgumentException("Matrix must be positive definite."); } } /// /// Solves A*X=B for X using Cholesky factorization. /// /// The square, positive definite matrix A. /// The number of rows and columns in A. /// On entry the B matrix; on exit the X matrix. /// The number of columns in the B matrix. /// This is equivalent to the POTRF add POTRS LAPACK routines. /// [SecuritySafeCritical] public override void CholeskySolve(float[] a, int orderA, float[] b, int columnsB) { if (a == null) { throw new ArgumentNullException(nameof(a)); } if (b == null) { throw new ArgumentNullException(nameof(b)); } if (b.Length != orderA*columnsB) { throw new ArgumentException("The array arguments must have the same length.", nameof(b)); } if (ReferenceEquals(a, b)) { throw new ArgumentException("Arguments must be different objects."); } SafeNativeMethods.s_cholesky_solve(orderA, columnsB, a, b); } /// /// 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. /// On entry the B matrix; on exit the X matrix. /// The number of columns in the B matrix. /// This is equivalent to the POTRS LAPACK routine. [SecuritySafeCritical] public override void CholeskySolveFactored(float[] a, int orderA, float[] b, int columnsB) { if (a == null) { throw new ArgumentNullException(nameof(a)); } if (b == null) { throw new ArgumentNullException(nameof(b)); } if (b.Length != orderA*columnsB) { throw new ArgumentException("The array arguments must have the same length.", nameof(b)); } if (ReferenceEquals(a, b)) { throw new ArgumentException("Arguments must be different objects."); } SafeNativeMethods.s_cholesky_solve_factored(orderA, columnsB, a, b); } /// /// 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. /// A min(m,n) vector. On exit, contains additional information /// to be used by the QR solve routine. /// This is similar to the GEQRF and ORGQR LAPACK routines. [SecuritySafeCritical] public override void QRFactor(float[] r, int rowsR, int columnsR, float[] q, float[] tau) { if (r == null) { throw new ArgumentNullException(nameof(r)); } if (q == null) { throw new ArgumentNullException(nameof(q)); } if (r.Length != rowsR*columnsR) { throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "r"); } if (tau.Length < Math.Min(rowsR, columnsR)) { throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); } if (q.Length != rowsR*rowsR) { throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); } var work = new float[columnsR*Control.BlockSize]; SafeNativeMethods.s_qr_factor(rowsR, columnsR, r, tau, q, work, work.Length); } /// /// 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. /// A min(m,n) vector. On exit, contains additional information /// to be used by the QR solve routine. /// 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. [SecuritySafeCritical] public override void QRFactor(float[] r, int rowsR, int columnsR, float[] q, float[] tau, float[] work) { if (r == null) { throw new ArgumentNullException(nameof(r)); } if (q == null) { throw new ArgumentNullException(nameof(q)); } if (work == null) { throw new ArgumentNullException(nameof(work)); } if (r.Length != rowsR*columnsR) { throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * columnsR"), "r"); } if (tau.Length < Math.Min(rowsR, columnsR)) { throw new ArgumentException(string.Format(Resources.ArrayTooSmall, "min(m,n)"), "tau"); } if (q.Length != rowsR*rowsR) { throw new ArgumentException(string.Format(Resources.ArgumentArrayWrongLength, "rowsR * rowsR"), "q"); } if (work.Length < columnsR*Control.BlockSize) { work[0] = columnsR*Control.BlockSize; throw new ArgumentException(Resources.WorkArrayTooSmall, nameof(work)); } SafeNativeMethods.s_qr_factor(rowsR, columnsR, r, tau, q, work, work.Length); } /// /// Solves A*X=B for X using QR factorization of A. /// /// The A matrix. /// 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. /// Rows must be greater or equal to columns. public override void QRSolve(float[] a, int rows, int columns, float[] b, int columnsB, float[] x, QRMethod method = QRMethod.Full) { if (a == null) { throw new ArgumentNullException(nameof(a)); } if (b == null) { throw new ArgumentNullException(nameof(b)); } if (x == null) { throw new ArgumentNullException(nameof(x)); } if (a.Length != rows*columns) { throw new ArgumentException("The array arguments must have the same length.", nameof(a)); } if (b.Length != rows*columnsB) { throw new ArgumentException("The array arguments must have the same length.", nameof(b)); } if (x.Length != columns*columnsB) { throw new ArgumentException("The array arguments must have the same length.", nameof(x)); } if (rows < columns) { throw new ArgumentException(Resources.RowsLessThanColumns); } var work = new float[columns*Control.BlockSize]; QRSolve(a, rows, columns, b, columnsB, x, work); } /// /// Solves A*X=B for X using QR factorization of A. /// /// The A matrix. /// 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. /// 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. /// Rows must be greater or equal to columns. public override void QRSolve(float[] a, int rows, int columns, float[] b, int columnsB, float[] x, float[] work, QRMethod method = QRMethod.Full) { if (a == null) { throw new ArgumentNullException(nameof(a)); } if (b == null) { throw new ArgumentNullException(nameof(b)); } if (x == null) { throw new ArgumentNullException(nameof(x)); } if (work == null) { throw new ArgumentNullException(nameof(work)); } if (a.Length != rows*columns) { throw new ArgumentException("The array arguments must have the same length.", nameof(a)); } if (b.Length != rows*columnsB) { throw new ArgumentException("The array arguments must have the same length.", nameof(b)); } if (x.Length != columns*columnsB) { throw new ArgumentException("The array arguments must have the same length.", nameof(x)); } if (rows < columns) { throw new ArgumentException(Resources.RowsLessThanColumns); } if (work.Length < 1) { work[0] = rows*Control.BlockSize; throw new ArgumentException(Resources.WorkArrayTooSmall, nameof(work)); } SafeNativeMethods.s_qr_solve(rows, columns, columnsB, a, b, x, work, work.Length); } /// /// 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. /// Contains additional information on Q. Only used for the native solver /// and can be null for the managed provider. /// The B matrix. /// The number of columns of B. /// On exit, the solution matrix. /// Rows must be greater or equal to columns. [SecuritySafeCritical] public override void QRSolveFactored(float[] q, float[] r, int rowsR, int columnsR, float[] tau, float[] b, int columnsB, float[] x, QRMethod method = QRMethod.Full) { if (r == null) { throw new ArgumentNullException(nameof(r)); } if (q == null) { throw new ArgumentNullException(nameof(q)); } if (b == null) { throw new ArgumentNullException(nameof(q)); } if (x == null) { throw new ArgumentNullException(nameof(q)); } if (r.Length != rowsR*columnsR) { throw new ArgumentException("The array arguments must have the same length.", nameof(r)); } if (q.Length != rowsR*rowsR) { throw new ArgumentException("The array arguments must have the same length.", nameof(q)); } if (b.Length != rowsR*columnsB) { throw new ArgumentException("The array arguments must have the same length.", nameof(b)); } if (x.Length != columnsR*columnsB) { throw new ArgumentException("The array arguments must have the same length.", nameof(x)); } if (rowsR < columnsR) { throw new ArgumentException(Resources.RowsLessThanColumns); } var work = new float[columnsR*Control.BlockSize]; QRSolveFactored(q, r, rowsR, columnsR, tau, b, columnsB, x, work); } /// /// Solves A*X=B for X using a previously QR factored matrix. /// /// The Q matrix obtained by QR factor. This is only used for the managed provider and can be /// null for the native provider. The native provider uses the Q portion stored in the R matrix. /// The R matrix obtained by calling . /// The number of rows in the A matrix. /// The number of columns in the A matrix. /// Contains additional information on Q. Only used for the native solver /// and can be null for the managed provider. /// On entry the B matrix; on exit the X matrix. /// The number of columns of B. /// On exit, the solution matrix. /// The work array - only used in the native provider. 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. /// Rows must be greater or equal to columns. public override void QRSolveFactored(float[] q, float[] r, int rowsR, int columnsR, float[] tau, float[] b, int columnsB, float[] x, float[] work, QRMethod method = QRMethod.Full) { if (r == null) { throw new ArgumentNullException(nameof(r)); } if (q == null) { throw new ArgumentNullException(nameof(q)); } if (b == null) { throw new ArgumentNullException(nameof(q)); } if (x == null) { throw new ArgumentNullException(nameof(q)); } if (work == null) { throw new ArgumentNullException(nameof(work)); } if (r.Length != rowsR*columnsR) { throw new ArgumentException("The array arguments must have the same length.", nameof(r)); } if (q.Length != rowsR*rowsR) { throw new ArgumentException("The array arguments must have the same length.", nameof(q)); } if (b.Length != rowsR*columnsB) { throw new ArgumentException("The array arguments must have the same length.", nameof(b)); } if (x.Length != columnsR*columnsB) { throw new ArgumentException("The array arguments must have the same length.", nameof(x)); } if (rowsR < columnsR) { throw new ArgumentException(Resources.RowsLessThanColumns); } if (work.Length < 1) { work[0] = rowsR*Control.BlockSize; throw new ArgumentException(Resources.WorkArrayTooSmall, nameof(work)); } SafeNativeMethods.s_qr_solve_factored(rowsR, columnsR, columnsB, r, b, tau, x, work, work.Length); } /// /// 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. [SecuritySafeCritical] public override void SingularValueDecomposition(bool computeVectors, float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt) { if (a == null) { throw new ArgumentNullException(nameof(a)); } if (s == null) { throw new ArgumentNullException(nameof(s)); } if (u == null) { throw new ArgumentNullException(nameof(u)); } if (vt == null) { throw new ArgumentNullException(nameof(vt)); } if (u.Length != rowsA*rowsA) { throw new ArgumentException("The array arguments must have the same length.", nameof(u)); } if (vt.Length != columnsA*columnsA) { throw new ArgumentException("The array arguments must have the same length.", nameof(vt)); } if (s.Length != Math.Min(rowsA, columnsA)) { throw new ArgumentException("The array arguments must have the same length.", nameof(s)); } 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); } /// /// Solves A*X=B for X using the singular value decomposition of A. /// /// On entry, the M by N matrix to decompose. /// 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 override void SvdSolve(float[] a, int rowsA, int columnsA, float[] b, int columnsB, float[] x) { if (a == null) { throw new ArgumentNullException(nameof(a)); } if (b == null) { throw new ArgumentNullException(nameof(b)); } if (x == null) { throw new ArgumentNullException(nameof(x)); } if (b.Length != rowsA*columnsB) { throw new ArgumentException("The array arguments must have the same length.", nameof(b)); } if (x.Length != columnsA*columnsB) { throw new ArgumentException("The array arguments must have the same length.", nameof(b)); } var work = new float[Math.Max(((3*Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA)), 5*Math.Min(rowsA, columnsA))]; var s = new float[Math.Min(rowsA, columnsA)]; var u = new float[rowsA*rowsA]; var vt = new float[columnsA*columnsA]; var clone = new float[a.Length]; a.Copy(clone); SingularValueDecomposition(true, clone, rowsA, columnsA, s, u, vt, work); SvdSolveFactored(rowsA, columnsA, s, u, vt, b, columnsB, x); } /// /// 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. [SecuritySafeCritical] public override void SingularValueDecomposition(bool computeVectors, float[] a, int rowsA, int columnsA, float[] s, float[] u, float[] vt, float[] work) { if (a == null) { throw new ArgumentNullException(nameof(a)); } if (s == null) { throw new ArgumentNullException(nameof(s)); } if (u == null) { throw new ArgumentNullException(nameof(u)); } if (vt == null) { throw new ArgumentNullException(nameof(vt)); } if (work == null) { throw new ArgumentNullException(nameof(work)); } if (u.Length != rowsA*rowsA) { throw new ArgumentException("The array arguments must have the same length.", nameof(u)); } if (vt.Length != columnsA*columnsA) { throw new ArgumentException("The array arguments must have the same length.", nameof(vt)); } if (s.Length != Math.Min(rowsA, columnsA)) { throw new ArgumentException("The array arguments must have the same length.", nameof(s)); } if (work.Length == 0) { throw new ArgumentException(Resources.ArgumentSingleDimensionArray, nameof(work)); } if (work.Length < Math.Max(((3*Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA)), 5*Math.Min(rowsA, columnsA))) { work[0] = Math.Max((3*Math.Min(rowsA, columnsA)) + Math.Max(rowsA, columnsA), 5*Math.Min(rowsA, columnsA)); throw new ArgumentException(Resources.WorkArrayTooSmall, nameof(work)); } SafeNativeMethods.s_svd_factor(computeVectors, rowsA, columnsA, a, s, u, vt, work, work.Length); } } } #endif