// $Id: tmatrix.cc,v 1.37 2006-11-02 14:57:20 ansari Exp $ // C.Magneville 04/99 #include "sopnamsp.h" #include "machdefs.h" #include #include #include #include #include "pexceptions.h" #include "tmatrix.h" /*! \class SOPHYA::TMatrix \ingroup TArray The TMatrix class specializes the TArray class for representing two dimensional arrays as matrices. Matrix and vector operations, such as matrix multiplication or transposition is implemented. Sub-matrices, in particular matrix rows and columns can easily and efficiently be extracted and manipulated. It should be noted that a significant memory overhead is associated with small matrices (typically less than 10x10=100 elements).However, higher dimension arrays (3D for examples) can be used to represent large number of small matrices or vectors. \warning Matrix row, column indices (r,c) correspond to array indices (r=jy, c=ix) for CMemoryMapping and to (r=ix, c=jy) for FortranMemoryMapping. \b Matrix is a typedef for double precision floating point matrix ( TMatrix ). \sa SOPHYA::TArray SOPHYA::TVector \sa SOPHYA::Range SOPHYA::Sequence \sa SOPHYA::MathArray SOPHYA::SimpleMatrixOperation The following sample code illustrates vector-matrix multiplication and matrix inversion, using simple gauss inversion. \code #include "array.h" // .... int n = 5; // Size of matrix and vectors here Matrix a(n,n); a = RandomSequence(RandomSequence::Gaussian, 0., 2.5); Vector x(n); x = RegularSequence(1.,3.); Vector b = a*x; cout << " ----- Vector x = \n " << x << endl; cout << " ----- Vector b = a*x = \n " << b << endl; SimpleMatrixOperation smo; Matrix inva = smo.Inverse(a); cout << " ----- Matrix Inverse(a) = \n " << inva << endl; cout << " ----- Matrix a*Inverse(a) = \n " << inva*a << endl; cout << " ----- Matrix Inverse(a)*b (=Inv(a)*a*x) = \n " << inva*b << endl; cout << " ----- Matrix x-Inverse(a)*b = (=0 ?)\n " << x-inva*b << endl; \endcode */ //////////////////////////////////////////////////////////////// //**** Createur, Destructeur //! Default constructor template TMatrix::TMatrix() // Constructeur par defaut. : TArray() { arrtype_ = 1; // Type = Matrix } //! constructor of a matrix with r lines et c columns. /*! \param r : number of rows \param c : number of columns \param mm : define the memory mapping type \param fzero : if \b true , set matrix elements to zero \sa ReSize */ template TMatrix::TMatrix(sa_size_t r,sa_size_t c, short mm, bool fzero) // Construit une matrice de r lignes et c colonnes. : TArray() { if ( (r == 0) || (c == 0) ) throw ParmError("TMatrix::TMatrix(sa_size_t r,sa_size_t c) NRows or NCols = 0"); arrtype_ = 1; // Type = Matrix ReSize(r, c, mm, fzero); } //! Constructor by copy /*! \warning datas are \b SHARED with \b a. \sa NDataBlock::NDataBlock(const NDataBlock&) */ template TMatrix::TMatrix(const TMatrix& a) // Constructeur par copie : TArray(a) { arrtype_ = 1; // Type = Matrix UpdateMemoryMapping(a, SameMemoryMapping); } //! Constructor by copy /*! \param share : if true, share data. If false copy data */ template TMatrix::TMatrix(const TMatrix& a, bool share) // Constructeur par copie avec possibilite de forcer le partage ou non. : TArray(a, share) { arrtype_ = 1; // Type = Matrix UpdateMemoryMapping(a, SameMemoryMapping); } //! Constructor of a matrix from a TArray \b a /*! \param a : TArray to be copied or shared \param share : if true, share data. If false copy data */ template TMatrix::TMatrix(const TArray& a, bool share) : TArray(a, share) { if (a.NbDimensions() > 2) throw SzMismatchError("TMatrix::TMatrix(const TArray& a, ...) a.NbDimensions()>2"); if (a.NbDimensions() == 1) { size_[1] = 1; step_[1] = size_[0]*step_[0]; ndim_ = 2; } arrtype_ = 1; // Type = Matrix UpdateMemoryMapping(a, SameMemoryMapping); } //! Constructor of a matrix from a TArray \b a , with a different data type template TMatrix::TMatrix(const BaseArray& a) : TArray() { arrtype_ = 1; // Type = Matrix UpdateMemoryMapping(a, SameMemoryMapping); SetBA(a); } //! Destructor template TMatrix::~TMatrix() { } //! Set matrix equal to \b a and return *this /*! \warning Datas are copied (cloned) from \b a. \sa NDataBlock::operator=(const NDataBlock&) */ template TArray& TMatrix::Set(const TArray& a) { if (a.NbDimensions() > 2) throw SzMismatchError("TMatrix::Set(const TArray& a) a.NbDimensions() > 2"); if ((arrtype_ == 2) && (a.NbDimensions() > 1) && (a.Size(0) > 1) && (a.Size(1) > 1) ) throw SzMismatchError("TMatrix::Set(const TArray& a) Size(0,1)>1 for Vector"); TArray::Set(a); if (NbDimensions() == 1) { size_[1] = 1; step_[1] = size_[0]*step_[0]; ndim_ = 2; } UpdateMemoryMapping(*this, SameMemoryMapping); return(*this); } template TArray& TMatrix::SetBA(const BaseArray& a) { if (a.NbDimensions() > 2) throw SzMismatchError("TMatrix::SetBA(const BaseArray& a) a.NbDimensions() > 2"); if ((arrtype_ == 2) && (a.NbDimensions() > 1) && (a.Size(0) > 1) && (a.Size(1) > 1) ) throw SzMismatchError("TMatrix::Set(const TArray& a) Size(0,1)>1 for Vector"); TArray::SetBA(a); if (NbDimensions() == 1) { size_[1] = 1; step_[1] = size_[0]*step_[0]; ndim_ = 2; } UpdateMemoryMapping(*this, SameMemoryMapping); return(*this); } //! Resize the matrix /*! \param r : number of rows \param c : number of columns \param mm : define the memory mapping type (SameMemoryMapping,CMemoryMapping ,FortranMemoryMapping,DefaultMemoryMapping) \param fzero : if \b true , set matrix elements to zero */ template void TMatrix::ReSize(sa_size_t r, sa_size_t c, short mm, bool fzero) { if(r==0||c==0) throw(SzMismatchError("TMatrix::ReSize r or c==0 ")); if ((arrtype_ == 2) && (r > 1) && (c > 1)) throw(SzMismatchError("TMatrix::ReSize r>1&&c>1 for Vector ")); sa_size_t size[BASEARRAY_MAXNDIMS]; for(int_4 kk=0; kk::ReSize(2, size, 1, fzero); UpdateMemoryMapping(mm); } //! Re-allocate space for the matrix /*! \param r : number of rows \param c : number of columns \param mm : define the memory mapping type \param force : if true re-allocation is forced, if not it occurs only if the required space is greater than the old one. \sa ReSize */ template void TMatrix::Realloc(sa_size_t r,sa_size_t c, short mm, bool force) { if(r==0||c==0) throw(SzMismatchError("TMatrix::Realloc r or c==0 ")); if ((arrtype_ == 2) && (r > 1) && (c > 1)) throw(SzMismatchError("TMatrix::Realloc r>1&&c>1 for Vector ")); sa_size_t size[BASEARRAY_MAXNDIMS]; for(int_4 kk=0; kk::Realloc(2, size, 1, force); UpdateMemoryMapping(mm); } // $CHECK$ Reza 03/2000 Doit-on declarer cette methode const ? //! Return a submatrix define by \b Range \b rline and \b rcol template TMatrix TMatrix::SubMatrix(Range rline, Range rcol) const { Range rx=Range::first(); Range ry=Range::first(); short mm = GetMemoryMapping(); if (mm == CMemoryMapping) { rx = rcol; ry = rline; } else { ry = rcol; rx = rline; } TMatrix sm(SubArray(rx, ry, Range::first(), Range::first(), Range::first(), false), true); sm.UpdateMemoryMapping(mm); return(sm); } //////////////////////////////////////////////////////////////// // Transposition //! Transpose matrix in place, by changing the memory mapping template TMatrix& TMatrix::TransposeSelf() { short vt = (marowi_ == veceli_) ? ColumnVector : RowVector; int_4 rci = macoli_; macoli_ = marowi_; marowi_ = rci; veceli_ = (vt == ColumnVector ) ? marowi_ : macoli_; return(*this); } //! Returns the transpose of the original matrix. /*! The data is shared between the two matrices \return return a new matrix */ template TMatrix TMatrix::Transpose() const { TMatrix tm(*this); tm.TransposeSelf(); return tm; } //! Returns a new matrix, corresponding to the transpose of the original matrix /*! \param mm : define the memory mapping type (SameMemoryMapping,CMemoryMapping,FortranMemoryMapping) \return return a new matrix */ template TMatrix TMatrix::Transpose(short mm) const { if (mm == SameMemoryMapping) mm = GetMemoryMapping(); TMatrix tm(NCols(), NRows(), mm); for(sa_size_t i=0; i TMatrix TMatrix::Rearrange(short mm) const { if ( mm == SameMemoryMapping) mm = GetMemoryMapping(); else if ( (mm != CMemoryMapping) && (mm != FortranMemoryMapping) ) mm = GetDefaultMemoryMapping(); if (mm == GetMemoryMapping()) return (TMatrix(*this, true)); TMatrix tm(NRows(), NCols(), mm); for(sa_size_t i=0; i TMatrix& TMatrix::SetIdentity(IdentityMatrix imx) { if (ndim_ == 0) { sa_size_t sz = imx.Size(); if (sz < 1) sz = 1; ReSize(sz, sz); } T diag = (T)imx.Diag(); if (NRows() != NCols()) throw SzMismatchError("TMatrix::operator= (IdentityMatrix) NRows() != NCols()") ; *this = (T) 0; for(sa_size_t i=0; i string TMatrix::InfoString() const { string rs = "TMatrix<"; rs += typeid(T).name(); char buff[64]; sprintf(buff, ">(NRows=%ld, NCols=%ld)", (long)NRows(), (long)NCols()); rs += buff; return(rs); } //! Print matrix /*! \param os : output stream \param maxprt : maximum numer of print \param si : if true, display attached DvList \param ascd : if true, suppresses the display of line numbers, suitable for ascii dump format. \sa SetMaxPrint */ template void TMatrix::Print(ostream& os, sa_size_t maxprt, bool si, bool ascd) const { if (maxprt < 0) maxprt = max_nprt_; sa_size_t npr = 0; // keep stream's io flags // ios_base::fmtflags ioflg = os.flags(); compil pas sur OSF-cxx // os << right ; compile pas sur OSF-cxx Show(os, si); if (ndim_ < 1) return; // Calcul de la largeur d'impression pour chaque element int fprtw = os.precision()+7; int prtw = 5; if ( (typeid(T) == typeid( int_4 )) || (typeid(T) == typeid( uint_4 )) ) prtw = 8; else if ( (typeid(T) == typeid( int_8 )) || (typeid(T) == typeid( uint_8 )) ) prtw = 11; else if ( typeid(T) == typeid( r_4 ) ) prtw = fprtw; else if ( typeid(T) == typeid( r_8 ) ) prtw = fprtw; else if ( typeid(T) == typeid(complex) ) prtw = fprtw; else if ( typeid(T) == typeid(complex) ) prtw = fprtw; sa_size_t kc,kr; for(kr=0; kr 1) && (size_[macoli_] > 10) && !ascd) os << "----- Line= " << kr << endl; for(kc=0; kc 0) os << " "; os << setw(prtw) << (*this)(kr, kc); npr++; if (npr >= (sa_size_t) maxprt) { if (npr < totsize_) os << "\n .... " << endl; return; } } os << endl; } os << endl; //compile pas sur OSF-cxx os.flags(ioflg); // reset stream io flags } ////////////////////////////////////////////////////////// /////////////// Multiplication matricielle /////////////// ////////////////////////////////////////////////////////// //! Return the matrix product C = (*this)*B /*! \param mm : define the memory mapping type for the return matrix */ ////////////// Routine de base sans optimisation ////////////// /* template TMatrix TMatrix::Multiply(const TMatrix& b, short mm) const { if (NCols() != b.NRows()) throw(SzMismatchError("TMatrix::Multiply(b) NCols() != b.NRows() ") ); if (mm == SameMemoryMapping) mm = GetMemoryMapping(); TMatrix rm(NRows(), b.NCols(), mm); const T * pea; const T * peb; T sum; sa_size_t r,c,k; sa_size_t stepa = Step(ColsKA()); sa_size_t stepb = b.Step(b.RowsKA()); // Calcul de C=rm = A*B (A=*this) for(r=0; r TMatrix TMatrix::Multiply(const TMatrix& b, short mm) const // Calcul de C= rm = A*B (A=*this) // Remember: C-like matrices are column packed // Fortan-like matrices are line packed { if (NCols() != b.NRows()) throw(SzMismatchError("TMatrix::Multiply(b) NCols() != b.NRows() ") ); // Commentaire: pas de difference de vitesse notable selon le mapping de la matrice produit "rm" if (mm == SameMemoryMapping) mm = GetMemoryMapping(); TMatrix rm(NRows(), b.NCols(), mm); // Les "steps" pour l'adressage des colonnes de A et des lignes de B sa_size_t stepa = Step(ColsKA()); sa_size_t stepb = b.Step(b.RowsKA()); // Taille totale des matrices A et B size_t totsiza = this->DataBlock().Size(); size_t totsizb = b.DataBlock().Size(); /////////////////////////////////////////////////////////////////// // On decide si on optimise ou non selon les dimensions de A et B // // (il semble que optimiser ou non ne degrade pas // // beaucoup la vitesse pour les petites matrices) // //////////////////////////////////////////////////////////////////// uint_2 popt = GetMatProdOpt(); bool no_optim = false; // optimization demandee par default if( (popt&(uint_2)1) == 0 ) { // pas d'optimization explicitement demande no_optim = true; } else if( (popt&(uint_2)2) == 0 ) { // pas d'optimization forcee, la methode decide // On part sur une disponibilite dans le cache processeur de 100 ko // (A et B peuvent etre stoquees dans le cache) if((totsiza+totsizb)*sizeof(T)<100000) no_optim = true; } sa_size_t r,c,k; T sum; const T * pe; ///////////////////////////////// // Pas d'optimisation demandee // ///////////////////////////////// if( no_optim ) { //cout<<"no_optim("< copy A to optimize "< acopy(NRows(),NCols(),BaseArray::CMemoryMapping); acopy = *this; rm = acopy.Multiply(b,mm); } else { // on copie B //cout<<"A.col not packed && B.row not packed ==> copy B to optimize "< bcopy(b.NRows(),b.NCols(),BaseArray::FortranMemoryMapping); bcopy = b; rm = Multiply(bcopy,mm); } return rm; } //---- stepb>stepa if(stepa!=1 && stepb!=1 && stepb>stepa) { //cout<<"A.col not packed && B.row not packed ==> optimize on B "<=stepb if(stepa!=1 && stepb!=1) { //cout<<"A.col not packed && B.row not packed ==> optimize on A "<::Multiply(b) Optimize case not treated... Please report BUG !!! "<::Multiply(b) Optimize case not treated... Please report BUG !!! ") ); return rm; } /////////////////////////////////////////////////////////////// #ifdef __CXX_PRAGMA_TEMPLATES__ #pragma define_template TMatrix #pragma define_template TMatrix #pragma define_template TMatrix #pragma define_template TMatrix #pragma define_template TMatrix #pragma define_template TMatrix #pragma define_template TMatrix #pragma define_template TMatrix #pragma define_template TMatrix< complex > #pragma define_template TMatrix< complex > #endif #if defined(ANSI_TEMPLATES) || defined(GNU_TEMPLATES) namespace SOPHYA { template class TMatrix; template class TMatrix; template class TMatrix; template class TMatrix; template class TMatrix; template class TMatrix; template class TMatrix; template class TMatrix; template class TMatrix< complex >; template class TMatrix< complex >; } #endif