/// <summary> /// singular value decomposition /// </summary> /// <param name="X">matrix X. The elements of X will not be altered.</param> /// <param name="U">(return value) left singular vectors of X as columns of matrix U. /// If this parameter is set, it must be not null. It might be an empty array. On return /// it will be set to a physical array accordingly.</param> /// <param name="V">right singular vectors of X as rows of matrix V. /// If this parameter is set, it must be not null. It might be an empty array. On return /// it will be set to a physical array accordingly.</param> /// <param name="small">if true: return only first min(M,N) columns of U and S will be /// of size [min(M,N),min(M,N)]</param> /// <param name="discardFiniteTest">if true: the matrix given will not be checked for infinte or NaN values. If such elements /// are contained nevertheless, this may result in failing convergence or error. In worst case /// the function may hang inside the Lapack lib. Use with care! </param> /// <returns>singluar values as diagonal matrix of same size as X</returns> /// <remarks>the right singular vectors V will be returned as reference array.</remarks> public static ILArray <float> svd(ILArray <fcomplex> X, ref ILArray <fcomplex> U, ref ILArray <fcomplex> V, bool small, bool discardFiniteTest) { if (!X.IsMatrix) { throw new ILArgumentSizeException("svd is defined for matrices only!"); } // early exit for small matrices if (X.Dimensions[1] < 4 && X.Dimensions[0] == X.Dimensions[1]) { switch (X.Dimensions[0]) { case 1: if (!Object.Equals(U, null)) { U = ( fcomplex )1.0; } if (!Object.Equals(V, null)) { V = ( fcomplex )1.0; } return(new ILArray <float> (ILMath.abs(X))); //case 2: // return -1; //case 3: // return -1; } } if (!discardFiniteTest && !all(all(isfinite(X)))) { throw new ILArgumentException("svd: input must have only finite elements!"); } if (Lapack == null) { throw new ILMathException("No Lapack package available."); } // parameter evaluation int M = X.Dimensions[0]; int N = X.Dimensions[1]; int minMN = (M < N) ? M : N; int LDU = M; int LDVT = N; int LDA = M; float [] dS = new float [minMN]; char jobz = (small) ? 'S' : 'A'; fcomplex [] dU = null; fcomplex [] dVT = null; int info = 0; if (!Object.Equals(U, null) || !Object.Equals(V, null)) { // need to return U and VT if (small) { dU = new fcomplex [M * minMN]; dVT = new fcomplex [N * minMN]; } else { dU = new fcomplex [M * M]; dVT = new fcomplex [N * N]; } } else { jobz = 'N'; } if (X.IsReference) { X.Detach(); } // must create copy of input ! fcomplex [] dInput = new fcomplex [X.m_data.Length]; System.Array.Copy(X.m_data, dInput, X.m_data.Length); Lapack.cgesdd(jobz, M, N, dInput, LDA, dS, dU, LDU, dVT, LDVT, ref info); if (info < 0) { throw new ILArgumentException("ILMath.svd: the " + (-info).ToString() + "th argument was invalid."); } if (info > 0) { throw new ILArgumentException("svd was not converging!"); } ILArray <float> ret = null; if (info == 0) { // success if (!Object.Equals(U, null) || !Object.Equals(V, null)) { if (small) { ret = ILArray <float> .zeros(minMN, minMN); } else { ret = ILArray <float> .zeros(M, N); } for (int i = 0; i < minMN; i++) { ret.SetValue(dS[i], i, i); } if (!Object.Equals(U, null)) { U = new ILArray <fcomplex> (dU, M, dU.Length / M); } if (!Object.Equals(V, null)) { V = conj(new ILArray <fcomplex> (dVT, N, dVT.Length / N).T); } } else { ret = new ILArray <float> (dS, minMN, 1); } } return(ret); }