MOOSE - Multiscale Object Oriented Simulation Environment
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
MatrixOps.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 ** This program is part of 'MOOSE', the
3 ** Messaging Object Oriented Simulation Environment.
4 ** Copyright (C) 2003-2011 Upinder S. Bhalla. and NCBS
5 ** It is made available under the terms of the
6 ** GNU Lesser General Public License version 2.1
7 ** See the file COPYING.LIB for the full notice.
8 **********************************************************************/
9 #include <vector>
10 #include <math.h>
11 #include "doubleEq.h"
12 #include <iostream>
13 #include "MatrixOps.h"
14 
15 using std::cerr;
16 using std::endl;
17 using std::cout;
18 
19 void matPrint( Matrix* A )
20 {
21  for( unsigned int i = 0; i < A->size(); ++i )
22  {
23  for( unsigned int j = 0; j < A->size(); ++j )
24  cout << (*A)[i][j] << " ";
25  cout << endl;
26  }
27 }
28 
29 void vecPrint( Vector* v )
30 {
31  for( unsigned int j = 0; j < v->size(); ++j )
32  cout << (*v)[j] << " ";
33  cout << endl;
34 }
35 
37 {
38  unsigned int n = A->size();
39  Matrix *C = matAlloc( n );
40 
41  for( unsigned int i = 0; i < n; ++i )
42  {
43  for( unsigned int j = 0; j < n; ++j )
44  {
45  for( unsigned int k = 0; k < n; ++k )
46  (*C)[i][j] += (*A)[i][k] * (*B)[k][j];
47  }
48  }
49 
50  return C;
51 }
52 
53 void matMatMul( Matrix* A, Matrix* B, unsigned int resIndex )
54 {
55  Matrix *C;
56 
57  C = matMatMul( A, B );
58 
59  if ( resIndex == FIRST )
60  *A = *C;
61  else if ( resIndex == SECOND )
62  *B = *C;
63 
64  delete C;
65 }
66 
67 void triMatMul( Matrix* A, Matrix* B)
68 {
69  unsigned int n = A->size();
70  unsigned int k;
71  double temp;
72 
73  for ( unsigned int i = 0; i < n; ++i )
74  {
75  for ( unsigned int j = 0; j < n; ++j )
76  {
77  k = j >= i ? j : i;
78  temp = (*A)[i][j];
79 
80  for ( ; k < n; ++k )
81  (*A)[i][j] += (*A)[i][k] * (*B)[k][j];
82 
83  (*A)[i][j] -= temp;
84  }
85  }
86 }
87 
88 void matPermMul( Matrix* A, vector< unsigned int >* swaps )
89 {
90  unsigned int i,j,n,index;
91  double temp;
92  n = A->size();
93 
94  while (!swaps->empty())
95  {
96  index = swaps->back();
97  swaps->pop_back();
98  i = index % 10;
99  j = (index / 10 ) % 10;
100 
101  //Swapping the columns.
102  for( unsigned int l = 0; l < n; ++l )
103  {
104  temp = (*A)[l][i];
105  (*A)[l][i] = (*A)[l][j];
106  (*A)[l][j] = temp;
107  }
108  }
109 }
110 
111 Matrix* matMatAdd( const Matrix* A, const Matrix* B, double alpha, double beta )
112 {
113  unsigned int n = A->size();
114  Matrix *C = matAlloc( n );
115 
116  for( unsigned int i = 0; i < n; ++i )
117  {
118  for( unsigned int j = 0; j < n; ++j )
119  (*C)[i][j] = alpha * (*A)[i][j] + beta * (*B)[i][j];
120  }
121 
122  return C;
123 }
124 
125 void matMatAdd( Matrix* A, Matrix* B, double alpha, double beta,
126  unsigned int resIndex )
127 {
128  Matrix *C;
129  unsigned int n = A->size();
130 
131  if ( resIndex == FIRST ) {
132  C = A;
133  } else if ( resIndex == SECOND ) {
134  C = B;
135  } else {
136  cerr << "matMatAdd : Invalid index supplied to store result.\n";
137  return;
138  }
139 
140  for( unsigned int i = 0; i < n; ++i )
141  {
142  for( unsigned int j = 0; j < n; ++j )
143  (*C)[i][j] = alpha * (*A)[i][j] + beta * (*B)[i][j];
144  }
145 }
146 
147 Matrix* matEyeAdd( const Matrix* A, double k )
148 {
149  unsigned int n = A->size();
150  Matrix* B = matAlloc( n );
151 
152  for( unsigned int i = 0; i < n; ++i )
153  {
154  for( unsigned int j = 0; j < n; ++j )
155  {
156  if ( i == j )
157  (*B)[i][j] = (*A)[i][j] + k;
158  else
159  (*B)[i][j] = (*A)[i][j];
160  }
161  }
162 
163  return B;
164 }
165 
166 void matEyeAdd( Matrix* A, double k, unsigned int dummy )
167 {
168  unsigned int n = A->size();
169  dummy = 0;
170 
171  for( unsigned int i = 0; i < n; ++i )
172  (*A)[i][i] += k;
173 }
174 
175 Matrix* matScalShift( const Matrix* A, double mul, double add )
176 {
177  unsigned int n = A->size();
178  Matrix *C = matAlloc( n );
179 
180  for( unsigned int i = 0; i < n; ++i )
181  {
182  for( unsigned int j = 0; j < n; ++j )
183  (*C)[i][j] = (*A)[i][j] * mul + add;
184  }
185 
186  return C;
187 }
188 
189 void matScalShift( Matrix* A, double mul, double add, unsigned int dummy )
190 {
191  unsigned int n = A->size();
192  dummy = 0;
193 
194  for( unsigned int i = 0; i < n; ++i )
195  {
196  for( unsigned int j = 0; j < n; ++j )
197  (*A)[i][j] = (*A)[i][j] * mul + add;
198  }
199 }
200 
201 Vector* vecMatMul( const Vector* v, Matrix* A )
202 {
203  unsigned int n = A->size();
204  Vector* w = vecAlloc( n );
205 
206  for( unsigned int i = 0; i < n; ++i )
207  {
208  for( unsigned int j = 0; j < n; ++j )
209  (*w)[i] += (*v)[j] * (*A)[j][i];
210  }
211 
212  return w;
213 }
214 
215 Vector* vecScalShift( const Vector* v, double scal, double shift )
216 {
217  unsigned int n = v->size();
218  Vector* w = vecAlloc( n );
219 
220  for ( unsigned int i = 0; i < n; ++i )
221  (*w)[i] = scal * (*v)[i] + shift;
222 
223  return w;
224 }
225 
226 void vecScalShift( Vector* v, double scal, double shift, unsigned int dummy )
227 {
228  unsigned int n = v->size();
229  dummy = 0;
230 
231  for ( unsigned int i = 0; i < n; ++i )
232  (*v)[i] += scal * (*v)[i] + shift;
233 }
234 
236 {
237  unsigned int n = A->size();
238  Vector* w = vecAlloc( n );
239 
240  for( unsigned int i = 0; i < n; ++i )
241  {
242  for( unsigned int j = 0; j < n; ++j )
243  (*w)[i] += (*A)[i][j] * (*v)[j];
244  }
245 
246  return w;
247 }
248 
249 Vector* vecVecScalAdd( const Vector* v1, const Vector* v2,
250  double alpha, double beta )
251 {
252  unsigned int n = v1->size();
253  Vector *w = vecAlloc( n );
254 
255  for ( unsigned int i = 0; i < n; ++i )
256  (*w)[i] = (*v1)[i] * alpha + (*v2)[i] * beta;
257 
258  return w;
259 }
260 
261 void vecVecScalAdd( Vector* v1, Vector* v2, double alpha, double beta,
262  unsigned int dummy )
263 {
264  unsigned int n = v1->size();
265  dummy = 0;
266 
267  for ( unsigned int i = 0; i < n; ++i )
268  (*v1)[i] = (*v1)[i] * alpha + (*v2)[i] * beta;
269 }
270 
271 double matTrace( Matrix* A )
272 {
273  unsigned int n = A->size();
274  double trace = 0;
275 
276  for ( unsigned int i = 0; i < n; ++i )
277  trace += (*A)[i][i];
278 
279  return trace;
280 }
281 
282 double matColNorm( Matrix* A )
283 {
284  double norm = 0, colSum = 0;
285  unsigned int n = A->size();
286 
287  for( unsigned int i = 0; i < n; ++i )
288  {
289  colSum = 0;
290  for( unsigned int j = 0; j < n; ++j )
291  colSum += fabs( (*A)[j][i] );
292 
293  if ( colSum > norm )
294  norm = colSum;
295  }
296 
297  return norm;
298 }
299 
301 {
302  unsigned int n = A->size();
303  Matrix* At = matAlloc( n );
304 
305  for( unsigned int i = 0; i < n; ++i )
306  {
307  for( unsigned int j = 0; j < n; ++j )
308  (*At)[i][j] = (*A)[j][i];
309  }
310 
311  return At;
312 }
313 
314 double doPartialPivot( Matrix* A, unsigned int row, unsigned int col,
315  vector< unsigned int >* swaps )
316 {
317  unsigned int n = A->size(), pivotRow = row;
318  double pivot = (*A)[row][col];
319 
320  for( unsigned i = row; i < n; ++i )
321  {
322  if( fabs( (*A)[i][col] ) > pivot )
323  {
324  pivotRow = i;
325  pivot = (*A)[i][col];
326  }
327  }
328 
329  //If pivot is non-zero, do the row swap.
330  if ( !doubleEq(pivot,0.0) && pivotRow != row )
331  {
332  Matrix::iterator pivotRowItr = (A->begin() + pivotRow);
333  Matrix::iterator currRowItr = (A->begin() + row);
334  swap( *pivotRowItr, *currRowItr );
335 
336  //The row numbers interchanged are stored as a 2-digit number and pushed
337  //into this vector. This information is later used in creating the
338  //permutation matrices.
339  swaps->push_back( 10 * pivotRow + row );
340  return pivot;
341  }
342  else if ( !doubleEq(pivot, 0.0) && pivotRow == row )
343  return (*A)[row][col]; //Choice of pivot is unchanged.
344  else
345  return 0; //Matrix is singular!
346 }
347 
348 void matInv( Matrix* A, vector< unsigned int >* swaps, Matrix* invA )
349 {
350  Matrix *L, *invL;
351  unsigned int n = A->size(), i, j, diagPos;
352  double pivot, rowMultiplier1, rowMultiplier2;
353 
354  L = matAlloc( n );
355  invL = matAlloc( n );
356  *invA = *A;
357 
358  //The upper triangular portion is stored and inverted in invA
359 
360  //Creating a copy of the input matrix, as well as initializing the
361  //lower triangular matrix L.
362  for (i = 0; i < n; ++i)
363  (*L)[i][i] = 1;
364 
366  //Calculation of LU decomposition.
368  for ( unsigned int k = 0; k < n; ++k )
369  pivot = doPartialPivot( invA, k, k, swaps );
370 
371  diagPos = 0;
372  pivot = 0;
373  i = 1;
374  j = 0;
375  while( diagPos < n - 1 )
376  {
377  rowMultiplier1 = (*invA)[diagPos][j]; //Pivot element.
378  rowMultiplier2 = (*invA)[i][j];
379 
380  (*invA)[i][j] = 0;
381  for( unsigned int k = j + 1; k < n; ++k )
382  (*invA)[i][k] = ( (*invA)[i][k] * rowMultiplier1 -
383  (*invA)[diagPos][k] *rowMultiplier2 ) / rowMultiplier1;
384 
385 
386  (*L)[i][j] = rowMultiplier2 / rowMultiplier1;
387 
388  if ( i != n - 1 )
389  ++i;
390  else
391  {
392  ++j;
393  ++diagPos;
394  //In case of zero pivot, divide by a very small number whose value we
395  //know.
396  if ( doubleEq( (*invA)[diagPos][diagPos], 0.0 ) )
397  {
398  cerr << "Warning : Singularity detected. Proceeding with computation"
399  "anyway.\n";
400  (*invA)[diagPos][diagPos] = EPSILON;
401  }
402 
403  i = diagPos + 1;
404  }
405  }
406  //End of computation of L and U (which is stored in invA).
408 
410  //Obtaining the inverse of invA and L, which is obtained by solving the
411  //simple systems Ux = I and Lx= I.
413 
414  double sum = 0;
415 
416  //We serially solve for the equations Ux = e_n, Ux=e_{n-1} ..., Ux = e1.
417  //where, e_k is the k'th elementary basis vector.
418  for( int k = n - 1; k >= 0; --k )
419  {
420  for ( int l = k; l >= 0; --l )
421  {
422  sum = 0;
423 
424  if ( l != k )
425  {
426  for ( int m = k; m > l; --m )
427  sum += (*invA)[l][m] * (*invA)[m][k];
428  }
429 
430  if ( l == k )
431  (*invA)[l][k] = (1 - sum)/(*invA)[l][l];
432  else
433  (*invA)[l][k] = -sum/(*invA)[l][l];
434  }
435  }
436  //Similarly as above, we find the inverse of the lower triangular matrix by
437  //forward-substitution.
438 
439  *invL = *L;
440  double invTemp;
441 
442  //Always true when the diagonal elements are 1.
443  for( unsigned int k = 0; k < n - 1; ++k )
444  (*invL)[k+1][k] = -(*invL)[k+1][k];
445 
446  for( unsigned int k = 0; k <= n - 1; ++k )
447  {
448  for( unsigned int l = k + 2; l <= n - 1; ++l )
449  {
450  invTemp = 0;
451  for ( unsigned int m = k + 1; m <= n - 1; ++m )
452  invTemp -= (*invL)[m][k] * (*L)[l][m];
453 
454  (*invL)[l][k] = invTemp;
455  }
456  }
457 
458  //End of computation of invL and invU. Note that they have been computed in
459  //place, which means the original copies of L and U are now gone.
461 
463  //Constructing the inverse of the permutation matrix P.
464  //P is calculated only if there was any pivoting carried out.
465  //At this stage, L^(-1) has already been calculated.
466  //P^(-1) = P^T.
467  //Since we have computed PA = LU,
468  //the final inverse is given by U^(-1)*L^(-1)*P^(-1).
469  //If P was not calculated i.e. there were no exchanges, then the
470  //inverse is just U^(-1) * L^(-1).
472  triMatMul( invA, invL );
473  if ( !swaps->empty() )
474  matPermMul( invA, swaps );
475 
476  delete invL;
477  delete L;
478 }
479 
480 Matrix* matAlloc( unsigned int n )
481 {
482  Matrix* A = new Matrix;
483 
484  A->resize( n );
485  for ( unsigned int i = 0; i < n; ++i )
486  (*A)[i].resize( n, 0.0 );
487 
488  return A;
489 }
490 
491 Vector* vecAlloc( unsigned int n )
492 {
493  Vector* vec = new Vector;
494 
495  vec->resize( n, 0.0 );
496 
497  return vec;
498 }
Matrix * matMatAdd(const Matrix *A, const Matrix *B, double alpha, double beta)
Definition: MatrixOps.cpp:111
vector< double > Vector
Definition: MatrixOps.h:23
Matrix * matTrans(Matrix *A)
Definition: MatrixOps.cpp:300
#define SECOND
Definition: MatrixOps.h:31
void matPrint(Matrix *A)
Definition: MatrixOps.cpp:19
Matrix * matScalShift(const Matrix *A, double mul, double add)
Definition: MatrixOps.cpp:175
static DestFinfo dummy("dummy","This Finfo is a dummy. If you are reading this you have used an invalid index", 0)
Vector * vecScalShift(const Vector *v, double scal, double shift)
Definition: MatrixOps.cpp:215
void matPermMul(Matrix *A, vector< unsigned int > *swaps)
Definition: MatrixOps.cpp:88
Vector * vecVecScalAdd(const Vector *v1, const Vector *v2, double alpha, double beta)
Definition: MatrixOps.cpp:249
Vector * vecMatMul(const Vector *v, Matrix *A)
Definition: MatrixOps.cpp:201
bool doubleEq(double x, double y)
Definition: doubleEq.cpp:16
Matrix * matMatMul(Matrix *A, Matrix *B)
Definition: MatrixOps.cpp:36
Vector * matVecMul(Matrix *A, Vector *v)
Definition: MatrixOps.cpp:235
void triMatMul(Matrix *A, Matrix *B)
Definition: MatrixOps.cpp:67
Matrix * matEyeAdd(const Matrix *A, double k)
Definition: MatrixOps.cpp:147
void matInv(Matrix *A, vector< unsigned int > *swaps, Matrix *invA)
Definition: MatrixOps.cpp:348
double doPartialPivot(Matrix *A, unsigned int row, unsigned int col, vector< unsigned int > *swaps)
Definition: MatrixOps.cpp:314
vector< vector< double > > Matrix
Definition: MatrixOps.h:22
Vector * vecAlloc(unsigned int n)
Definition: MatrixOps.cpp:491
double matColNorm(Matrix *A)
Definition: MatrixOps.cpp:282
#define FIRST
Definition: MatrixOps.h:30
void vecPrint(Vector *v)
Definition: MatrixOps.cpp:29
#define EPSILON
Definition: MatrixOps.h:28
double matTrace(Matrix *A)
Definition: MatrixOps.cpp:271
Matrix * matAlloc(unsigned int n)
Definition: MatrixOps.cpp:480