nnfw/include/matrices.h

Go to the documentation of this file.
00001 /********************************************************************************
00002  *  Neural Network Framework.                                                   *
00003  *  Copyright (C) 2005-2011 Gianluca Massera <emmegian@yahoo.it>                *
00004  *                                                                              *
00005  *  This program is free software; you can redistribute it and/or modify        *
00006  *  it under the terms of the GNU General Public License as published by        *
00007  *  the Free Software Foundation; either version 2 of the License, or           *
00008  *  (at your option) any later version.                                         *
00009  *                                                                              *
00010  *  This program is distributed in the hope that it will be useful,             *
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of              *
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               *
00013  *  GNU General Public License for more details.                                *
00014  *                                                                              *
00015  *  You should have received a copy of the GNU General Public License           *
00016  *  along with this program; if not, write to the Free Software                 *
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  *
00018  ********************************************************************************/
00019 
00020 #ifndef MATRICES_H
00021 #define MATRICES_H
00022 
00027 #include "memutils.h"
00028 #include "vectors.h"
00029 #include <exception>
00030 
00031 namespace farsa {
00032 
00043 class FARSA_NNFW_API DoubleMatrix {
00044 public:
00046     DoubleMatrix( unsigned int rows, unsigned int cols, bool isinternal = false ) {
00047         shData = new sharedData();
00048         shData->refcounts = 1;
00049         shData->nrows = rows;
00050         shData->ncols = cols;
00051         shData->tsize = rows*cols;
00052         shData->temporary = false;
00053         shData->alldata = new double[ shData->tsize ];
00054         shData->alldataref = new doubleRef[ shData->tsize ];
00055         shData->rowdata = new DoubleVector[ rows ];
00056         shData->coldata = new DoubleVector[ cols ];
00057         for( unsigned int i=0; i<rows; i++ ) {
00058             // this will also activate isinternal flag on DoubleVectors
00059             shData->rowdata[i].resizeNoData( cols );
00060         }
00061         for( unsigned int i=0; i<cols; i++ ) {
00062             // this will also activate isinternal flag on DoubleVectors
00063             shData->coldata[i].resizeNoData( rows );
00064         }
00065         for( unsigned int t=0; t<shData->tsize; t++ ) {
00066             unsigned int r = t/cols; //--- division may be expensive
00067             unsigned int c = t%cols; //--- module may be expensive
00068             shData->rowdata[r].shData->dataref[c].setRef( shData->alldata + t );
00069             shData->coldata[c].shData->dataref[r].setRef( shData->alldata + t );
00070             shData->alldataref[t].setRef( shData->alldata + t );
00071         }
00072         this->isinternal = isinternal;
00073     };
00075     DoubleMatrix( const DoubleMatrix& src ) {
00076         shData = src.shData;
00077         shData->refcounts++;
00078         //--- is not a temporary anymore !
00079         shData->temporary = false;
00080         isinternal = false;
00081     };
00111     DoubleMatrix(  unsigned int rows, unsigned int cols, bool temp, unsigned int dummy ) {
00112         (void)dummy; // This stops warnings. Assigning dummy a value would have produce a "parameter ‘dummy’ set but not used" warning
00113         shData = new sharedData();
00114         shData->refcounts = 1;
00115         shData->nrows = rows;
00116         shData->ncols = cols;
00117         shData->tsize = rows*cols;
00118         shData->temporary = temp;
00119         shData->alldata = new double[ shData->tsize ];
00120         shData->alldataref = new doubleRef[ shData->tsize ];
00121         shData->rowdata = new DoubleVector[ rows ];
00122         shData->coldata = new DoubleVector[ cols ];
00123         for( unsigned int i=0; i<rows; i++ ) {
00124             // this will also activate isinternal flag on DoubleVectors
00125             shData->rowdata[i].resizeNoData( cols );
00126         }
00127         for( unsigned int i=0; i<cols; i++ ) {
00128             // this will also activate isinternal flag on DoubleVectors
00129             shData->coldata[i].resizeNoData( rows );
00130         }
00131         for( unsigned int t=0; t<shData->tsize; t++ ) {
00132             unsigned int r = t/cols; //--- division may be expensive
00133             unsigned int c = t%cols; //--- module may be expensive
00134             shData->rowdata[r].shData->dataref[c].setRef( shData->alldata + t );
00135             shData->coldata[c].shData->dataref[r].setRef( shData->alldata + t );
00136             shData->alldataref[t].setRef( shData->alldata + t );
00137         }
00138         this->isinternal = false;
00139     };
00141     ~DoubleMatrix() {
00142         shData->refcounts -= 1;
00143         if ( shData->refcounts == 0 ) {
00144             //--- the last destroy the data
00145             if ( shData->tsize > 0 ) {
00146                 delete [](shData->rowdata);
00147                 delete [](shData->coldata);
00148                 delete [](shData->alldataref);
00149                 delete [](shData->alldata);
00150             }
00151             delete shData;
00152         }
00153     };
00155     unsigned int rows() const {
00156         return shData->nrows;
00157     };
00159     unsigned int cols() const {
00160         return shData->ncols;
00161     };
00163     unsigned int size() const {
00164         return shData->tsize;
00165     };
00167     bool operator==( const DoubleMatrix& right ) const {
00168         if ( shData == right.shData ) return true;
00169         if ( shData->tsize != right.shData->tsize ) return false;
00170         if ( shData->nrows != right.shData->nrows ) return false;
00171         if ( shData->ncols != right.shData->ncols ) return false;
00172         for( unsigned int i=0; i<shData->tsize; i++ ) {
00173             if ( shData->alldata[i] != right.shData->alldata[i] ) return false;
00174         }
00175         return true;
00176     };
00178     bool operator!=( const DoubleMatrix& right ) const {
00179         return !( *this == right );
00180     };
00184     DoubleMatrix& resize( unsigned int rows, unsigned int cols ) {
00185         if ( isinternal ) throw MatrixResizeNotAllowed();
00186         DoubleMatrix newMatrix( rows, cols );
00187         newMatrix.copyValues( *this );
00188         //--- the assignment will release the shared data, so there is no need to call detach()
00189         (*this) = newMatrix;
00190         return (*this);
00191     };
00198     DoubleMatrix& operator=( const DoubleMatrix& src ) {
00199         if ( isinternal ) throw MatrixAssignmentNotAllowed();
00200         if ( shData == src.shData ) return (*this);
00201         //--- eliminate the previous data
00202         shData->refcounts -= 1;
00203         if ( shData->refcounts == 0 ) {
00204             //--- the last destroy the data
00205             if ( shData->tsize > 0 ) {
00206                 delete [](shData->rowdata);
00207                 delete [](shData->coldata);
00208                 delete [](shData->alldata);
00209             }
00210             delete shData;
00211         }
00212         //--- set the new data taken from src
00213         shData = src.shData;
00214         shData->refcounts++;
00215         //--- is not a temporary anymore !
00216         shData->temporary = false;
00217         return (*this);
00218     };
00225     DoubleMatrix& copyValues( const DoubleMatrix& src ) {
00226         // if shared data is the same, they already share the same values
00227         if ( shData == src.shData ) return (*this);
00228         detach();
00229         unsigned int rowMax = qMin( shData->nrows, src.shData->nrows );
00230         unsigned int colMax = qMin( shData->ncols, src.shData->ncols );
00231         for( unsigned int r=0; r<rowMax; r++ ) {
00232             for( unsigned int c=0; c<colMax; c++ ) {
00233                 shData->rowdata[r][c] = src.shData->rowdata[r][c];
00234             }
00235         }
00236         return (*this);
00237     };
00247     DoubleMatrix& copyValues( const DoubleMatrix& src, unsigned int srcRowOffset, unsigned int srcColOffset, unsigned int thisRowOffset, unsigned int thisColOffset ) {
00248         // if shared data is the same and offsets are zero, then there is nothing to do
00249         if ( shData == src.shData && srcRowOffset == 0 && srcColOffset == 0 && thisRowOffset == 0 && thisColOffset == 0 ) {
00250             return (*this);
00251         }
00252         detach();
00253         unsigned int rowMax = qMin( shData->nrows-thisRowOffset, src.shData->nrows-srcRowOffset );
00254         unsigned int colMax = qMin( shData->ncols-thisColOffset, src.shData->ncols-thisColOffset );
00255         for( unsigned int r=0; r<rowMax; r++ ) {
00256             for( unsigned int c=0; c<colMax; c++ ) {
00257                 shData->rowdata[r+thisRowOffset][c+thisColOffset] = src.shData->rowdata[r+srcRowOffset][c+srcColOffset];
00258             }
00259         }
00260         return (*this);
00261     };
00275     DoubleMatrix& copyValues( const DoubleMatrix& src, unsigned int srcRowOffset, unsigned int srcColOffset, unsigned int thisRowOffset, unsigned int thisColOffset, unsigned int rowStride, unsigned int colStride ) {
00276         if ( rowStride == 0 || colStride == 0 ) return (*this);
00277         // if shared data is the same and offsets are zero, then there is nothing to do
00278         if ( shData == src.shData && srcRowOffset == 0 && srcColOffset == 0 && thisRowOffset == 0 && thisColOffset == 0 ) {
00279             return (*this);
00280         }
00281         detach();
00282         unsigned int rowMax = qMin( shData->nrows-thisRowOffset, src.shData->nrows-srcRowOffset );
00283         unsigned int colMax = qMin( shData->ncols-thisColOffset, src.shData->ncols-thisColOffset );
00284         for( unsigned int r=0; r<rowMax; r=r+rowStride ) {
00285             for( unsigned int c=0; c<colMax; c=c+colStride ) {
00286                 shData->rowdata[r+thisRowOffset][c+thisColOffset] = src.shData->rowdata[r+srcRowOffset][c+srcColOffset];
00287             }
00288         }
00289         return (*this);
00290     };
00294     DoubleMatrix& detach() {
00295         if ( shData->refcounts > 1 ) {
00296             sharedData* tmp = new sharedData();
00297             tmp->refcounts = 1;
00298             tmp->nrows = shData->nrows;
00299             tmp->ncols = shData->ncols;
00300             tmp->tsize = shData->tsize;
00301             tmp->temporary = false;
00302             tmp->alldata = new double[shData->tsize];
00303             tmp->rowdata = new DoubleVector[ shData->nrows ];
00304             tmp->coldata = new DoubleVector[ shData->ncols ];
00305             for( unsigned int i=0; i<shData->nrows; i++ ) {
00306                 tmp->rowdata[i].resizeNoData( shData->ncols );
00307             }
00308             for( unsigned int i=0; i<shData->ncols; i++ ) {
00309                 tmp->coldata[i].resizeNoData( shData->nrows );
00310             }
00311             for( unsigned int t=0; t<tmp->tsize; t++ ) {
00312                 unsigned int r = t/( tmp->ncols ); //--- division may be expensive
00313                 unsigned int c = t%( tmp->ncols ); //--- module may be expensive
00314                 tmp->rowdata[r].shData->dataref[c].setRef( tmp->alldata + t );
00315                 tmp->coldata[c].shData->dataref[r].setRef( tmp->alldata + t );
00316             }
00317             shData->refcounts--;
00318             shData = tmp;
00319         }
00320         return (*this);
00321     };
00325     doubleRef& at( unsigned int row, unsigned int col ) {
00326 #ifdef FARSA_DEBUG
00327         if ( row >= shData->nrows ) throw OutsideMatrixBoundaries("Accessing an element beyond Row boundary of matrix");
00328         if ( col >= shData->ncols ) throw OutsideMatrixBoundaries("Accessing an element beyond Column boundary of matrix");
00329 #endif
00330         detach();
00331         return shData->rowdata[row][col];
00332     };
00336     double at( unsigned int row, unsigned int col ) const {
00337 #ifdef FARSA_DEBUG
00338         if ( row >= shData->nrows ) throw OutsideMatrixBoundaries("Accessing an element beyond Row boundary of matrix");
00339         if ( col >= shData->ncols ) throw OutsideMatrixBoundaries("Accessing an element beyond Column boundary of matrix");
00340 #endif
00341         return shData->rowdata[row][col];
00342     };
00346     DoubleVector& operator[]( unsigned int row ) {
00347 #ifdef FARSA_DEBUG
00348         if ( row >= shData->nrows ) throw OutsideMatrixBoundaries("Accessing an element beyond Row boundary of matrix");
00349 #endif
00350         detach();
00351         return shData->rowdata[row];
00352     };
00356     DoubleVector operator[]( unsigned int row ) const {
00357 #ifdef FARSA_DEBUG
00358         if ( row >= shData->nrows ) throw OutsideMatrixBoundaries("Accessing an element beyond Row boundary of matrix");
00359 #endif
00360         return shData->rowdata[row];
00361     };
00363     DoubleVector& row( unsigned int r ) {
00364 #ifdef FARSA_DEBUG
00365         if ( r >= shData->nrows ) throw OutsideMatrixBoundaries("Accessing an element beyond Row boundary of matrix");
00366 #endif
00367         detach();
00368         return shData->rowdata[r];
00369     };
00371     DoubleVector row( unsigned int r ) const {
00372 #ifdef FARSA_DEBUG
00373         if ( r >= shData->nrows ) throw OutsideMatrixBoundaries("Accessing an element beyond Row boundary of matrix");
00374 #endif
00375         return shData->rowdata[r];
00376     };
00378     DoubleVector& column( unsigned int c ) {
00379 #ifdef FARSA_DEBUG
00380         if ( c >= shData->ncols ) throw OutsideMatrixBoundaries("Accessing an element beyond Column boundary of matrix");
00381 #endif
00382         detach();
00383         return shData->coldata[c];
00384     };
00386     DoubleVector column( unsigned int c ) const {
00387 #ifdef FARSA_DEBUG
00388         if ( c >= shData->ncols ) throw OutsideMatrixBoundaries("Accessing an element beyond Column boundary of matrix");
00389 #endif
00390         return shData->coldata[c];
00391     };
00396     DoubleMatrix& steady( unsigned int i, unsigned int j ) {
00397 #ifdef FARSA_DEBUG
00398         if( i >= shData->nrows || j >= shData->ncols ) throw OutsideMatrixBoundaries("Fixating elements outside boundary");
00399 #endif
00400         detach();
00401         shData->rowdata[i].steady(j);
00402         shData->coldata[j].steady(i);
00403         shData->alldataref[ i*shData->ncols + j ].setSteady();
00404         return (*this);
00405     };
00409     DoubleMatrix& unsteady( unsigned int i, unsigned int j ) {
00410 #ifdef FARSA_DEBUG
00411         if( i >= shData->nrows || j >= shData->ncols ) throw OutsideMatrixBoundaries("Un-Fixating elements outside boundary");
00412 #endif
00413         detach();
00414         shData->rowdata[i].unsteady(j);
00415         shData->coldata[j].unsteady(i);
00416         shData->alldataref[ i*shData->ncols + j ].setNoSteady();
00417         return (*this);
00418     };
00420     bool isSteady( unsigned int i, unsigned int j ) const {
00421 #ifdef FARSA_DEBUG
00422         if( i >= shData->nrows || j >= shData->ncols ) throw OutsideMatrixBoundaries("Accessing elements outside boundary");
00423 #endif
00424         return shData->rowdata[i].isSteady(j);
00425     };
00429     DoubleMatrix& setAll( const double value ) {
00430         detach();
00431         for( unsigned int i=0; i<shData->tsize; i++ ) {
00432             shData->alldataref[i] = value;
00433         }
00434         return (*this);
00435     };
00439     DoubleMatrix& setIdentity() {
00440         detach();
00441         for( unsigned int r=0; r<shData->nrows; r++ ) {
00442             for( unsigned int c=0; c<shData->ncols; c++ ) {
00443                 shData->rowdata[r][c] = (r==c);
00444             }
00445         }
00446         return (*this);
00447     };
00451     DoubleMatrix& zeroing() {
00452         detach();
00453         for( unsigned int i=0; i<shData->tsize; i++ ) {
00454             shData->alldataref[i] = 0;
00455         }
00456         return (*this);
00457     };
00461     const DoubleMatrix operator+( const DoubleMatrix& right ) const {
00462 #ifdef FARSA_DEBUG
00463         if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) {
00464             throw IncompatibleMatrices("Incompatibles Matrices in operator+ (dimension must be equals)");
00465         }
00466 #endif
00467         if ( right.shData->temporary ) {
00468             for( unsigned int i=0; i<shData->tsize; i++ ) {
00469                 right.shData->alldataref[i] = shData->alldata[i] + right.shData->alldata[i];
00470             }
00471             return right;
00472         } else if ( shData->temporary ) {
00473             for( unsigned int i=0; i<shData->tsize; i++ ) {
00474                 shData->alldataref[i] = shData->alldata[i] + right.shData->alldata[i];
00475             }
00476             return (*this);
00477         } else {
00478             DoubleMatrix ret( shData->nrows, shData->ncols, true, 0 );
00479             for( unsigned int i=0; i<shData->tsize; i++ ) {
00480                 ret.shData->alldataref[i] = shData->alldata[i] + right.shData->alldata[i];
00481             }
00482             return ret;
00483         }
00484     };
00488     const DoubleMatrix operator-( const DoubleMatrix& right ) const {
00489 #ifdef FARSA_DEBUG
00490         if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) {
00491             throw IncompatibleMatrices("Incompatibles Matrices in operator- (dimension must be equals)");
00492         }
00493 #endif
00494         if ( right.shData->temporary ) {
00495             for( unsigned int i=0; i<shData->tsize; i++ ) {
00496                 right.shData->alldataref[i] = shData->alldata[i] - right.shData->alldata[i];
00497             }
00498             return right;
00499         } else if ( shData->temporary ) {
00500             for( unsigned int i=0; i<shData->tsize; i++ ) {
00501                 shData->alldataref[i] = shData->alldata[i] - right.shData->alldata[i];
00502             }
00503             return (*this);
00504         } else {
00505             DoubleMatrix ret( shData->nrows, shData->ncols, true, 0 );
00506             for( unsigned int i=0; i<shData->tsize; i++ ) {
00507                 ret.shData->alldataref[i] = shData->alldata[i] - right.shData->alldata[i];
00508             }
00509             return ret;
00510         }
00511     };
00516     const DoubleMatrix operator*( const DoubleMatrix& right ) const {
00517 #ifdef FARSA_DEBUG
00518         if ( shData->ncols != right.shData->nrows ) {
00519             throw IncompatibleMatrices("Incompatibles Matrices in operator* (left matrix column dimension must be equal to right matrix row dimension)");
00520         }
00521 #endif
00522         // FIXME: this is very slow and inefficient way to multiple matrices
00523         DoubleMatrix ret( shData->nrows, right.shData->ncols, true, 0 );
00524         ret.zeroing();
00525         for( unsigned int i=0; i<ret.shData->nrows; i++ ) {
00526             for( unsigned int j=0; j<ret.shData->ncols; j++ ) {
00527                 for( unsigned int r=0; r<shData->ncols; r++ ) {
00528                     ret.shData->rowdata[i][j] += shData->rowdata[i][r] * right.shData->rowdata[r][j];
00529                 }
00530             }
00531         }
00532         return ret;
00533     };
00537     const DoubleMatrix operator%( const DoubleMatrix& right ) const {
00538 #ifdef FARSA_DEBUG
00539         if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) {
00540             throw IncompatibleMatrices("Incompatibles Matrices in operator% (dimension must be equals)");
00541         }
00542 #endif
00543         if ( right.shData->temporary ) {
00544             for( unsigned int i=0; i<shData->tsize; i++ ) {
00545                 right.shData->alldataref[i] = shData->alldata[i] * right.shData->alldata[i];
00546             }
00547             return right;
00548         } else if ( shData->temporary ) {
00549             for( unsigned int i=0; i<shData->tsize; i++ ) {
00550                 shData->alldataref[i] = shData->alldata[i] * right.shData->alldata[i];
00551             }
00552             return (*this);
00553         } else {
00554             DoubleMatrix ret( shData->nrows, shData->ncols, true, 0 );
00555             for( unsigned int i=0; i<shData->tsize; i++ ) {
00556                 ret.shData->alldataref[i] = shData->alldata[i] * right.shData->alldata[i];
00557             }
00558             return ret;
00559         }
00560     };
00564     const DoubleMatrix operator/( const DoubleMatrix& right ) const {
00565 #ifdef FARSA_DEBUG
00566         if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) {
00567             throw IncompatibleMatrices("Incompatibles Matrices in operator/ (dimension must be equals)");
00568         }
00569 #endif
00570         if ( right.shData->temporary ) {
00571             for( unsigned int i=0; i<shData->tsize; i++ ) {
00572                 right.shData->alldataref[i] = shData->alldata[i] / right.shData->alldata[i];
00573             }
00574             return right;
00575         } else if ( shData->temporary ) {
00576             for( unsigned int i=0; i<shData->tsize; i++ ) {
00577                 shData->alldataref[i] = shData->alldata[i] / right.shData->alldata[i];
00578             }
00579             return (*this);
00580         } else {
00581             DoubleMatrix ret( shData->nrows, shData->ncols, true, 0 );
00582             for( unsigned int i=0; i<shData->tsize; i++ ) {
00583                 ret.shData->alldataref[i] = shData->alldata[i] / right.shData->alldata[i];
00584             }
00585             return ret;
00586         }
00587     };
00591     DoubleMatrix& operator+=( const DoubleMatrix& right ) {
00592 #ifdef FARSA_DEBUG
00593         if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) {
00594             throw IncompatibleMatrices("Incompatibles Matrices in operator += (dimension must be equals)");
00595         }
00596 #endif
00597         detach();
00598         for( unsigned int i=0; i<shData->tsize; i++ ) {
00599             shData->alldataref[i] += right.shData->alldata[i];
00600         }
00601         return (*this);
00602     };
00606     DoubleMatrix& operator-=( const DoubleMatrix& right ) {
00607 #ifdef FARSA_DEBUG
00608         if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) {
00609             throw IncompatibleMatrices("Incompatibles Matrices in operator -= (dimension must be equals)");
00610         }
00611 #endif
00612         detach();
00613         for( unsigned int i=0; i<shData->tsize; i++ ) {
00614             shData->alldataref[i] -= right.shData->alldata[i];
00615         }
00616         return (*this);
00617     };
00621     DoubleMatrix& operator%=( const DoubleMatrix& right ) {
00622 #ifdef FARSA_DEBUG
00623         if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) {
00624             throw IncompatibleMatrices("Incompatibles Matrices in operator %= (dimension must be equals)");
00625         }
00626 #endif
00627         detach();
00628         for( unsigned int i=0; i<shData->tsize; i++ ) {
00629             shData->alldataref[i] *= right.shData->alldata[i];
00630         }
00631         return (*this);
00632     };
00636     DoubleMatrix& operator/=( const DoubleMatrix& right ) {
00637 #ifdef FARSA_DEBUG
00638         if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) {
00639             throw IncompatibleMatrices("Incompatibles Matrices in operator /= (dimension must be equals)");
00640         }
00641 #endif
00642         detach();
00643         for( unsigned int i=0; i<shData->tsize; i++ ) {
00644             shData->alldataref[i] /= right.shData->alldata[i];
00645         }
00646         return (*this);
00647     };
00648 
00649 private:
00650     class sharedData {
00651     public:
00653         unsigned int nrows;
00655         unsigned int ncols;
00657         unsigned int tsize;
00659         double* alldata;
00661         doubleRef* alldataref;
00663         DoubleVector* rowdata;
00665         DoubleVector* coldata;
00667         int refcounts;
00669         bool temporary;
00670     };
00672     sharedData* shData;
00677     bool isinternal;
00678 };
00679 
00680 }
00681 
00682 #endif
00683