CARMA C++
Matrix.h
1 #ifndef SZA_UTIL_MATRIX_H
2 #define SZA_UTIL_MATRIX_H
3 
11 #include <iostream>
12 #include <cmath>
13 
16 #include "carma/szautil/Vector.h"
17 
18 namespace sza {
19  namespace util {
20 
21  // Some C++ ugliness: To declare non-member friend template
22  // functions under the new standard, the function templates must
23  // be forward-declared before appearing in the class definition.
24  // I suspect this won't even compile with gcc versions < 3.2.2
25 
26  template<class type>
27  class Matrix;
28 
29  template<class type>
30  std::ostream& operator<<(std::ostream& os,
31  Matrix<type>& mat);
32 
33  template<class type>
34  std::ostringstream& operator<<(std::ostringstream& os,
35  Matrix<type>& mat);
36 
37  template<class type>
38  Vector<type> operator*(Vector<type>& vec,
39  Matrix<type>& mat);
40 
41  // Multiplication operators
42 
43  template<class type>
44  Matrix<type> operator*(unsigned fac, Matrix<type>& mat);
45 
46  template<class type>
47  Matrix<type> operator*(int fac, Matrix<type>& mat);
48 
49  template<class type>
50  Matrix<type> operator*(float fac, Matrix<type>& mat);
51 
52  template<class type>
53  Matrix<type> operator*(double fac, Matrix<type>& mat);
54 
55  template<class type>
56  class Matrix {
57 
58  public:
59 
63  Matrix();
64 
68  Matrix(const Matrix<type>& mat);
69 
73  Matrix(unsigned nRow, unsigned nCol);
74 
78  virtual ~Matrix();
79 
83  Vector<type>& operator[](unsigned iRow);
84 
88  Matrix<type> operator*(Matrix<type>& mat);
89 
93  void operator=(const Matrix<type>& mat);
94 
98  template <class T>
99  Matrix<type> operator*(T);
100 
104  template <class T>
105  Matrix<type> operator/(T);
106 
110  template <class T>
111  Matrix<type> operator+(T);
112 
116  template <class T>
117  Matrix<type> operator-(T);
118 
122  Vector<type> operator*( Vector<type>& vec);
123 
127  Matrix<type> reduce(unsigned iRow, unsigned iCol);
128 
132  Matrix<type> transpose();
133  Matrix<type> trans();
134 
138  Matrix<type> adjoint();
139  Matrix<type> adj();
140 
144  Matrix<type> inverse();
145  Matrix<type> inv();
146 
150  type determinant();
151  type det();
152  type determinant(unsigned i, unsigned j);
153  type det(unsigned i, unsigned j);
154 
158  type trace();
159 
163  type cofactor(unsigned iRow, unsigned iCol);
164 
165 
166  //------------------------------------------------------------
167  // Non-member friends
168  //------------------------------------------------------------
169 
173  friend Vector<type> operator * <>
174  ( Vector<type>& vec, Matrix<type>& mat);
175 
179  friend std::ostream& operator << <>
180  (std::ostream& os, Matrix<type>& mat);
181 
185  friend std::ostringstream& operator << <>
186  (std::ostringstream& os, Matrix<type>& mat);
187 
188  public:
189 
190  // Privately, Matrix is implemented as a vector of vectors
191 
192  Vector<Vector<type> > data_;
193 
194  // Store the size
195 
196  unsigned nRow_;
197  unsigned nCol_;
198 
199  }; // End class Matrix
200 
201  //------------------------------------------------------------
202  // Method bodies
203  //------------------------------------------------------------
204 
208  template<class type>
209  Matrix<type>::Matrix()
210  {
211  nRow_ = 0;
212  nCol_ = 0;
213  }
214 
218  template<class type>
219  Matrix<type>::Matrix(const Matrix<type>& mat)
220  {
221  if(&mat == this)
222  return;
223 
224  nRow_ = mat.nRow_;
225  nCol_ = mat.nCol_;
226 
227  data_ = mat.data_;
228  }
229 
233  template<class type>
234  void Matrix<type>::operator=(const Matrix<type>& mat)
235  {
236  if(&mat == this)
237  return;
238 
239  nRow_ = mat.nRow_;
240  nCol_ = mat.nCol_;
241 
242  data_.resize(nRow_);
243  for(unsigned irow=0; irow < nRow_; irow++) {
244  data_[irow].resize(nCol_);
245  }
246 
247  data_ = mat.data_;
248  }
249 
253  template<class type>
254  Matrix<type>::Matrix(unsigned nRow, unsigned nCol)
255  {
256  data_.resize(nRow);
257 
258  for(unsigned irow=0; irow < nRow; irow++)
259  data_[irow].resize(nCol);
260 
261  nRow_ = nRow;
262  nCol_ = nCol;
263  }
264 
268  template<class type>
269  Matrix<type>::~Matrix() {}
270 
274  template<class type>
275  Vector<type>& Matrix<type>::operator[](unsigned iRow)
276  {
277  sza::util::LogStream errStr;
278 
279  if(iRow > data_.size()-1) {
280  errStr.initMessage(true);
281  errStr << "Matrix has no row: " << iRow;
282  errStr.report();
283  throw Error(errStr);
284  }
285 
286  return data_[iRow];
287  }
288 
293  // Matrix
294 
295  template<class type>
296  Matrix<type>
297  Matrix<type>::operator*(Matrix<type>& mat)
298  {
299  LogStream errStr;
300 
301  // Make sure no dimension is zero
302 
303  if(mat.data_.size() == 0 || data_.size() == 0) {
304  errStr.appendMessage(true, "Zero-size dimension encountered");
305  errStr.report();
306  throw Error(errStr);
307  }
308 
309  // Make sure all rows have the same number of columns
310 
311  for(unsigned iRow=0; iRow < mat.data_.size(); iRow++)
312  if(mat.data_[iRow].size() != mat.nCol_) {
313  errStr.appendMessage(true, "Matrix has variable dimensions");
314  break;
315  }
316 
317  for(unsigned iRow=0; iRow < data_.size(); iRow++)
318  if(data_[iRow].size() != nCol_) {
319  errStr.appendMessage(true, "Matrix has variable dimensions");
320  break;
321  }
322 
323  // Make sure the matrices can in fact be multiplied.
324 
325  if(nRow_ != mat.nCol_ || nCol_ != mat.nRow_)
326  errStr.appendMessage(true, "Matrices have incompatible dimensions");
327 
328  if(errStr.isError()) {
329  errStr.report();
330  throw Error(errStr);
331  }
332 
333  Matrix<type> result(nRow_, mat.nCol_);
334 
335  bool first;
336  for(unsigned iRow=0; iRow < nRow_; iRow++)
337  for(unsigned iCol=0; iCol < mat.nCol_; iCol++) {
338  first = true;
339  for(unsigned j=0; j < nCol_; j++) {
340  if(first) {
341  result[iRow][iCol] = data_[iRow][j] * mat.data_[j][iCol];
342  first = false;
343  } else {
344  result[iRow][iCol] += data_[iRow][j] * mat.data_[j][iCol];
345  }
346  }
347  }
348  return result;
349  }
350 
354  template<class type>
355  Vector<type>
356  Matrix<type>::operator*( Vector<type>& vec)
357  {
358  LogStream errStr;
359 
360  if(nCol_==0 || vec.size()==0) {
361  errStr.appendMessage(true, "zero dimension encountered");
362  errStr.report();
363  throw Error(errStr);
364  }
365 
366  if(nCol_ != vec.size()) {
367  errStr.appendMessage(true, "vector has incompatible dimensions");
368  errStr.report();
369  throw Error(errStr);
370  }
371 
372  // Now do the calculation
373 
374  Vector<type> result;
375 
376  result.resize(nRow_);
377 
378  bool first = true;
379 
380  for(unsigned iRow=0; iRow < nRow_; iRow++)
381  for(unsigned j=0; j < nCol_; j++) {
382  if(first) {
383  result[iRow] = vec[j] * data_[iRow][j];
384  first = false;
385  } else {
386  result[iRow] += vec[j] * data_[iRow][j];
387  }
388  }
389 
390  return result;
391  }
392 
393  // Factors
394 
395  template<class type>
396  template<class T>
397  Matrix<type> Matrix<type>::operator*(T fac)
398  {
399  Matrix<type> result = *this;
400 
401  for(unsigned iRow=0; iRow < nRow_; iRow++)
402  for(unsigned iCol=0; iCol < nCol_; iCol++)
403  result[iRow][iCol] *= fac;
404 
405  return result;
406  }
407 
408  template<class type>
409  template<class T>
410  Matrix<type> Matrix<type>::operator/(T fac)
411  {
412  Matrix<type> result = *this;
413 
414  for(unsigned iRow=0; iRow < nRow_; iRow++)
415  for(unsigned iCol=0; iCol < nCol_; iCol++)
416  result[iRow][iCol] /= fac;
417 
418  return result;
419  }
420 
421  template<class type>
422  template<class T>
423  Matrix<type> Matrix<type>::operator+(T fac)
424  {
425  Matrix<type> result = *this;
426 
427  for(unsigned iRow=0; iRow < nRow_; iRow++)
428  for(unsigned iCol=0; iCol < nCol_; iCol++)
429  result[iRow][iCol] += fac;
430 
431  return result;
432  }
433 
434  template<class type>
435  template<class T>
436  Matrix<type> Matrix<type>::operator-(T fac)
437  {
438  Matrix<type> result = *this;
439 
440  for(unsigned iRow=0; iRow < nRow_; iRow++)
441  for(unsigned iCol=0; iCol < nCol_; iCol++)
442  result[iRow][iCol] -= fac;
443 
444  return result;
445  }
446 
447  //------------------------------------------------------------
448  // Non-member operators
449  //------------------------------------------------------------
450 
454  template<class type>
455  Vector<type> operator*( Vector<type>& vec,
456  Matrix<type>& mat)
457  {
458  LogStream errStr;
459 
460  if(mat.nRow_==0 || vec.size()==0) {
461  errStr.appendMessage(true, "zero dimension encountered");
462  errStr.report();
463  throw Error(errStr);
464  }
465 
466  if(mat.nRow_ != vec.size()) {
467  errStr.appendMessage(true, "vector has incompatible dimensions");
468  errStr.report();
469  throw Error(errStr);
470  }
471 
472  // Now do the calculation
473 
474  Vector<type> result;
475 
476  result.resize(mat.nCol_);
477 
478  bool first = true;
479 
480  for(unsigned iCol=0; iCol < mat.nCol_; iCol++)
481  for(unsigned j=0; j < mat.nRow_; j++) {
482  if(first) {
483  result[iCol] = mat.data_[j][iCol] * vec[j];
484  first = false;
485  } else {
486  result[iCol] += mat.data_[j][iCol] * vec[j];
487  }
488  }
489 
490  return result;
491  }
492 
496  template<class type>
497  Matrix<type> operator*(unsigned fac, Matrix<type>& mat)
498  {
499  return mat * fac; // Member operator is already defined
500  }
501 
502  template<class type>
503  Matrix<type> operator*(int fac, Matrix<type>& mat)
504  {
505  return mat * fac; // Member operator is already defined
506  }
507 
508  template<class type>
509  Matrix<type> operator*(float fac, Matrix<type>& mat)
510  {
511  return mat * fac; // Member operator is already defined
512  }
513 
514  template<class type>
515  Matrix<type> operator*(double fac, Matrix<type>& mat)
516  {
517  return mat * fac; // Member operator is already defined
518  }
519 
523  template<class type>
524  std::ostream& operator<<(std::ostream& os,
525  Matrix<type>& mat)
526  {
527  for(unsigned iRow=0; iRow < mat.nRow_; iRow++) {
528  os << "|";
529  for(unsigned iCol=0; iCol < mat.nCol_; iCol++) {
530  if(iCol != 0)
531  os << " ";
532  os << std::setw(10) << std::setprecision(4) << mat.data_[iRow][iCol];
533  }
534  os << "|" << std::endl;
535  }
536  os << std::ends;
537  return os;
538  }
539 
543  template<class type>
544  std::ostringstream& operator<<(std::ostringstream& os,
545  Matrix<type>& mat)
546  {
547  for(unsigned iRow=0; iRow < mat.nRow_; iRow++) {
548  os << "|";
549  for(unsigned iCol=0; iCol < mat.nCol_; iCol++) {
550  if(iCol != 0)
551  os << " ";
552  os << mat.data_[iRow][iCol];
553  }
554  os << "|" << std::endl;
555  }
556  os << std::ends;
557  return os;
558  }
559 
563  template<class type>
564  Matrix<type> Matrix<type>::transpose()
565  {
566  Matrix<type> result(nCol_, nRow_);
567 
568  for(unsigned i=0; i < nRow_; i++)
569  for(unsigned j=0; j < nCol_; j++)
570  result.data_[j][i] = data_[i][j];
571 
572  return result;
573  }
574 
575  template<class type>
576  Matrix<type> Matrix<type>::trans()
577  {
578  return transpose();
579  }
580 
584  template<class type>
585  Matrix<type> Matrix<type>::adjoint()
586  {
587  Matrix<type> result(nCol_, nRow_);
588  int prefac;
589 
590  for(unsigned i=0; i < nRow_; i++)
591  for(unsigned j=0; j < nCol_; j++) {
592  prefac = (i+j)%2 == 0 ? 1 : -1;
593  result.data_[i][j] = prefac * determinant(i, j);
594  }
595 
596  return result.transpose();
597  }
598 
599  template<class type>
600  Matrix<type> Matrix<type>::adj()
601  {
602  return adjoint();
603  }
604 
608  template<class type>
609  Matrix<type> Matrix<type>::inverse()
610  {
611  Matrix<type> result = adjoint();
612  type deter = determinant();
613 
614  if(::std::isfinite(1.0/((double)deter)))
615  return result/deter;
616  else {
617  ThrowError("Matrix is not invertible.");
618  }
619  }
620 
621  template<class type>
622  Matrix<type> Matrix<type>::inv()
623  {
624  return inverse();
625  }
626 
630  template<class type>
631  type Matrix<type>::trace()
632  {
633  if(nRow_ != nCol_) {
634  ThrowError("Not an N x N matrix");
635  }
636 
637  Matrix<type> result = data_[0][0];
638 
639  for(unsigned i=1; i < nRow_; i++)
640  result += data_[i][i];
641 
642  return result;
643  }
644 
648  template<class type>
649  type Matrix<type>::cofactor(unsigned i, unsigned j)
650  {
651  if(nRow_ != nCol_) {
652  ThrowError("Not and N x N matrix");
653  }
654 
655  if(nRow_ < 2) {
656  ThrowError("Cannot determine cofactors for dimensions < 2");
657  }
658 
659  int prefac = (i+j)%2 == 0 ? 1 : -1;
660 
661  return prefac * determinant(i, j);
662  }
663 
667  template<class type>
668  type Matrix<type>::determinant()
669  {
670  type sum;
671 
672  if(nRow_ != nCol_) {
673  ThrowError("Not and N x N matrix");
674  }
675 
676  // If this is a N x N matrix with N < 3, the determinant is trivial
677 
678  if(nRow_ == 1)
679  return data_[0][0];
680  else if(nRow_ == 2)
681  return data_[0][0] * data_[1][1] - data_[1][0] * data_[0][1];
682  else {
683  bool first=true;
684 
685  for(unsigned iCol=0; iCol < nCol_; iCol++) {
686  if(first) {
687  sum = data_[0][iCol] * cofactor(0, iCol);
688  first = false;
689  } else {
690  sum += data_[0][iCol] * cofactor(0, iCol);
691  }
692  }
693  }
694  return sum;
695  }
696 
700  template<class type>
701  type Matrix<type>::det()
702  {
703  return determinant();
704  }
705 
709  template<class type>
710  type Matrix<type>::determinant(unsigned i, unsigned j)
711  {
712  if(nRow_ != nCol_) {
713  ThrowError("Not and N x N matrix");
714  }
715 
716  if(nRow_ < 2) {
717  ThrowError("Cannot compute determinant for dimensions < 2");
718  }
719 
720  Matrix<type> reduced = reduce(i, j);
721 
722  return reduced.determinant();
723  }
724 
728  template<class type>
729  type Matrix<type>::det(unsigned i, unsigned j)
730  {
731  return determinant(i, j);
732  }
733 
737  template<class type>
738  Matrix<type> Matrix<type>::reduce(unsigned i, unsigned j)
739  {
740  if(nCol_ == 1 || nRow_ == 1) {
741  ThrowError("Matrix dimensions cannot be reduced");
742  }
743 
744  // Else output the reduced matrix
745 
746  Matrix<type> result(nRow_-1, nCol_-1);
747 
748  // Copy the original matrix, exclusive of the specified row and column
749 
750  for(unsigned iRow=0, iRowRed=0; iRow < nRow_; iRow++) {
751 
752  if(iRow != i) {
753 
754  for(unsigned iCol=0, iColRed=0; iCol < nCol_; iCol++) {
755 
756  if(iCol != j) {
757  result.data_[iRowRed][iColRed] = data_[iRow][iCol];
758  iColRed++;
759  }
760  }
761 
762  iRowRed++;
763  }
764  }
765  return result;
766  }
767 
768  } // End namespace util
769 } // End namespace sza
770 
771 
772 
773 #endif // End #ifndef SZA_UTIL_MATRIX_H
Started: Sun Dec 14 07:19:50 UTC 2003.
Tagged: Fri Nov 14 12:39:33 UTC 2003.