matrices.h
Go to the documentation of this file.
1 /********************************************************************************
2  * Neural Network Framework. *
3  * Copyright (C) 2005-2011 Gianluca Massera <emmegian@yahoo.it> *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the Free Software *
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
18  ********************************************************************************/
19 
20 #ifndef MATRICES_H
21 #define MATRICES_H
22 
27 #include "memutils.h"
28 #include "vectors.h"
29 #include <exception>
30 
31 namespace farsa {
32 
43 class FARSA_NNFW_API DoubleMatrix {
44 public:
46  DoubleMatrix( unsigned int rows, unsigned int cols, bool isinternal = false ) {
47  shData = new sharedData();
48  shData->refcounts = 1;
49  shData->nrows = rows;
50  shData->ncols = cols;
51  shData->tsize = rows*cols;
52  shData->temporary = false;
53  shData->alldata = new double[ shData->tsize ];
54  shData->alldataref = new doubleRef[ shData->tsize ];
55  shData->rowdata = new DoubleVector[ rows ];
56  shData->coldata = new DoubleVector[ cols ];
57  for( unsigned int i=0; i<rows; i++ ) {
58  // this will also activate isinternal flag on DoubleVectors
59  shData->rowdata[i].resizeNoData( cols );
60  }
61  for( unsigned int i=0; i<cols; i++ ) {
62  // this will also activate isinternal flag on DoubleVectors
63  shData->coldata[i].resizeNoData( rows );
64  }
65  for( unsigned int t=0; t<shData->tsize; t++ ) {
66  unsigned int r = t/cols; //--- division may be expensive
67  unsigned int c = t%cols; //--- module may be expensive
68  shData->rowdata[r].shData->dataref[c].setRef( shData->alldata + t );
69  shData->coldata[c].shData->dataref[r].setRef( shData->alldata + t );
70  shData->alldataref[t].setRef( shData->alldata + t );
71  }
72  this->isinternal = isinternal;
73  };
75  DoubleMatrix( const DoubleMatrix& src ) {
76  shData = src.shData;
77  shData->refcounts++;
78  //--- is not a temporary anymore !
79  shData->temporary = false;
80  isinternal = false;
81  };
111  DoubleMatrix( unsigned int rows, unsigned int cols, bool temp, unsigned int dummy ) {
112  (void)dummy; // This stops warnings. Assigning dummy a value would have produce a "parameter ‘dummy’ set but not used" warning
113  shData = new sharedData();
114  shData->refcounts = 1;
115  shData->nrows = rows;
116  shData->ncols = cols;
117  shData->tsize = rows*cols;
118  shData->temporary = temp;
119  shData->alldata = new double[ shData->tsize ];
120  shData->alldataref = new doubleRef[ shData->tsize ];
121  shData->rowdata = new DoubleVector[ rows ];
122  shData->coldata = new DoubleVector[ cols ];
123  for( unsigned int i=0; i<rows; i++ ) {
124  // this will also activate isinternal flag on DoubleVectors
125  shData->rowdata[i].resizeNoData( cols );
126  }
127  for( unsigned int i=0; i<cols; i++ ) {
128  // this will also activate isinternal flag on DoubleVectors
129  shData->coldata[i].resizeNoData( rows );
130  }
131  for( unsigned int t=0; t<shData->tsize; t++ ) {
132  unsigned int r = t/cols; //--- division may be expensive
133  unsigned int c = t%cols; //--- module may be expensive
134  shData->rowdata[r].shData->dataref[c].setRef( shData->alldata + t );
135  shData->coldata[c].shData->dataref[r].setRef( shData->alldata + t );
136  shData->alldataref[t].setRef( shData->alldata + t );
137  }
138  this->isinternal = false;
139  };
142  shData->refcounts -= 1;
143  if ( shData->refcounts == 0 ) {
144  //--- the last destroy the data
145  if ( shData->tsize > 0 ) {
146  delete [](shData->rowdata);
147  delete [](shData->coldata);
148  delete [](shData->alldataref);
149  delete [](shData->alldata);
150  }
151  delete shData;
152  }
153  };
155  unsigned int rows() const {
156  return shData->nrows;
157  };
159  unsigned int cols() const {
160  return shData->ncols;
161  };
163  unsigned int size() const {
164  return shData->tsize;
165  };
167  bool operator==( const DoubleMatrix& right ) const {
168  if ( shData == right.shData ) return true;
169  if ( shData->tsize != right.shData->tsize ) return false;
170  if ( shData->nrows != right.shData->nrows ) return false;
171  if ( shData->ncols != right.shData->ncols ) return false;
172  for( unsigned int i=0; i<shData->tsize; i++ ) {
173  if ( shData->alldata[i] != right.shData->alldata[i] ) return false;
174  }
175  return true;
176  };
178  bool operator!=( const DoubleMatrix& right ) const {
179  return !( *this == right );
180  };
184  DoubleMatrix& resize( unsigned int rows, unsigned int cols ) {
185  if ( isinternal ) throw MatrixResizeNotAllowed();
186  DoubleMatrix newMatrix( rows, cols );
187  newMatrix.copyValues( *this );
188  //--- the assignment will release the shared data, so there is no need to call detach()
189  (*this) = newMatrix;
190  return (*this);
191  };
199  if ( isinternal ) throw MatrixAssignmentNotAllowed();
200  if ( shData == src.shData ) return (*this);
201  //--- eliminate the previous data
202  shData->refcounts -= 1;
203  if ( shData->refcounts == 0 ) {
204  //--- the last destroy the data
205  if ( shData->tsize > 0 ) {
206  delete [](shData->rowdata);
207  delete [](shData->coldata);
208  delete [](shData->alldata);
209  }
210  delete shData;
211  }
212  //--- set the new data taken from src
213  shData = src.shData;
214  shData->refcounts++;
215  //--- is not a temporary anymore !
216  shData->temporary = false;
217  return (*this);
218  };
226  // if shared data is the same, they already share the same values
227  if ( shData == src.shData ) return (*this);
228  detach();
229  unsigned int rowMax = qMin( shData->nrows, src.shData->nrows );
230  unsigned int colMax = qMin( shData->ncols, src.shData->ncols );
231  for( unsigned int r=0; r<rowMax; r++ ) {
232  for( unsigned int c=0; c<colMax; c++ ) {
233  shData->rowdata[r][c] = src.shData->rowdata[r][c];
234  }
235  }
236  return (*this);
237  };
247  DoubleMatrix& copyValues( const DoubleMatrix& src, unsigned int srcRowOffset, unsigned int srcColOffset, unsigned int thisRowOffset, unsigned int thisColOffset ) {
248  // if shared data is the same and offsets are zero, then there is nothing to do
249  if ( shData == src.shData && srcRowOffset == 0 && srcColOffset == 0 && thisRowOffset == 0 && thisColOffset == 0 ) {
250  return (*this);
251  }
252  detach();
253  unsigned int rowMax = qMin( shData->nrows-thisRowOffset, src.shData->nrows-srcRowOffset );
254  unsigned int colMax = qMin( shData->ncols-thisColOffset, src.shData->ncols-thisColOffset );
255  for( unsigned int r=0; r<rowMax; r++ ) {
256  for( unsigned int c=0; c<colMax; c++ ) {
257  shData->rowdata[r+thisRowOffset][c+thisColOffset] = src.shData->rowdata[r+srcRowOffset][c+srcColOffset];
258  }
259  }
260  return (*this);
261  };
275  DoubleMatrix& copyValues( const DoubleMatrix& src, unsigned int srcRowOffset, unsigned int srcColOffset, unsigned int thisRowOffset, unsigned int thisColOffset, unsigned int rowStride, unsigned int colStride ) {
276  if ( rowStride == 0 || colStride == 0 ) return (*this);
277  // if shared data is the same and offsets are zero, then there is nothing to do
278  if ( shData == src.shData && srcRowOffset == 0 && srcColOffset == 0 && thisRowOffset == 0 && thisColOffset == 0 ) {
279  return (*this);
280  }
281  detach();
282  unsigned int rowMax = qMin( shData->nrows-thisRowOffset, src.shData->nrows-srcRowOffset );
283  unsigned int colMax = qMin( shData->ncols-thisColOffset, src.shData->ncols-thisColOffset );
284  for( unsigned int r=0; r<rowMax; r=r+rowStride ) {
285  for( unsigned int c=0; c<colMax; c=c+colStride ) {
286  shData->rowdata[r+thisRowOffset][c+thisColOffset] = src.shData->rowdata[r+srcRowOffset][c+srcColOffset];
287  }
288  }
289  return (*this);
290  };
295  if ( shData->refcounts > 1 ) {
296  sharedData* tmp = new sharedData();
297  tmp->refcounts = 1;
298  tmp->nrows = shData->nrows;
299  tmp->ncols = shData->ncols;
300  tmp->tsize = shData->tsize;
301  tmp->temporary = false;
302  tmp->alldata = new double[shData->tsize];
303  tmp->rowdata = new DoubleVector[ shData->nrows ];
304  tmp->coldata = new DoubleVector[ shData->ncols ];
305  for( unsigned int i=0; i<shData->nrows; i++ ) {
306  tmp->rowdata[i].resizeNoData( shData->ncols );
307  }
308  for( unsigned int i=0; i<shData->ncols; i++ ) {
309  tmp->coldata[i].resizeNoData( shData->nrows );
310  }
311  for( unsigned int t=0; t<tmp->tsize; t++ ) {
312  unsigned int r = t/( tmp->ncols ); //--- division may be expensive
313  unsigned int c = t%( tmp->ncols ); //--- module may be expensive
314  tmp->rowdata[r].shData->dataref[c].setRef( tmp->alldata + t );
315  tmp->coldata[c].shData->dataref[r].setRef( tmp->alldata + t );
316  }
317  shData->refcounts--;
318  shData = tmp;
319  }
320  return (*this);
321  };
325  doubleRef& at( unsigned int row, unsigned int col ) {
326 #ifdef FARSA_DEBUG
327  if ( row >= shData->nrows ) throw OutsideMatrixBoundaries("Accessing an element beyond Row boundary of matrix");
328  if ( col >= shData->ncols ) throw OutsideMatrixBoundaries("Accessing an element beyond Column boundary of matrix");
329 #endif
330  detach();
331  return shData->rowdata[row][col];
332  };
336  double at( unsigned int row, unsigned int col ) const {
337 #ifdef FARSA_DEBUG
338  if ( row >= shData->nrows ) throw OutsideMatrixBoundaries("Accessing an element beyond Row boundary of matrix");
339  if ( col >= shData->ncols ) throw OutsideMatrixBoundaries("Accessing an element beyond Column boundary of matrix");
340 #endif
341  return shData->rowdata[row][col];
342  };
346  DoubleVector& operator[]( unsigned int row ) {
347 #ifdef FARSA_DEBUG
348  if ( row >= shData->nrows ) throw OutsideMatrixBoundaries("Accessing an element beyond Row boundary of matrix");
349 #endif
350  detach();
351  return shData->rowdata[row];
352  };
356  DoubleVector operator[]( unsigned int row ) const {
357 #ifdef FARSA_DEBUG
358  if ( row >= shData->nrows ) throw OutsideMatrixBoundaries("Accessing an element beyond Row boundary of matrix");
359 #endif
360  return shData->rowdata[row];
361  };
363  DoubleVector& row( unsigned int r ) {
364 #ifdef FARSA_DEBUG
365  if ( r >= shData->nrows ) throw OutsideMatrixBoundaries("Accessing an element beyond Row boundary of matrix");
366 #endif
367  detach();
368  return shData->rowdata[r];
369  };
371  DoubleVector row( unsigned int r ) const {
372 #ifdef FARSA_DEBUG
373  if ( r >= shData->nrows ) throw OutsideMatrixBoundaries("Accessing an element beyond Row boundary of matrix");
374 #endif
375  return shData->rowdata[r];
376  };
378  DoubleVector& column( unsigned int c ) {
379 #ifdef FARSA_DEBUG
380  if ( c >= shData->ncols ) throw OutsideMatrixBoundaries("Accessing an element beyond Column boundary of matrix");
381 #endif
382  detach();
383  return shData->coldata[c];
384  };
386  DoubleVector column( unsigned int c ) const {
387 #ifdef FARSA_DEBUG
388  if ( c >= shData->ncols ) throw OutsideMatrixBoundaries("Accessing an element beyond Column boundary of matrix");
389 #endif
390  return shData->coldata[c];
391  };
396  DoubleMatrix& steady( unsigned int i, unsigned int j ) {
397 #ifdef FARSA_DEBUG
398  if( i >= shData->nrows || j >= shData->ncols ) throw OutsideMatrixBoundaries("Fixating elements outside boundary");
399 #endif
400  detach();
401  shData->rowdata[i].steady(j);
402  shData->coldata[j].steady(i);
403  shData->alldataref[ i*shData->ncols + j ].setSteady();
404  return (*this);
405  };
409  DoubleMatrix& unsteady( unsigned int i, unsigned int j ) {
410 #ifdef FARSA_DEBUG
411  if( i >= shData->nrows || j >= shData->ncols ) throw OutsideMatrixBoundaries("Un-Fixating elements outside boundary");
412 #endif
413  detach();
414  shData->rowdata[i].unsteady(j);
415  shData->coldata[j].unsteady(i);
416  shData->alldataref[ i*shData->ncols + j ].setNoSteady();
417  return (*this);
418  };
420  bool isSteady( unsigned int i, unsigned int j ) const {
421 #ifdef FARSA_DEBUG
422  if( i >= shData->nrows || j >= shData->ncols ) throw OutsideMatrixBoundaries("Accessing elements outside boundary");
423 #endif
424  return shData->rowdata[i].isSteady(j);
425  };
429  DoubleMatrix& setAll( const double value ) {
430  detach();
431  for( unsigned int i=0; i<shData->tsize; i++ ) {
432  shData->alldataref[i] = value;
433  }
434  return (*this);
435  };
440  detach();
441  for( unsigned int r=0; r<shData->nrows; r++ ) {
442  for( unsigned int c=0; c<shData->ncols; c++ ) {
443  shData->rowdata[r][c] = (r==c);
444  }
445  }
446  return (*this);
447  };
452  detach();
453  for( unsigned int i=0; i<shData->tsize; i++ ) {
454  shData->alldataref[i] = 0;
455  }
456  return (*this);
457  };
461  const DoubleMatrix operator+( const DoubleMatrix& right ) const {
462 #ifdef FARSA_DEBUG
463  if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) {
464  throw IncompatibleMatrices("Incompatibles Matrices in operator+ (dimension must be equals)");
465  }
466 #endif
467  if ( right.shData->temporary ) {
468  for( unsigned int i=0; i<shData->tsize; i++ ) {
469  right.shData->alldataref[i] = shData->alldata[i] + right.shData->alldata[i];
470  }
471  return right;
472  } else if ( shData->temporary ) {
473  for( unsigned int i=0; i<shData->tsize; i++ ) {
474  shData->alldataref[i] = shData->alldata[i] + right.shData->alldata[i];
475  }
476  return (*this);
477  } else {
478  DoubleMatrix ret( shData->nrows, shData->ncols, true, 0 );
479  for( unsigned int i=0; i<shData->tsize; i++ ) {
480  ret.shData->alldataref[i] = shData->alldata[i] + right.shData->alldata[i];
481  }
482  return ret;
483  }
484  };
488  const DoubleMatrix operator-( const DoubleMatrix& right ) const {
489 #ifdef FARSA_DEBUG
490  if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) {
491  throw IncompatibleMatrices("Incompatibles Matrices in operator- (dimension must be equals)");
492  }
493 #endif
494  if ( right.shData->temporary ) {
495  for( unsigned int i=0; i<shData->tsize; i++ ) {
496  right.shData->alldataref[i] = shData->alldata[i] - right.shData->alldata[i];
497  }
498  return right;
499  } else if ( shData->temporary ) {
500  for( unsigned int i=0; i<shData->tsize; i++ ) {
501  shData->alldataref[i] = shData->alldata[i] - right.shData->alldata[i];
502  }
503  return (*this);
504  } else {
505  DoubleMatrix ret( shData->nrows, shData->ncols, true, 0 );
506  for( unsigned int i=0; i<shData->tsize; i++ ) {
507  ret.shData->alldataref[i] = shData->alldata[i] - right.shData->alldata[i];
508  }
509  return ret;
510  }
511  };
516  const DoubleMatrix operator*( const DoubleMatrix& right ) const {
517 #ifdef FARSA_DEBUG
518  if ( shData->ncols != right.shData->nrows ) {
519  throw IncompatibleMatrices("Incompatibles Matrices in operator* (left matrix column dimension must be equal to right matrix row dimension)");
520  }
521 #endif
522  // FIXME: this is very slow and inefficient way to multiple matrices
523  DoubleMatrix ret( shData->nrows, right.shData->ncols, true, 0 );
524  ret.zeroing();
525  for( unsigned int i=0; i<ret.shData->nrows; i++ ) {
526  for( unsigned int j=0; j<ret.shData->ncols; j++ ) {
527  for( unsigned int r=0; r<shData->ncols; r++ ) {
528  ret.shData->rowdata[i][j] += shData->rowdata[i][r] * right.shData->rowdata[r][j];
529  }
530  }
531  }
532  return ret;
533  };
537  const DoubleMatrix operator%( const DoubleMatrix& right ) const {
538 #ifdef FARSA_DEBUG
539  if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) {
540  throw IncompatibleMatrices("Incompatibles Matrices in operator% (dimension must be equals)");
541  }
542 #endif
543  if ( right.shData->temporary ) {
544  for( unsigned int i=0; i<shData->tsize; i++ ) {
545  right.shData->alldataref[i] = shData->alldata[i] * right.shData->alldata[i];
546  }
547  return right;
548  } else if ( shData->temporary ) {
549  for( unsigned int i=0; i<shData->tsize; i++ ) {
550  shData->alldataref[i] = shData->alldata[i] * right.shData->alldata[i];
551  }
552  return (*this);
553  } else {
554  DoubleMatrix ret( shData->nrows, shData->ncols, true, 0 );
555  for( unsigned int i=0; i<shData->tsize; i++ ) {
556  ret.shData->alldataref[i] = shData->alldata[i] * right.shData->alldata[i];
557  }
558  return ret;
559  }
560  };
564  const DoubleMatrix operator/( const DoubleMatrix& right ) const {
565 #ifdef FARSA_DEBUG
566  if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) {
567  throw IncompatibleMatrices("Incompatibles Matrices in operator/ (dimension must be equals)");
568  }
569 #endif
570  if ( right.shData->temporary ) {
571  for( unsigned int i=0; i<shData->tsize; i++ ) {
572  right.shData->alldataref[i] = shData->alldata[i] / right.shData->alldata[i];
573  }
574  return right;
575  } else if ( shData->temporary ) {
576  for( unsigned int i=0; i<shData->tsize; i++ ) {
577  shData->alldataref[i] = shData->alldata[i] / right.shData->alldata[i];
578  }
579  return (*this);
580  } else {
581  DoubleMatrix ret( shData->nrows, shData->ncols, true, 0 );
582  for( unsigned int i=0; i<shData->tsize; i++ ) {
583  ret.shData->alldataref[i] = shData->alldata[i] / right.shData->alldata[i];
584  }
585  return ret;
586  }
587  };
592 #ifdef FARSA_DEBUG
593  if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) {
594  throw IncompatibleMatrices("Incompatibles Matrices in operator += (dimension must be equals)");
595  }
596 #endif
597  detach();
598  for( unsigned int i=0; i<shData->tsize; i++ ) {
599  shData->alldataref[i] += right.shData->alldata[i];
600  }
601  return (*this);
602  };
607 #ifdef FARSA_DEBUG
608  if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) {
609  throw IncompatibleMatrices("Incompatibles Matrices in operator -= (dimension must be equals)");
610  }
611 #endif
612  detach();
613  for( unsigned int i=0; i<shData->tsize; i++ ) {
614  shData->alldataref[i] -= right.shData->alldata[i];
615  }
616  return (*this);
617  };
622 #ifdef FARSA_DEBUG
623  if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) {
624  throw IncompatibleMatrices("Incompatibles Matrices in operator %= (dimension must be equals)");
625  }
626 #endif
627  detach();
628  for( unsigned int i=0; i<shData->tsize; i++ ) {
629  shData->alldataref[i] *= right.shData->alldata[i];
630  }
631  return (*this);
632  };
637 #ifdef FARSA_DEBUG
638  if ( shData->tsize != right.shData->tsize || shData->nrows != right.shData->nrows || shData->ncols != right.shData->ncols ) {
639  throw IncompatibleMatrices("Incompatibles Matrices in operator /= (dimension must be equals)");
640  }
641 #endif
642  detach();
643  for( unsigned int i=0; i<shData->tsize; i++ ) {
644  shData->alldataref[i] /= right.shData->alldata[i];
645  }
646  return (*this);
647  };
648 
649 private:
650  class sharedData {
651  public:
653  unsigned int nrows;
655  unsigned int ncols;
657  unsigned int tsize;
659  double* alldata;
661  doubleRef* alldataref;
663  DoubleVector* rowdata;
665  DoubleVector* coldata;
667  int refcounts;
669  bool temporary;
670  };
672  sharedData* shData;
677  bool isinternal;
678 };
679 
680 }
681 
682 #endif
683