/// <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 ILArray <fcomplex> lu(ILArray <fcomplex> A, ref ILArray <fcomplex> U, ref ILArray <fcomplex> 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; ILArray <fcomplex> L = (ILArray <fcomplex>)A.Clone(); int [] pivInd = ILMemoryPool.Pool.New <int>(minMN); Lapack.cgetrf(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, new fcomplex(1.0f, 0.0f)); P = ILArray <fcomplex> .zeros(minMN, minMN); // construct permutation matrix P for (int r = 0; r < m; r++) { P[r, pivInd[r]] = new fcomplex(1.0f, 0.0f); } } else { pivInd = perm2indicesBackward(pivInd); U = copyUpperTriangle(L, minMN, n); L = copyLowerTrianglePerm(L, m, minMN, new fcomplex(1.0f, 0.0f), pivInd); } } } ILMemoryPool.Pool.RegisterObject(pivInd); return(L); }
/// <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 ILArray <fcomplex> det(ILArray <fcomplex> 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"); } ILArray <fcomplex> L = A.C; int [] pivInd = new int[m]; int info = 0; Lapack.cgetrf(m, m, L.m_data, m, pivInd, ref info); if (info < 0) { throw new ILArgumentException("det: illegal parameter error."); } // determine pivoting: number of exchanges fcomplex retA = new fcomplex(1.0f, 0.0f); for (int i = 0; i < m;) { retA *= L.m_data[i * m + i]; if (pivInd[i] != ++i) { retA *= -1.0f; } } L.Dispose(); return(new ILArray <fcomplex> (retA)); }