// // Math.NET Numerics, part of the Math.NET Project // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // // Copyright (c) 2009-2015 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 NATIVE using System; using System.Security; using IStation.Numerics.Providers.Common.Cuda; using Complex = System.Numerics.Complex; namespace IStation.Numerics.Providers.LinearAlgebra.Cuda { /// /// NVidia's CUDA Toolkit linear algebra provider. /// internal partial class CudaLinearAlgebraProvider { /// /// 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 Complex DotProduct(Complex[] x, Complex[] 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.z_dot_product(_blasHandle, 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(Complex[] y, Complex alpha, Complex[] x, Complex[] 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 == Complex.Zero) { return; } SafeNativeMethods.z_axpy(_blasHandle, 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(Complex alpha, Complex[] x, Complex[] result) { if (x == null) { throw new ArgumentNullException(nameof(x)); } if (!ReferenceEquals(x, result)) { Array.Copy(x, 0, result, 0, x.Length); } if (alpha == Complex.One) { return; } SafeNativeMethods.z_scale(_blasHandle, 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 Complex.One and beta set to Complex.Zero, and x and y are not transposed. public override void MatrixMultiply(Complex[] x, int rowsX, int columnsX, Complex[] y, int rowsY, int columnsY, Complex[] result) { MatrixMultiplyWithUpdate(Transpose.DontTranspose, Transpose.DontTranspose, Complex.One, x, rowsX, columnsX, y, 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. [SecuritySafeCritical] public override void MatrixMultiplyWithUpdate(Transpose transposeA, Transpose transposeB, Complex alpha, Complex[] a, int rowsA, int columnsA, Complex[] b, int rowsB, int columnsB, Complex beta, Complex[] 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.z_matrix_multiply(_blasHandle, transposeA.ToCUDA(), transposeB.ToCUDA(), 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 Complex.One /// 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(Complex[] 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)); } Solver(SafeNativeMethods.z_lu_factor(_solverHandle, 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(Complex[] 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)); } Solver(SafeNativeMethods.z_lu_inverse(_solverHandle, _blasHandle, order, a)); } /// /// 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(Complex[] 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)); } BLAS(SafeNativeMethods.z_lu_inverse_factored(_blasHandle, order, a, 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 . /// 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, Complex[] a, int order, Complex[] 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."); } Solver(SafeNativeMethods.z_lu_solve(_solverHandle, 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, Complex[] a, int order, int[] ipiv, Complex[] 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."); } Solver(SafeNativeMethods.z_lu_solve_factored(_solverHandle, 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(Complex[] 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)); } Solver(SafeNativeMethods.z_cholesky_factor(_solverHandle, order, a)); } /// /// 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(Complex[] a, int orderA, Complex[] 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."); } Solver(SafeNativeMethods.z_cholesky_solve(_solverHandle, 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(Complex[] a, int orderA, Complex[] 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."); } Solver(SafeNativeMethods.z_cholesky_solve_factored(_solverHandle, orderA, columnsB, a, b)); } /// /// 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(Complex[] a, int rowsA, int columnsA, Complex[] b, int columnsB, Complex[] 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 s = new Complex[Math.Min(rowsA, columnsA)]; var u = new Complex[rowsA*rowsA]; var vt = new Complex[columnsA*columnsA]; var clone = new Complex[a.Length]; a.Copy(clone); SingularValueDecomposition(true, clone, rowsA, columnsA, s, u, vt); 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. /// This is equivalent to the GESVD LAPACK routine. [SecuritySafeCritical] public override void SingularValueDecomposition(bool computeVectors, Complex[] a, int rowsA, int columnsA, Complex[] s, Complex[] u, Complex[] 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)); } if (columnsA > rowsA || !computeVectors) // see remarks http://docs.nvidia.com/cuda/cusolver/index.html#cuds-lt-t-gt-gesvd base.SingularValueDecomposition(computeVectors, a, rowsA, columnsA, s, u, vt); else Solver(SafeNativeMethods.z_svd_factor(_solverHandle, computeVectors, rowsA, columnsA, a, s, u, vt)); } } } #endif