// // Matrix.cpp // // This file contains function definitions for the Matrix class. // This code is part of the Bioinformatics Template Library (BTL). // // Copyright (C) 1997 Birkbeck College, Malet Street, London WC1E 7HX, U.K. // (classlib@mail.cryst.bbk.ac.uk) // // This library is free software; you can // redistribute it and/or modify it under the terms of // the GNU Library General Public License as published by the Free // Software Foundation; either version 2 of the License, or (at your // option) any later version. This library is distributed in the hope // that it will be useful, but WITHOUT ANY WARRANTY; without even the // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the GNU Library General Public License for more details. // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. //////////////////////////////////////////////////////////////////////////// #include "Typedefn.h" #include "MathVector.h" #include "Matrix.h" #include "NumericLimits.h" #include "NumericUtilities.h" #include #include #include #include //............................................................................ // Subroutine to calculate the single value decomposition of a Matrix: // // A = U * L * Vtrans // // where A is this matrix. // // Output: // Matrix U is written over this Matrix, V is output, L is a diagonal // matrix and is output as a Vector. void Matrix::SVD(Vector& L, Matrix& V) { if (L.size() != ncols) FATAL_ERROR("The size of the input Vector must equal the number of " "columns in this Matrix."); if (V.nrows != ncols || V.ncols != ncols) FATAL_ERROR("The number of columns and rows of the input Matrix must " "equal the number of columns in this Matrix."); Matrix ATA = (*this) & (*this); ATA.Eigens(L,V); // Find inverse of Matrix with L at its diagonal. for (size_type i=1; i<=ncols; i++) L(i) = sqrt(L(i)); // U = A * V * Linv // Linv is a diagonal matrix and is represented here as a Vector. // N.B. x /= y is often less efficient than x *= 1/y // (*this) = (*this) * V; for (size_type row=1; row<=nrows; row++) for (size_type col=1; col<=ncols; col++) (*this)(row,col) *= 1/L(col); } //............................................................................. // Subroutine to compute eigenvalues & eigenvectors of a real symmetric // matrix, from IBM SSP manual (see p165). // // Ian Tickle April 1992 // // (modified by David Moss February 1993) // // Eigenvalues & vectors of real symmetric matrix stored in triangular form. // // Arguments: // // mv = 0 to compute eigenvalues only. // mv = 1 to compute eigenvalues & eigenvectors. // // n - supplied dimension of n*n matrix. // // a - supplied array of size n*(n+1)/2 containing n*n matrix in the order: // // 1 2 3 ... // 1 a[0] // 2 a[1] a[2] // 3 a[3] a[4] a[5] ... // ... // NOTE a is used as working space and is overwritten on return. // Eigenvalues are written into diagonal elements of a // i.e. a[0] a[2] a[5] for a 3*3 matrix. // // r - Resultant matrix of eigenvectors stored columnwise in the same // order as eigenvalues. void Matrix::Eigen(int mv, int n, long double* a, long double* r) const { int i, il, ilq, ilr, im, imq, imr, ind, iq, j, l, ll, lm, lq, m, mm, mq; long double anorm, anrmx, cosx, cosx2, sincs, sinx, sinx2, thr, x, y; long double theshold = LDBL_EPSILON; if (mv) { for (i=1; i0.) { anorm=sqrt(2.*anorm); anrmx=theshold*anorm/n; /* Compute threshold and initialise flag. */ thr=anorm; do { thr/=n; do { ind=0; l=0; do { lq=l*(l+1)/2; ll=l+lq; m=l+1; ilq=n*l; do /* Compute sin & cos. */ { mq=m*(m+1)/2; lm=l+mq; if (fabs(a[lm])>=thr) { ind=1; mm=m+mq; x=.5*(a[ll]-a[mm]); y=-a[lm]/sqrt(a[lm]*a[lm]+x*x); if (x<0.) y=-y; sinx=y/sqrt(2.*(1.+(sqrt(1.-y*y)))); sinx2=sinx*sinx; cosx=sqrt(1.-sinx2); cosx2=cosx*cosx; sincs=sinx*cosx; /* Rotate l & m columns. */ imq=n*m; for (i=0; ianrmx); } } // Matrix::eigen //............................................................................. // Sort eigenvalues & eigenvectors (if mv=1) in order of descending eigenvalue. // Arguments are as for eigen, which must have been called. void Matrix::Esort(int mv, int n, long double* a, long double* r) const { int i, im, j, k, km, l, m; long double am; k=0; for (i=0; ii && a[l]>am) { im=j; km=l; am=a[l]; } l+=j+2; } if (im!=i) { a[km]=a[k]; a[k]=am; if (mv) { l=n*i; m=n*im; for (j=0; j= 1) Matrix::value_type Matrix::operator()(const size_type& i, const size_type& j) const { #if defined(DEBUG_VERSION) // Bounds checks // if (i<1 || j<1 || i>nrows || j>ncols) FATAL_ERROR("Matrix indices out of range"); #endif return mat[(ncols*(i-1) + j - 1)]; } //............................................................................. // Read/write access a matrix element given its indices e.g. x(i,j) i = row, // j = col N.B. (i,j >= 1) Matrix::value_type& Matrix::operator()(const size_type& i, const size_type& j) { #if defined(DEBUG_VERSION) // Bounds checks // if (i<1 || j<1 || i>nrows || j>ncols) FATAL_ERROR("Matrix indices out of range"); #endif return mat[(ncols*(i-1) + j - 1)]; } //............................................................................. // Returns an iterator that points the first elment of a given row // N.B. ( 0 <= i < nrows )"] */ Matrix::iterator Matrix::operator[](const size_type& i) { #if defined(DEBUG_VERSION) // Bounds checks // if (i>=nrows) FATAL_ERROR("Matrix index out of range"); #endif return (&mat[0] + ncols*i); } //............................................................................. // Returns a const_iterator that points the first elment of a given row // N.B. ( 0 <= i < nrows )"] */ Matrix::const_iterator Matrix::operator[](const size_type& i) const { #if defined(DEBUG_VERSION) // Bounds checks // if (i>=nrows) FATAL_ERROR("Matrix index out of range"); #endif return (&mat[0] + ncols*i); } //............................................................................. // Matrix multiplication Matrix Matrix::operator*(const Matrix& m) const { if (ncols != m.nrows) FATAL_ERROR("The number of rows in the input Matrix must equal the " "number of columns in this Matrix"); Matrix answer(nrows,m.ncols); for (size_type i=1; i<=nrows; i++) for (size_type j=1; j<=m.ncols; j++) for (size_type k=1; k<=m.nrows; k++) answer(i,j) += (*this)(i,k) * m(k,j); return answer; } //............................................................................. // Premultiplication of a vector by a matrix Vector Matrix::operator*(const Vector &v) const { if (ncols != v.size()) FATAL_ERROR("The size of the input Vector must equal the number of " "columns in this Matrix"); Vector v1(nrows); for(size_type i=1, iarr=0; i<=nrows; i++, iarr=ncols*(i-1)) for(size_type j=1; j<=ncols; j++) // v1(i) = *this(i,j) * v(j) v1(i) += mat[iarr + j-1] * v(j); return v1; } //............................................................................. // Postmultiplication of a vector by a matrix Vector operator*(const Vector &v, const Matrix &m) { typedef Matrix::size_type size_type; size_type size = min(m.ReadNRows(),v.size()); if (v.size() != m.ReadNRows()) FATAL_ERROR("The size of the input Vector must equal the number of rows" " in this Matrix"); Vector v1(size); for(size_type i=1, iarr=0; i<=size; i++, iarr=i-1) for(size_type j=1; j<=size; j++) // v1(i)+=v(j)*m(j,i) v1(i) += v(j) * m.mat[(m.ncols*(j-1) + iarr)]; return v1; } //............................................................................. // multiple matrix by a value_type Matrix& Matrix::operator*=(const value_type& r) { for (size_type i=0; i::digits10(); } unsigned int error, maxError=0; for (size_type i=0; i::AreNotEqual(mat[i], m.mat[i]); if (error > maxError) maxError = error; } return maxError; } //............................................................................. // D S Moss May 1991 // // Inverts a symmetric positive definite matrix. // Employs Cholesky decomposition in step (1). // (1)M=LL(tr) (2)compute L(-1) (3)M(-1)=L(-1)(tr)L(-1) // // mat - a pointer to a column of pointers to the rows of a square or lower // triangular matrix containing the lower triangle of M and overwritten with L // then with L(-1) and finally with the lower triangle of M(-1) // void Matrix::CholeskyInverse() { value_type a; size_type i,j,k; if (nrows != ncols) FATAL_ERROR("This Matrix must be square"); // Compute L where M=LL(tr) using Cholesky decomposition. for (i=1; i<=nrows; i++) for (j=1; j<=i; j++) { a=(*this)(i,j); for (k=1; k NumericLimits::min() * 100) (*this)(i,i)=sqrt(a); else { WARNING("A principal minor is singular\n"); (*this)(i,i)=0.0; } else (*this)(i,j)=a/(*this)(j,j); } // compute L(-1) for (j=1; j<=nrows; j++) { (*this)(j,j)=1.0/(*this)(j,j); for (i=j+1; i<=nrows; i++) { a=0.0; for (k=j; k vecLmax) vecLmax = vecL(i); // Set very small values to zero in the matLinv Matrix to reduce rounding // error problems. // Matrix matLinv(ncols,ncols); value_type vecLmin = vecLmax * NumericLimits::epsilon(); {for (size_type i=1; i<=ncols; i++) if (vecL(i) < vecLmin) matLinv(i,i) = 0.0; else matLinv(i,i) = 1.0/vecL(i);} // Replace this Matrix with its inverse *this = matV * matLinv % (*this); } //............................................................................. // Find the largest difference between elements of matrices of the same // dimensions. Matrix::value_type Matrix::MaxDifference(const Matrix& m) const { if (m.ncols != ncols || m.nrows != nrows) { WARNING("The input Matrix should have the same dimensions as " "this Matrix."); return 99999.9; } value_type diff, maxdiff=0.0; for (size_type i=0; i maxdiff) maxdiff = diff; } return maxdiff; } //............................................................................. Matrix Matrix::Minor(size_type i, size_type j) const { if (nrows != ncols) FATAL_ERROR("Cannot calculate Minor of non-square matrix."); if ( i<1 || i>nrows || j<1 || j>ncols) FATAL_ERROR("One or both indexes are out of range"); Matrix minor(nrows-1,ncols-1); for (size_type row = 1; row <= nrows; row++) for (size_type col = 1; col <= ncols; col++) { if (row < i) { if (col < j) minor(row,col) = mat[(ncols*(row-1) + col - 1)]; else if (col > j) minor(row,col-1) = mat[(ncols*(row-1) + col - 1)]; } else if (row > i) { if (col < j) minor(row-1,col) = mat[(ncols*(row-1) + col - 1)]; else if (col > j) minor(row-1,col-1) = mat[(ncols*(row-1) + col - 1)]; } } return minor; } //............................................................................. // Copy a row of this Matrix into a Vector Vector Matrix::ReadRow(const size_type& rowIndx) const { Vector vec(ncols); if (rowIndx>nrows || rowIndx<1) FATAL_ERROR("Matrix index out of range"); size_type rowarr = ncols * (rowIndx-1); for (size_type i=0; i::epsilon()) FATAL_ERROR("Singular matrix: Inverse is not defined"); // find Adjoint of this Matrix. Matrix invmat = Adjoint(); // divide thru by the determinant if non-zero invmat /= det; return invmat; } //............................................................................. Matrix Matrix::Transpose() const { Matrix trans(ncols,nrows); for (size_type row = 1; row <= nrows; row++) for (size_type col = 1; col <= ncols; col++) trans(col,row) = mat[ncols*(row-1) + col - 1]; return trans; } //............................................................................. // Write a row of this Matrix from a Vector void Matrix::WriteRow(const Vector& rowVector, const size_type& rowIndx) { if (rowIndx>nrows || rowIndx<1) FATAL_ERROR("Matrix index out of range"); if (rowVector.size() != ncols) FATAL_ERROR("The size of the input Vector must be the same as the " "number of columns in this matrix"); size_type rowarr = ncols * (rowIndx-1); for (size_type i=0; inrows || rowIndx<1) FATAL_ERROR("Matrix index out of range"); size_type rowarr = ncols * (rowIndx-1); for (size_type i=0; i