/******************************************************************** ** Image Component Library (ICL) ** ** ** ** Copyright (C) 2006-2013 CITEC, University of Bielefeld ** ** Neuroinformatics Group ** ** Website: www.iclcv.org and ** ** http://opensource.cit-ec.de/projects/icl ** ** ** ** File : ICLMath/src/ICLMath/DynMatrix.cpp ** ** Module : ICLMath ** ** Authors: Christof Elbrechter ** ** ** ** ** ** GNU LESSER GENERAL PUBLIC LICENSE ** ** This file may be used under the terms of the GNU Lesser General ** ** Public License version 3.0 as published by the ** ** ** ** Free Software Foundation and appearing in the file LICENSE.LGPL ** ** included in the packaging of this file. Please review the ** ** following information to ensure the license requirements will ** ** be met: http://www.gnu.org/licenses/lgpl-3.0.txt ** ** ** ** The development of this software was supported by the ** ** Excellence Cluster EXC 277 Cognitive Interaction Technology. ** ** The Excellence Cluster EXC 277 is a grant of the Deutsche ** ** Forschungsgemeinschaft (DFG) in the context of the German ** ** Excellence Initiative. ** ** ** ********************************************************************/ #include #include #include #include #include // Intel Math Kernel Library #ifdef ICL_HAVE_MKL #include "mkl_types.h" #include "mkl_lapack.h" #endif #include #include using namespace icl::utils; namespace icl{ namespace math{ template static double dot(const DynMatrix &a, const DynMatrix &b){ ICLASSERT_RETURN_VAL(a.dim() == b.dim(),0.0); double s = 0; for(unsigned int i=0;i optimization: Use a boolean array for that template static void get_minor_matrix(const DynMatrix &M,int col, int row, DynMatrix &D){ /// we assert M is squared here, and D has size M.size()-Size(1,1) int nextCol=0,nextRow=0; const unsigned int dim = M.cols(); for(unsigned int i=0;i DynMatrix DynMatrix::inv() const throw (InvalidMatrixDimensionException,SingularMatrixException){ double detVal = det(); if(!detVal) throw SingularMatrixException("Determinant was 0 -> (matrix is singular to machine precision)"); detVal = 1.0/detVal; DynMatrix M(cols()-1,cols()-1),I(cols(),cols()); for(unsigned int i=0;i T DynMatrix::det() const throw (InvalidMatrixDimensionException){ unsigned int order = cols(); if(order != rows()) throw(InvalidMatrixDimensionException("Determinant can only be calculated on squared matrices")); switch(order){ case 0: throw(InvalidMatrixDimensionException("Matrix order must be > 0")); case 1: return *m_data; case 2: return m_data[0]*m_data[3]-m_data[1]*m_data[2]; case 3: { const T *src = m_data; const T &a = *src++; const T &b = *src++; const T &c = *src++; const T &d = *src++; const T &e = *src++; const T &f = *src++; const T &g = *src++; const T &h = *src++; const T &i = *src++; return ( a*e*i + b*f*g + c*d*h ) - ( g*e*c + h*f*a + i*d*b); } case 4: { const T *src = m_data; const T &m00=*src++; const T &m01=*src++; const T &m02=*src++; const T &m03=*src++; const T &m10=*src++; const T &m11=*src++; const T &m12=*src++; const T &m13=*src++; const T &m20=*src++; const T &m21=*src++; const T &m22=*src++; const T &m23=*src++; const T &m30=*src++; const T &m31=*src++; const T &m32=*src++; const T &m33=*src++; return m03 * m12 * m21 * m30-m02 * m13 * m21 * m30-m03 * m11 * m22 * m30+m01 * m13 * m22 * m30+ m02 * m11 * m23 * m30-m01 * m12 * m23 * m30-m03 * m12 * m20 * m31+m02 * m13 * m20 * m31+ m03 * m10 * m22 * m31-m00 * m13 * m22 * m31-m02 * m10 * m23 * m31+m00 * m12 * m23 * m31+ m03 * m11 * m20 * m32-m01 * m13 * m20 * m32-m03 * m10 * m21 * m32+m00 * m13 * m21 * m32+ m01 * m10 * m23 * m32-m00 * m11 * m23 * m32-m02 * m11 * m20 * m33+m01 * m12 * m20 * m33+ m02 * m10 * m21 * m33-m00 * m12 * m21 * m33-m01 * m10 * m22 * m33+m00 * m11 * m22 * m33; } default:{ // the determinant value T det = 0; DynMatrix D(order-1,order-1); for(unsigned int i=0;i void DynMatrix::decompose_QR(DynMatrix &Q, DynMatrix &R) const throw (InvalidMatrixDimensionException,SingularMatrixException) { DynMatrix A = *this; // Working copy DynMatrix a(1,rows()), q(1,rows()); Q.setBounds(cols(),rows()); R.setBounds(cols(),cols()); std::fill(R.begin(),R.end(),0.0); for (unsigned int i=0;i void DynMatrix::decompose_RQ(DynMatrix &R, DynMatrix &Q) const throw (InvalidMatrixDimensionException,SingularMatrixException) { // first reverse the rows of A and transpose it DynMatrix A_(rows(),cols()); for (unsigned int i = 0; i R_(rows(),rows()); DynMatrix Q_(rows(),rows()); A_.decompose_QR(Q_,R_); R.setBounds(rows(),rows()); Q.setBounds(rows(),rows()); // get R by reflecting all entries on the second diagonal for (unsigned int i = 0; i static inline bool is_close_to_zero(const T &t){ //std::cout << "is close to zero (" << t << ") returns " << (fabs(t) < 1E-15 ? "true" : "false") << std::endl; return fabs(t) < 1E-15; } template static inline int find_non_zero_in_col(const DynMatrix &U, int i, int m){ for(int j=i+1;j static inline void swap_range(Iterator beginA, Iterator endA, Iterator beginB){ for(;beginA != endA; ++beginA, ++beginB) { std::swap(*beginA, *beginB); } } template void DynMatrix::decompose_LU(DynMatrix &L, DynMatrix &U, T zeroThreshold) const{ const DynMatrix &A = *this; unsigned int m = A.rows(); unsigned int n = A.cols(); U = A; L = DynMatrix(m,m); for(unsigned int i=0;i p(1,m); for(unsigned int i=0;i L2 = L; for(unsigned int i=0;i DynMatrix DynMatrix::solve_upper_triangular(const DynMatrix &b) const throw(InvalidMatrixDimensionException){ const DynMatrix &M = *this; ICLASSERT_THROW(M.cols() == M.rows(), ICLException("solve_upper_triangular only works for squared matrices")); int m = M.cols(); DynMatrix x(1,m); for(int i=m-1;i>=0;--i){ float r = b[i]; for(int j=m-1;j>i;--j) r -= M(j,i) * x[j]; x[i] = r/M(i,i); } return x; } template DynMatrix DynMatrix::solve_lower_triangular(const DynMatrix &b) const throw(InvalidMatrixDimensionException){ const DynMatrix &M = *this; ICLASSERT_THROW(M.cols() == M.rows(), ICLException("solve_lower_triangular: only works for squared matrices")); int m = M.cols(); DynMatrix x(1,m); for(int i=0;i DynMatrix DynMatrix::solve(const DynMatrix &b, const std::string &method ,T zeroThreshold) throw(InvalidMatrixDimensionException, ICLException, SingularMatrixException){ ICLASSERT_THROW(rows() == b.rows(), InvalidMatrixDimensionException("DynMatrix::solve (Mx=b -> x=M^(-1)b needs M.rows == b.rows)")); if(method == "lu"){ DynMatrix L,U; decompose_LU(L,U); return U.solve_upper_triangular(L.solve_lower_triangular(b)); }else if(method == "svd"){ return pinv(true) * b; }else if(method == "qr"){ return pinv(false) * b; }else if(method == "inv"){ return inv() * b; } throw ICLException("DynMatrix::solve: invalid solve-method"); return DynMatrix(0,0); } template DynMatrix DynMatrix::pinv(bool useSVD, T zeroThreshold) const throw (InvalidMatrixDimensionException,SingularMatrixException, ICLException){ if(useSVD){ DynMatrix U,s,V; try{ svd_dyn(*this,U,s,V); }catch(const ICLException &){ return pinv(false,zeroThreshold); } DynMatrix S(U.cols(), V.rows(),0.0f); for(unsigned int i=0;i zeroThreshold) ? 1.0/s[i] : 0; } return V * S * U.transp(); }else{ DynMatrix Q,R; if(cols() > rows()){ transp().decompose_QR(Q,R); return (R.inv() * Q.transp()).transp(); }else{ decompose_QR(Q,R); return R.inv() * Q.transp(); } } } // fallback template DynMatrix DynMatrix::big_matrix_pinv(T zeroThreshold) const throw (InvalidMatrixDimensionException,SingularMatrixException, ICLException){ return pinv( true, zeroThreshold ); } #ifdef ICL_HAVE_MKL template DynMatrix DynMatrix::big_matrix_pinv(T zeroThreshold, GESDD gesdd, CBLAS_GEMM cblas_gemm) const throw (InvalidMatrixDimensionException,SingularMatrixException, ICLException){ // create a copy of source matrix, because GESDD is destroying the input matrix DynMatrix matrixCopy( *this ); // matrix dimensions int r = rows(); int c = cols(); // calculate SVD DynMatrix Vt, s, U; Vt.setBounds( c, c ); s.setBounds( 1, c ); U.setBounds( c, r ); char jobz = 'S'; // success message int info; // work buffer of size 1 to retrieve correct buffer size from first run of GESDD T* work = (T*) malloc( sizeof( T ) ); ICLASSERT_THROW( work != 0, ICLException("Insufficient memory to allocate work buffer!") ); // integer work buffer int* iwork = (int*) malloc( sizeof( int ) * std::max( 1, 8 * std::min( c, r ) ) ); ICLASSERT_THROW( iwork != 0, 0 ); // first run of GESDD to retrieve correct size of work buffer int lwork = -1; gesdd( &jobz, &c, &r, matrixCopy.begin(), &c, s.begin(), Vt.begin(), &c, U.begin(), &c, work, &lwork, iwork, &info ); ICLASSERT_THROW( info == 0, ICLException("GESDD failed!") ); lwork = work[0]; // free old work buffer and allocate a new one free( work ); work = (T*) malloc( sizeof( T ) * lwork ); ICLASSERT_THROW( work != 0, ICLException("Insufficient memory to allocate work buffer!") ); // compute singular value decomposition of a general rectangular matrix // using a divide and conquer method. gesdd( &jobz, &c, &r, matrixCopy.begin(), &c, s.begin(), Vt.begin(), &c, U.begin(), &c, work, &lwork, iwork, &info ); ICLASSERT_THROW( info == 0, ICLException("GESDD failed!") ); // free buffers free( iwork ); free( work ); // prepare matrix S and check if singular values are below zero threshold DynMatrix S( c, c, 0.0 ); for ( int i(0); i < c; ++i ) S( i, i ) = ( fabs( s[i] ) > zeroThreshold ) ? 1.0 / s[i] : 0.0; // dst = Vt.transp() * S * U.transp(); DynMatrix temp( c, c ); cblas_gemm( CblasRowMajor, CblasTrans, CblasNoTrans, c, c, c, 1.0, Vt.begin(), c, S.begin(), c, 0.0, temp.begin(), c ); DynMatrix pseudoInverse( r, c ); cblas_gemm( CblasRowMajor, CblasNoTrans, CblasTrans, c, r, c, 1.0, temp.begin(), c, U.begin(), c, 0.0, pseudoInverse.begin(), r ); return pseudoInverse; } template<> ICLMath_API DynMatrix DynMatrix::big_matrix_pinv(float zeroThreshold) const throw (InvalidMatrixDimensionException,SingularMatrixException,ICLException){ return big_matrix_pinv(zeroThreshold,sgesdd,cblas_sgemm); } template<> ICLMath_API DynMatrix DynMatrix::big_matrix_pinv(double zeroThreshold) const throw (InvalidMatrixDimensionException,SingularMatrixException,ICLException){ return big_matrix_pinv(zeroThreshold,dgesdd,cblas_dgemm); } #endif // This function was taken from VTK Version 5.6.0 // Jacobi iteration for the solution of eigenvectors/eigenvalues of a nxn // real symmetric matrix. Square nxn matrix a; size of matrix in n; // output eigenvalues in w; and output eigenvectors in v. Resulting // eigenvalues/vectors are sorted in decreasing order; eigenvectors are // normalized. template int jacobi_iterate_vtk(T **a, int n, T *w, T **v){ int i, j, k, iq, ip, numPos; T tresh, theta, tau, t, sm, s, h, g, c, tmp; T bspace[4], zspace[4]; T *b = bspace; T *z = zspace; // only allocate memory if the matrix is large if (n > 4){ b = new T[n]; z = new T[n]; } // initialize for (ip=0; ip 3 && (fabs(w[ip])+g) == fabs(w[ip]) && (fabs(w[iq])+g) == fabs(w[iq])){ a[ip][iq] = 0.0; }else if (fabs(a[ip][iq]) > tresh) { h = w[iq] - w[ip]; if ( (fabs(h)+g) == fabs(h)){ t = (a[ip][iq]) / h; }else { theta = 0.5*h / (a[ip][iq]); t = 1.0 / (fabs(theta)+sqrt(1.0+theta*theta)); if (theta < 0.0){ t = -t; } } c = 1.0 / sqrt(1+t*t); s = t*c; tau = s/(1.0+c); h = t*a[ip][iq]; z[ip] -= h; z[iq] += h; w[ip] -= h; w[iq] += h; a[ip][iq]=0.0; #define ROTATE(a,i,j,k,l) g=a[i][j];h=a[k][l];a[i][j]=g-s*(h+g*tau); \ a[k][l]=h+s*(g-h*tau) // ip already shifted left by 1 unit for (j = 0;j <= ip-1;j++){ ROTATE(a,j,ip,j,iq); } // ip and iq already shifted left by 1 unit for (j = ip+1;j <= iq-1;j++){ ROTATE(a,ip,j,j,iq); } // iq already shifted left by 1 unit for (j=iq+1; j= tmp){ // why exchage if same? k = i; tmp = w[k]; } } if (k != j) { w[k] = w[j]; w[j] = tmp; for (i=0; i> 1) + (n & 1); for (j=0; j= 0.0 ){ numPos++; } } // if ( numPos < ceil(double(n)/double(2.0)) ) if ( numPos < ceil_half_n){ for(i=0; i 4){ delete [] b; delete [] z; } return 1; } template void find_eigenvectors(const DynMatrix &a, DynMatrix &eigenvectors, DynMatrix &eigenvalues, T *buffer = 0){ const int n = a.cols(); T ** pa = new T*[n], *pvalues=new T[n], **pvectors=new T*[n]; for(int i=0;i(pa,n,pvalues,pvectors); for(int i=0;i ICLMath_API void find_eigenvectors(const DynMatrix &a, DynMatrix &eigenvectors, DynMatrix &eigenvalues, icl32f* buffer){ const int n = a.cols(); icl32f * useBuffer = buffer ? buffer : new icl32f[n*n]; IppStatus sts = ippmEigenValuesVectorsSym_m_32f (a.begin(), n*sizeof(icl32f), sizeof(icl32f), useBuffer, eigenvectors.begin(), n*sizeof(icl32f), sizeof(icl32f), eigenvalues.begin(),n); if(!buffer) delete [] useBuffer; if(sts != ippStsNoErr){ throw ICLException(std::string("IPP-Error in ") + __FUNCTION__ + "\"" +ippGetStatusString(sts) +"\""); } } template<> ICLMath_API void find_eigenvectors(const DynMatrix &a, DynMatrix &eigenvectors, DynMatrix &eigenvalues, icl64f* buffer){ const int n = a.cols(); icl64f * useBuffer = buffer ? buffer : new icl64f[n*n]; IppStatus sts = ippmEigenValuesVectorsSym_m_64f (a.begin(), n*sizeof(icl64f), sizeof(icl64f), useBuffer, eigenvectors.begin(), n*sizeof(icl64f), sizeof(icl64f), eigenvalues.begin(),n); if(!buffer) delete [] useBuffer; if(sts != ippStsNoErr){ throw ICLException(std::string("IPP-Error in ") + __FUNCTION__ + "\"" +ippGetStatusString(sts) +"\""); } } #endif template void DynMatrix::eigen(DynMatrix &eigenvectors, DynMatrix &eigenvalues) const throw(InvalidMatrixDimensionException, ICLException){ ICLASSERT_THROW(cols() == rows(), InvalidMatrixDimensionException("find eigenvectors: input matrix a is not a square-matrix")); const int n = cols(); eigenvalues.setBounds(1,n); eigenvectors.setBounds(n,n); find_eigenvectors(*this,eigenvectors,eigenvalues,0); } template void DynMatrix::svd(DynMatrix &V, DynMatrix &s, DynMatrix &U) const throw (ICLException){ svd_dyn(*this,V,s,U); } #ifdef ICL_HAVE_IPP #define DYN_MATRIX_INV(T, ippFunc) \ template<> ICLMath_API DynMatrix DynMatrix::inv() const throw (InvalidMatrixDimensionException,SingularMatrixException){ \ if(this->cols() != this->rows()){ \ throw InvalidMatrixDimensionException("inverse matrix can only be calculated on square matrices"); \ } \ unsigned int wh = this->cols(); \ DynMatrix d(wh, wh, 0.0); \ std::vector buffer(wh*wh + wh); \ IppStatus st = ippFunc(this->data(), wh*sizeof(T), sizeof(T), \ buffer.data(), \ d.data(), wh*sizeof(T), sizeof(T), \ wh); \ if(st != ippStsNoErr){ \ throw SingularMatrixException("matrix is too singular"); \ } \ return d; \ } #define DYN_MATRIX_DET(T, ippFunc) \ template<> ICLMath_API T DynMatrix::det() const throw (InvalidMatrixDimensionException){ \ if(this->cols() != this->rows()){ \ throw InvalidMatrixDimensionException("matrix determinant can only be calculated on square matrices"); \ } \ unsigned int wh = this->cols(); \ std::vector buffer(wh*wh+wh); \ T det(0); \ IppStatus st = ippFunc(this->data(),wh*sizeof(T),sizeof(T), \ wh,buffer.data(),&det); \ if(st != ippStsNoErr){ \ ERROR_LOG("matrix determinant could not be calculated"); \ } \ return det; \ } DYN_MATRIX_INV(float, ippmInvert_m_32f); DYN_MATRIX_INV(double, ippmInvert_m_64f); DYN_MATRIX_DET(float, ippmDet_m_32f); DYN_MATRIX_DET(double, ippmDet_m_64f); #undef DYN_MATRIX_INV #undef DYN_MATRIX_DET #else template ICLMath_API DynMatrix DynMatrix::inv()const throw (InvalidMatrixDimensionException, SingularMatrixException); template ICLMath_API DynMatrix DynMatrix::inv()const throw (InvalidMatrixDimensionException, SingularMatrixException); template ICLMath_API float DynMatrix::det()const throw (InvalidMatrixDimensionException); template ICLMath_API double DynMatrix::det()const throw (InvalidMatrixDimensionException); #endif template ICLMath_API void DynMatrix::svd(DynMatrix&, DynMatrix&, DynMatrix&) const throw (ICLException); template ICLMath_API void DynMatrix::svd(DynMatrix&, DynMatrix&, DynMatrix&) const throw (ICLException); template ICLMath_API void DynMatrix::eigen(DynMatrix&, DynMatrix&) const throw(InvalidMatrixDimensionException, ICLException); template ICLMath_API void DynMatrix::eigen(DynMatrix&, DynMatrix&) const throw(InvalidMatrixDimensionException, ICLException); template ICLMath_API void DynMatrix::decompose_QR(DynMatrix &Q, DynMatrix &R) const throw (InvalidMatrixDimensionException,SingularMatrixException); template ICLMath_API void DynMatrix::decompose_QR(DynMatrix &Q, DynMatrix &R) const throw (InvalidMatrixDimensionException,SingularMatrixException); template ICLMath_API void DynMatrix::decompose_RQ(DynMatrix &R, DynMatrix &Q) const throw (InvalidMatrixDimensionException,SingularMatrixException); template ICLMath_API void DynMatrix::decompose_RQ(DynMatrix &R, DynMatrix &Q) const throw (InvalidMatrixDimensionException,SingularMatrixException); template ICLMath_API void DynMatrix::decompose_LU(DynMatrix &L, DynMatrix &U, float zeroThreshold) const; template ICLMath_API void DynMatrix::decompose_LU(DynMatrix &L, DynMatrix &U, double zeroThreshold) const; template ICLMath_API DynMatrix DynMatrix::solve_upper_triangular(const DynMatrix &b) const throw(InvalidMatrixDimensionException); template ICLMath_API DynMatrix DynMatrix::solve_upper_triangular(const DynMatrix &b) const throw(InvalidMatrixDimensionException); template ICLMath_API DynMatrix DynMatrix::solve_lower_triangular(const DynMatrix &b) const throw(InvalidMatrixDimensionException); template ICLMath_API DynMatrix DynMatrix::solve_lower_triangular(const DynMatrix &b) const throw(InvalidMatrixDimensionException); template ICLMath_API DynMatrix DynMatrix::solve(const DynMatrix &b, const std::string &method, float zeroThreshold) throw(InvalidMatrixDimensionException, ICLException, SingularMatrixException); template ICLMath_API DynMatrix DynMatrix::solve(const DynMatrix &b, const std::string &method, double zeroThreshold) throw(InvalidMatrixDimensionException, ICLException, SingularMatrixException); template ICLMath_API DynMatrix DynMatrix::pinv(bool, float) const throw (InvalidMatrixDimensionException,SingularMatrixException,ICLException); template ICLMath_API DynMatrix DynMatrix::pinv(bool, double) const throw (InvalidMatrixDimensionException,SingularMatrixException,ICLException); template std::ostream &operator<<(std::ostream &s,const DynMatrix &m){ for(unsigned int i=0;i(s,m(j,i)) << " "; } s << "|"; if(i std::istream &operator>>(std::istream &s,DynMatrix &m){ char c; for(unsigned int i=0;i> c; // trailing '|' if ( ((c >= '0') && (c <= '9')) || c=='-' ){ s.unget(); } for(unsigned int j=0;j(s,m(j,i)); s >> c; if( c != ',') s.unget(); } s >> c; // ending '|' if ( ((c >= '0') && (c <= '9')) || c=='-' ){ s.unget(); } } return s; } #define X(T) \ template ICLMath_API std::ostream &operator<<(std::ostream&,const DynMatrix&); \ template ICLMath_API std::istream &operator>>(std::istream&,DynMatrix&) X(uint8_t); X(int16_t); X(int32_t); X(float); X(double); X(bool); X(std::complex); X(std::complex); #undef X template DynMatrix DynMatrix::loadCSV(const std::string &filename) throw (ICLException){ std::ifstream s(filename.c_str()); if(!s.good()) throw ICLException("DynMatrix::loadCSV: invalid filename ' "+ filename +'\''); std::vector data; data.reserve(256); int lineLen = -1; std::string line; while(!s.eof()){ std::getline(s,line); if(!line.length() || line[0] == '#' || line[0] == ' ') continue; std::vector v = icl::utils::parseVecStr(line,","); int cLen = (int)v.size(); if(lineLen == -1) lineLen = cLen; else if(lineLen != cLen){ throw ICLException("DynMatrix::loadCSV: row lengths differ"); } std::copy(v.begin(),v.end(),std::back_inserter(data)); } if(!lineLen) throw ICLException("DynMatrix::loadCSV: no data found in file ' " + filename + '\''); DynMatrix M(lineLen,data.size()/lineLen, data.data()); return M; } /// writes the current matrix to a csv file /** supported types T are all icl8u, icl16s, icl32s, icl32f, icl64f */ template void DynMatrix::saveCSV(const std::string &filename) throw (ICLException){ std::ofstream s(filename.c_str()); if(!s.good()) throw ICLException("DynMatrix::saveCSV:"); for(unsigned int y=0;y DynMatrix::loadCSV(const std::string &filename) throw (ICLException); \ template ICLMath_API void DynMatrix::saveCSV(const std::string&) throw (ICLException); ICL_INSTANTIATE_ALL_DEPTHS; #undef ICL_INSTANTIATE_DEPTH } // namespace math }