/// <summary> /// Decompose matrix A into uper and lower triangular part. Returns permutation matrix also. /// </summary> /// <param name="A">general input matrix. Size [m x n]</param> /// <param name="U">[output] reference to upper triangular matrix. Size [min(m,n) x n]. Must not be null.</param> /// <param name="P">[output] reference to permutation matrix. Size [min(m,n) x min(m,n)]. Must not be null.</param> /// <returns>lower triangular matrix L of size [m x min(m,n)]</returns> /// <remarks>A is decomposed into L and U, so that the equation /// <code>ILMath.multiply(L,U) == ILMath.multiply(P,A)</code> /// will hold except for round off error. /// <para>L and U will be true lower triangular matrices.</para> /// <example> <code> /// //Let's construct a matrix X: /// ILArray<double> X = new ILArray<double>(new double[]{1, 2, 3, 4, 4, 4, 5, 6, 7},3,3).T; /// // now X.ToString() will give something like: /// // {<Double> 63238509 [3x3] Ref(2) /// //(:,:) /// // 1,00000 2,00000 3,00000 /// // 4,00000 4,00000 4,00000 /// // 5,00000 6,00000 7,00000 /// //} /// // construct references on U and P and call the decomposition /// ILArray<double> U = new ILArray<double>.empty(); /// ILArray<double> P = new ILArray<double>.empty(); /// ILArray<double> L = ILMath.lu(X, ref U, ref P); /// /// // L.ToString() is now: /// // {<Double> 19634871 [3x3] Phys. /// //(:,:) /// // 1,00000 0,00000 0,00000 /// // 0,80000 1,00000 0,00000 /// // 0,20000 -1,00000 1,00000 /// //} /// // U is now: /// //{<Double> 22584602 [3x3] Phys. /// //(:,:) /// // 5,00000 6,00000 7,00000 /// // 0,00000 -0,80000 -1,60000 /// // 0,00000 0,00000 0,00000 /// //} /// // and P is: /// //{<Double> 2192437 [3x3] Phys. /// //(:,:) /// // 0,00000 0,00000 1,00000 /// // 0,00000 1,00000 0,00000 /// // 1,00000 0,00000 0,00000 /// //} /// </code> /// In order to reflect the pivoting done during the decomposition inside ?getrf, the matrix P may be used on A: /// <code> /// (ILMath.multiply(P,A) - ILMath.multiply(L,U)).ToString(); /// // will give: /// //{<Double> 59192235 [3x3] Phys. /// //(:,:) /// // 0,00000 0,00000 0,00000 /// // 0,00000 0,00000 0,00000 /// // 0,00000 0,00000 0,00000 /// //} /// </code> /// </example> /// <para>lu uses the Lapack function ?getrf.</para> /// <para>All of the matrices U,L,P returned will be solid ILArrays.</para> /// </remarks> /// <seealso cref="ILNumerics.BuiltInFunctions.ILMath.lu(ILArray<double>)"/> /// <seealso cref="ILNumerics.BuiltInFunctions.ILMath.lu(ILArray<double>, ref ILArray<double>)"/> /// <exception cref="ILNumerics.Exceptions.ILArgumentException"> if input A is not a matrix.</exception> public static /*!HC:inCls1*/ ILArray <double> lu(/*!HC:inCls1*/ ILArray <double> A, ref /*!HC:inCls1*/ ILArray <double> U, ref /*!HC:inCls1*/ ILArray <double> P) { if (!A.IsMatrix) { throw new ILArgumentSizeException("lu is defined for matrices only!"); } int m = A.Dimensions[0], n = A.Dimensions[1], info = 0, minMN = (m < n)? m : n; /*!HC:inCls1*/ ILArray <double> L = (/*!HC:inCls1*/ ILArray <double>)A.Clone(); int [] pivInd = ILMemoryPool.Pool.New <int>(minMN); /*!HC:lapack_*getrf*/ Lapack.dgetrf(m, n, L.m_data, m, pivInd, ref info); if (info < 0) { ILMemoryPool.Pool.RegisterObject(pivInd); throw new ILArgumentException("lu: illegal parameter error."); //} else if (info > 0) { // // singular diagonal entry found } else { // completed successfuly if (!Object.Equals(U, null)) { if (!Object.Equals(P, null)) { pivInd = perm2indicesForward(pivInd); U = copyUpperTriangle(L, minMN, n); L = copyLowerTriangle(L, m, minMN, /*!HC:unityVal*/ 1.0); P = /*!HC:inCls1*/ ILArray <double> .zeros(minMN, minMN); // construct permutation matrix P for (int r = 0; r < m; r++) { P[r, pivInd[r]] = /*!HC:unityVal*/ 1.0; } } else { pivInd = perm2indicesBackward(pivInd); U = copyUpperTriangle(L, minMN, n); L = copyLowerTrianglePerm(L, m, minMN, /*!HC:unityVal*/ 1.0, pivInd); } } } ILMemoryPool.Pool.RegisterObject(pivInd); return(L); }
static void Main(string[] args) { var x = new float[] { 1.0f, 1.0f, 1.0f }; WriteLine("Level1 BLAS sasum call test."); WriteLine(Blas1.sasum(x.Length, x, 1)); WriteLine("Level1 BLAS scopy call test."); Blas1.scopy(x.Length, x, 1, out var y, 1); for (var i = 0; i < y.Length; i++) { Write(y[i] + " "); } WriteLine("\n"); WriteLine("Level2 BLAS dgemv call test."); var ad = new double[] { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; var xd = new double[] { 1.0, 1.0, 1.0 }; var yd = new double[] { 0.0, 0.0, 0.0 }; Blas2.dgemv(CBlasLayout.RowMajor, CBlasTranspose.NoTrans, 3, 3, 1.0, ad, 3, xd, 1, 1.0, yd, 1); for (var i = 0; i < yd.Length; i++) { Write(yd[i] + " "); } WriteLine("\n"); WriteLine("LAPACK General Matrix call test."); var ag = new double[] { 2.0, 1.0, 1.0, 1.0, 1.0, 2.0, 1.0, 1.0, 1.0, 1.0, 2.0, 1.0, 1.0, 1.0, 1.0, 2.0 }; var bg = new double[] { 6.0, 7.0, 12.0, 15.0 }; Lapack.dgetrf(LapackLayout.RowMajor, 4, 4, ag, 4, out var ipiv); Lapack.dgetrs(LapackLayout.RowMajor, LapackTranspose.N, 4, 1, ag, 4, ipiv, bg, 1); for (var i = 0; i < bg.Length; i++) { Write(bg[i] + " "); } WriteLine(); WriteLine("Please press Enter key..."); ReadLine(); }
/// <summary> /// Determinant of square matrix /// </summary> /// <param name="A">square input matrix</param> /// <returns>determinant of A</returns> /// <remarks><para>The determinant is computed by decomposing A into upper and lower triangular part. Therefore LAPACK function ?getrf is used. <br /> /// Due to the properties of determinants, det(a) is the same as det(L) * det(U),where det(L) can easily be extracted from the permutation indices returned from LU decomposition. det(U) - with U beeing an upper triangular matrix - equals the product of the diagonal elements.</para> /// <para>For scalar A, a plain copy of A is returned.</para></remarks> /// <example>Creating a nonsingular 4x4 (double) matrix and it's determinant /// <code>ILArray<double> A = ILMath.counter(1.0,1.0,4,4); ///A[1] = 0.0; // make A nonsingular ///A[14] = 0.0; //(same as: A[2,3] = 0.0;) /// // A is now: /// //<Double> [4,4] /// //(:,:) 1e+001 * /// // 0,10000 0,50000 0,90000 1,30000 /// // 0,00000 0,60000 1,00000 1,40000 /// // 0,30000 0,70000 1,10000 0,00000 /// // 0,40000 0,80000 1,20000 1,60000 /// ///ILMath.det(A) gives: /// //<Double> -360 ///</code></example> ///<exception cref="ILNumerics.Exceptions.ILArgumentException">if A is empty or not a square matrix</exception> public static /*!HC:inCls1*/ ILArray <double> det(/*!HC:inCls1*/ ILArray <double> A) { if (A.IsScalar) { return(A.C); } if (A.IsEmpty) { throw new ILArgumentException("det: A must be a matrix"); } int m = A.Dimensions[0]; if (m != A.Dimensions[1]) { throw new ILArgumentException("det: matrix A must be square"); } /*!HC:inCls1*/ ILArray <double> L = A.C; int [] pivInd = new int[m]; int info = 0; /*!HC:lapack_*getrf*/ Lapack.dgetrf(m, m, L.m_data, m, pivInd, ref info); if (info < 0) { throw new ILArgumentException("det: illegal parameter error."); } // determine pivoting: number of exchanges /*!HC:inArr1*/ double retA = /*!HC:unityVal*/ 1.0; for (int i = 0; i < m;) { retA *= L.m_data[i * m + i]; if (pivInd[i] != ++i) { retA *= /*!HC:negUnityValNoCmplx*/ -1.0; } } L.Dispose(); return(new /*!HC:inCls1*/ ILArray <double> (retA)); }
/// <summary> /// Solve linear equation system /// </summary> /// <param name="A">Matrix A. Size [n x q]</param> /// <param name="B">'rigth hand side' B. Size [n x m]</param> /// <param name="props">Matrix properties. If defined, no checks are made for the structure of A. If the matrix A was found to be (close to or) singular, the 'MatrixProperties.Singular' flag in props will be set. This flag should be tested on return, in order to verify the reliability of the solution.</param> /// <returns>the solution x solving multiply(A,x) = B. Size [n x m]</returns> /// <remarks><para>depending on the <paramref name="props"/> parameter the equation system will be solved differently for special structures of A: /// <list type="bullet"> /// <item>If A is square (q == n) and an <b>upper or lower triangular</b> matrix, the system will directly be solved via backward- or forward substitution. Therefore the Lapack function ?trtrs will be used, whenever the memory layout of A is suitable. This may be the case even for reference ILArray's! /// <example><![CDATA[ILArray<double> A = ILMath.randn(4); // construct 4 x 4 matrix /// A = A.T; // A is a reference array now! The transpose is fast and does not consume much memory /// // now construct a right side and solve the equations: /// ILArray<double> B = new ILArray<double> (1.0,2.0,3.0).T; /// ILMath.linsolve(A,B); // ... will be carried out via Lapack, even for all arrays involved beeing reference arrays! ]]></example></item> /// <item><para>if A is square and symmetric or hermitian, A will be decomposed into a triangular equation system using cholesky factorization and Lapack. The system is than solved using performant Lapack routines.</para> /// <para>if during the cholesky factorization A was found to be <b>not positive definite</b> - the corresponding flag in props will be cleaned and <c>null</c> will be returned.</para></item> /// <item>otherwise if A is square only, it will be decomposed into upper and lower triangular matrices using LU decomposition and Lapack. The triangular system is than solved using performant Lapack routines.</item> /// <item>otherwise, if A is of size [q x n] and q != n, the system is solved using QR decomposition. A may be rank deficient. The solution is computed by use of the Lapack routine '?gelsy' and may be a reference array.</item> /// </list></para> /// <para>Compatibility with Matlab<sup>(R)</sup>: If A is square, the algorithm used follows the same logic as Matlab up to Rel 14, with the exception of Hessenberg matrices wich are solved via LU factorization here. The un-squared case is handled differently. A direct Lapack driver function (?gelsy) is used here. Therefore the solutions might differ! However, the solution will of course fullfill the equation A * x = B without round off errrors. </para> /// <para>For specifiying the rank of A in the unsquare case (q != n), the eps member from <see cref="ILNumerics.Settings.ILSettings"/> class is used.</para></remarks> public static /*!HC:inCls1*/ ILArray <double> linsolve(/*!HC:inCls1*/ ILArray <double> A, /*!HC:inCls1*/ ILArray <double> B, ref MatrixProperties props) { if (object.Equals(A, null)) { throw new ILArgumentException("linsolve: input argument A must not be null!"); } if (object.Equals(B, null)) { throw new ILArgumentException("linsolve: input argument B must not be null!"); } if (A.IsEmpty || B.IsEmpty) { return /*!HC:inCls1*/ (ILArray <double> .empty(A.Dimensions)); } if (A.Dimensions[0] != B.Dimensions[0]) { throw new ILArgumentException("linsolve: number of rows for matrix A must match number of rows for RHS!"); } int info = 0, m = A.Dimensions[0]; /*!HC:inCls1*/ ILArray <double> ret; if (m == A.Dimensions[1]) { props |= MatrixProperties.Square; if ((props & MatrixProperties.LowerTriangular) != 0) { ret = solveLowerTriangularSystem(A, B, ref info); if (info > 0) { props |= MatrixProperties.Singular; } return(ret); } if ((props & MatrixProperties.UpperTriangular) != 0) { ret = solveUpperTriangularSystem(A, B, ref info); if (info > 0) { props |= MatrixProperties.Singular; } return(ret); } if ((props & MatrixProperties.Hermitian) != 0) { /*!HC:inCls1*/ ILArray <double> cholFact = A.copyUpperTriangle(m); /*!HC:lapack_*potrf*/ Lapack.dpotrf('U', m, cholFact.m_data, m, ref info); if (info > 0) { props ^= MatrixProperties.Hermitian; return(null); } else { // solve ret = (/*!HC:inCls1*/ ILArray <double>)B.Clone(); /*!HC:lapack_*potrs*/ Lapack.dpotrs('U', m, B.Dimensions[1], cholFact.m_data, m, ret.m_data, m, ref info); return(ret); } } else { // attempt complete (expensive) LU factorization /*!HC:inCls1*/ ILArray <double> L = (/*!HC:inCls1*/ ILArray <double>)A.Clone(); int [] pivInd = new int[m]; /*!HC:lapack_*getrf*/ Lapack.dgetrf(m, m, L.m_data, m, pivInd, ref info); if (info > 0) { props |= MatrixProperties.Singular; } ret = (/*!HC:inCls1*/ ILArray <double>)B.Clone(); /*!HC:lapack_*getrs*/ Lapack.dgetrs('N', m, B.Dimensions[1], L.m_data, m, pivInd, ret.m_data, m, ref info); if (info < 0) { throw new ILArgumentException("linsolve: failed to solve via lapack dgetrs"); } return(ret); } } else { // under- / overdetermined system int n = A.Dimensions[1], rank = 0, minMN = (m < n)? m:n, maxMN = (m > n)? m:n; int nrhs = B.Dimensions[1]; if (B.Dimensions[0] != m) { throw new ILArgumentException("linsolve: right hand side matrix B must match input A!"); } /*!HC:inCls1*/ ILArray <double> tmpA = (/*!HC:inCls1*/ ILArray <double>)A.Clone(); if (m < n) { ret = new /*!HC:inCls1*/ ILArray <double> (new /*!HC:inArr1*/ double [n * nrhs], n, nrhs); ret["0:" + (m - 1) + ";:"] = B; } else { ret = (/*!HC:inCls1*/ ILArray <double>)B.Clone(); } int [] JPVT = new int [n]; /*!HC:Lapack.?gelsy*/ Lapack.dgelsy(m, n, B.Dimensions[1], tmpA.m_data, m, ret.m_data, maxMN, JPVT, /*!HC:HycalpEPS*/ ILMath.MachineParameterDouble.eps, ref rank, ref info); if (n < m) { ret = ret[ILMath.vector(0, n - 1), null]; } if (rank < minMN) { props |= MatrixProperties.RankDeficient; } return(ret); } }