MOOSE - Multiscale Object Oriented Simulation Environment
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
FastMatrixElim.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-2014 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 
10 #include <math.h>
11 #include <vector>
12 #include <algorithm>
13 #include <cassert>
14 #include <functional>
15 #include <iostream>
16 #include <iomanip>
17 
18 using namespace std;
19 #include "../basecode/SparseMatrix.h"
20 #include "../basecode/doubleEq.h"
21 #include "FastMatrixElim.h"
22 
23 /*
24 const unsigned int SM_MAX_ROWS = 200000;
25 const unsigned int SM_MAX_COLUMNS = 200000;
26 const unsigned int SM_RESERVE = 8;
27 */
28 
30  : SparseMatrix< double >()
31 {;}
32 
33 FastMatrixElim::FastMatrixElim( unsigned int nrows, unsigned int ncolumns )
34  : SparseMatrix< double >( nrows, ncolumns )
35 {;}
36 
38  : SparseMatrix< double >( orig )
39 {;}
40 
41 void sortByColumn(
42  vector< unsigned int >& col, vector< double >& entry );
43 void testSorting();
44 
45 //
46 // static unsigned int parents[] = { 1,6,3,6,5,8,7,8,9,10,-1};
47 // unsigned int numKids[] = {0,1,0,1,0,2,
48 
49 static const unsigned int EMPTY_VOXEL(-1);
50 
52 {
53  FastMatrixElim temp = *this;
54  temp.transpose();
55  return (
56  nrows_ == temp.nrows_ &&
57  ncolumns_ == temp.ncolumns_ &&
58  N_.size() == temp.N_.size() &&
59  rowStart_ == temp.rowStart_ &&
60  colIndex_ == temp.colIndex_
61  );
62 }
63 
64 bool FastMatrixElim::operator==( const FastMatrixElim& other ) const
65 {
66  if (
67  nrows_ == other.nrows_ &&
68  ncolumns_ == other.ncolumns_ &&
69  N_.size() == other.N_.size() &&
70  rowStart_ == other.rowStart_ &&
71  colIndex_ == other.colIndex_ ) {
72  for ( unsigned int i = 0; i < N_.size(); ++i )
73  if ( !doubleEq( N_[i], other.N_[i] ) )
74  return false;
75  return true;
76  }
77  return false;
78 }
79 
81 {
82  FastMatrixElim temp = *this;
83  temp.transpose();
84  return ( temp == *this );
85 }
86 
140  const vector< unsigned int >& parentVoxel,
141  vector< unsigned int >& lookupOldRowFromNew
142  )
143 {
144  // First we fill in the vector that specifies the old row number
145  // assigned to each row of the reordered matrix.
146  assert( parentVoxel.size() == nrows_ );
147  lookupOldRowFromNew.clear();
148  vector< unsigned int > numKids( nrows_, 0 );
149  vector< bool > rowPending( nrows_, true );
150  unsigned int numDone = 0;
151  for ( unsigned int i = 0; i < nrows_; ++i ) {
152  if ( parentVoxel[i] != EMPTY_VOXEL )
153  numKids[ parentVoxel[i] ]++;
154  }
155  while ( numDone < nrows_ ) {
156  for ( unsigned int i = 0; i < nrows_; ++i ) {
157  if ( rowPending[i] && numKids[i] == 0 ) {
158  lookupOldRowFromNew.push_back( i );
159  rowPending[i] = false;
160  numDone++;
161  unsigned int pa = parentVoxel[i];
162  // root parent is EMPTY_VOXEL
163  while ( pa != EMPTY_VOXEL && numKids[pa] == 1 ) {
164  assert( rowPending[pa] );
165  rowPending[pa] = false;
166  numDone++;
167  lookupOldRowFromNew.push_back( pa );
168  pa = parentVoxel[pa];
169  }
170  if ( pa != EMPTY_VOXEL ) {
171  assert( numKids[pa] > 0 );
172  numKids[pa]--;
173  }
174  }
175  }
176  }
177 
178  /*
179  cout << setprecision(4);
180  cout << "oldRowFromNew= {" ;
181  for ( unsigned int i = 0; i < nrows_; ++i )
182  cout << lookupOldRowFromNew[i] << ", ";
183  cout << "}\n";
184  */
185  // Then we fill in the reordered matrix. Note we need to reorder
186  // columns too.
187  shuffleRows( lookupOldRowFromNew );
188  return true;
189 }
190 
191 
192 // Fill in the reordered matrix. Note we need to reorder columns too.
194  const vector< unsigned int >& lookupOldRowFromNew )
195 {
196  vector< unsigned int > lookupNewRowFromOld( nrows_ );
197  for ( unsigned int i = 0; i < nrows_; ++i )
198  lookupNewRowFromOld[ lookupOldRowFromNew[i] ] = i;
199 
200  FastMatrixElim temp = *this;
201  clear();
202  setSize( temp.nrows_, temp.nrows_ );
203  for ( unsigned int i = 0; i < lookupOldRowFromNew.size(); ++i ) {
204  vector< unsigned int > c;
205  vector< double > e;
206  unsigned int num = temp.getRow( lookupOldRowFromNew[i], e, c );
207  vector< unsigned int > newc( num );
208  vector< double > newe( num );
209  for ( unsigned int j = 0; j < num; ++j ) {
210  newc[j] = lookupNewRowFromOld[ c[j] ];
211  newe[j] = e[j];
212  }
213  // Now we need to sort the new row entries in increasing col order.
214  /*
215  sortByColumn( newc, newe );
216  addRow( i, newe, newc );
217  */
218  sortByColumn( newc, e );
219  addRow( i, e, newc );
220  }
221 }
222 
223 void sortByColumn( vector< unsigned int >& col, vector< double >& entry )
224 {
225  unsigned int num = col.size();
226  assert( num == entry.size() );
227  // Stupid bubble sort, as we only have up to 5 entries and need to
228  // sort both the col and reorder the entries by the same sequence.
229  for ( unsigned int i = 0; i < num; ++i ) {
230  for ( unsigned int j = 1; j < num; ++j ) {
231  if ( col[j] < col[j-1] ) {
232  unsigned int temp = col[j];
233  col[j] = col[j-1];
234  col[j-1] = temp;
235  double v = entry[j];
236  entry[j] = entry[j-1];
237  entry[j-1] = v;
238  }
239  }
240  }
241 }
242 
243 
245  const double* test, unsigned int numCompts )
246 {
247  setSize( numCompts, numCompts );
248  vector< double > row( numCompts, ~0 );
249  for ( unsigned int i = 0; i < numCompts; ++i ) {
250  for ( unsigned int j = 0; j < numCompts; ++j ) {
251  unsigned int k = i * numCompts + j;
252  if ( test[k] < 0.1 ) {
253  } else {
254  N_.push_back( test[k] );
255  colIndex_.push_back( j );
256  }
257  }
258  rowStart_[i + 1] = N_.size();
259  }
260 }
261 
262 /*
263 I need an outer function to fill the vector of ops for forward elim.
264 Then I need another outer function to fill another set of ops for
265 back-substitution.
266 */
267 
275 void FastMatrixElim::buildForwardElim( vector< unsigned int >& diag,
276  vector< Triplet< double > >& fops )
277 {
278  vector< vector< unsigned int > > rowsToElim( nrows_ );
279  diag.clear();
280  for ( unsigned int i = 0; i < nrows_; ++i ) {
281  unsigned int rs = rowStart_[i];
282  unsigned int re = rowStart_[i+1];
283  for ( unsigned int j = rs; j < re; ++j ) {
284  unsigned int k = colIndex_[j];
285  if ( k == i ) {
286  diag.push_back(j);
287  } else if ( k > i ) {
288  rowsToElim[ i ].push_back( k );
289  }
290  }
291  }
292  assert( diag.size() == nrows_ );
293  for ( unsigned int i = 0; i < nrows_; ++i ) {
294  double d = N_[diag[i]];
295  unsigned int diagend = rowStart_[ i + 1 ];
296  assert( diag[i] < diagend );
297  vector< unsigned int >& elim = rowsToElim[i];
298  for ( unsigned int j = 0; j < elim.size(); ++j ) {
299  unsigned int erow = elim[j];
300  if ( erow == i ) continue;
301  unsigned int rs = rowStart_[ erow ];
302  unsigned int re = rowStart_[ erow+1 ];
303  // assert( colIndex_[rs] == i );
304  double ratio = get( erow, i ) / d;
305  // double ratio = N_[rs]/N_[diag[i]];
306  for ( unsigned int k = diag[i]+1; k < diagend; ++k ) {
307  unsigned int col = colIndex_[k];
308  // findElimEntry, subtract it out.
309  for ( unsigned int q = rs; q < re; ++q ) {
310  if ( colIndex_[q] == col ) {
311  N_[q] -= N_[k] * ratio;
312  }
313  }
314  }
315  fops.push_back( Triplet< double >( ratio, i, erow) );
316  }
317  }
318  /*
319  for ( unsigned int i = 0; i < rowsToElim.size(); ++i ) {
320  cout << i << " : ";
321  for ( unsigned int j = 0; j < rowsToElim[i].size(); ++j ) {
322  cout << rowsToElim[i][j] << " ";
323  }
324  cout << endl;
325  }
326  for ( unsigned int i = 0; i < fops.size(); ++i ) {
327  cout << "fops[" << i << "]= " << fops[i].b_ << " " << fops[i].c_ <<
328  " " << fops[i].a_ << endl;
329  }
330  */
331 }
332 
342 void FastMatrixElim::buildBackwardSub( vector< unsigned int >& diag,
343  vector< Triplet< double > >& bops, vector< double >& diagVal )
344 {
345  // This vec tells the routine which rows below have to be back-subbed.
346  // This includes the rows if any in the tridiagonal band and also
347  // rows, if any, on branches.
348  vector< vector< unsigned int > > rowsToSub( nrows_ );
349 
350  for ( unsigned int i = 0; i < nrows_; ++i ) {
351  unsigned int d = diag[i] + 1;
352  unsigned int re = rowStart_[i+1];
353  for ( unsigned int j = d; j < re; ++j ) {
354  unsigned int k = colIndex_[j];
355  // At this point the row to sub is at (i, k). We need to go down
356  // to the (k,k) diagonal to sub it out.
357  rowsToSub[ k ].push_back( i );
358  }
359  }
360  /*
361  for ( unsigned int i = 0; i < rowsToSub.size(); ++i ) {
362  cout << i << " : ";
363  for ( unsigned int j = 0; j < rowsToSub[i].size(); ++j ) {
364  cout << rowsToSub[i][j] << " ";
365  }
366  cout << endl;
367  }
368  */
369 
370  diagVal.resize( 0 );
371  // Fill in the diagonal terms. Here we do all entries.
372  for ( unsigned int i = 0; i != nrows_ ; ++i ) {
373  diagVal.push_back( 1.0 / N_[diag[i]] );
374  }
375 
376  // Fill in the back-sub operations. Note we don't need to check zero.
377  for ( unsigned int i = nrows_-1; i != 0 ; --i ) {
378  for ( int j = rowsToSub[i].size() - 1; j != -1; --j ) {
379  unsigned int k = rowsToSub[i][j];
380  double val = get( k, i ); //k is the row to go, i is the diag.
381  bops.push_back( Triplet< double >( val * diagVal[i], i, k ) );
382  }
383  }
384 
385  /*
386  for ( unsigned int i = 0; i < bops.size(); ++i ) {
387  cout << i << ": " << bops[i].a_ << " " <<
388  bops[i].b_ << " " << // diagonal index
389  bops[i].c_ << " " << // off-diagonal index
390  1.0 / diagVal[bops[i].b_] << // diagonal value.
391  endl;
392  }
393  */
394 }
395 
406  const vector< unsigned int >& parentVoxel,
407  double diffConst, double motorConst,
408  double dt )
409 {
410  FastMatrixElim m;
411  m.nrows_ = m.ncolumns_ = nrows_;
412  m.rowStart_.resize( nrows_ + 1 );
413  m.rowStart_[0] = 0;
414  for ( unsigned int i = 1; i <= nrows_; ++i ) {
415  // Insert an entry for diagonal in each.
416  m.rowStart_[i] = rowStart_[i] + i;
417  }
418  for ( unsigned int i = 0; i < nrows_; ++i ) {
419  double proximalTerms = 0.0;
420  double distalTerms = 0.0;
421  double term = 0.0;
422  bool pendingDiagonal = true;
423  unsigned int diagonalIndex = EMPTY_VOXEL;
424  for ( unsigned int j = rowStart_[i]; j < rowStart_[i+1]; ++j ) {
425  // Treat transport as something either to or from soma.
426  // The motor direction is based on this.
427  // Assume no transport between sibling compartments.
428  if ( parentVoxel[colIndex_[j]] == i ) {
429  term = N_[j] * dt * ( diffConst + motorConst );
430  proximalTerms += N_[j];
431  } else {
432  term = N_[j] * dt * diffConst;
433  distalTerms += N_[j];
434  }
435  if ( colIndex_[j] < i ) {
436  m.colIndex_.push_back( colIndex_[j] );
437  m.N_.push_back( term );
438  } else if ( colIndex_[j] == i ) {
439  assert( 0 );
440  } else {
441  if ( pendingDiagonal ) {
442  pendingDiagonal = false;
443  diagonalIndex = m.N_.size();
444  m.colIndex_.push_back( i ); // diagonal
445  m.N_.push_back( 0 ); // placeholder
446  }
447  m.colIndex_.push_back( colIndex_[j] );
448  m.N_.push_back( term );
449  }
450  }
451  if ( pendingDiagonal ) {
452  diagonalIndex = m.N_.size();
453  m.colIndex_.push_back( i ); // diagonal
454  m.N_.push_back( 0 ); // placeholder
455  }
456  assert( diagonalIndex != EMPTY_VOXEL );
457  m.N_[diagonalIndex] = 1.0 - dt * (
458  diffConst * ( proximalTerms + distalTerms ) +
459  motorConst * distalTerms
460  );
461  }
462  *this = m;
463 }
464 
466 // All the rest was setup. This function does the work, in a tight
467 // inner loop called many times. Doesn't even depend on matrix contents,
468 // but the data layout is built by the matrix so it is set as a
469 // static function of it.
471 // Static function.
472 void FastMatrixElim::advance( vector< double >& y,
473  const vector< Triplet< double > >& ops, // has both fops and bops.
474  const vector< double >& diagVal )
475 {
476  for ( vector< Triplet< double > >::const_iterator
477  i = ops.begin(); i != ops.end(); ++i )
478  y[i->c_] -= y[i->b_] * i->a_;
479 
480  assert( y.size() == diagVal.size() );
481  vector< double >::iterator iy = y.begin();
482  for ( vector< double >::const_iterator
483  i = diagVal.begin(); i != diagVal.end(); ++i )
484  *iy++ *= *i;
485 }
486 
492  const vector< unsigned int >& lookupOldRowsFromNew,
493  vector< Triplet< double > >& ops,
494  vector< double >& diagVal )
495 {
496  vector< double > oldDiag = diagVal;
497 
498  for ( unsigned int i = 0; i < ops.size(); ++i ) {
499  ops[i].b_ = lookupOldRowsFromNew[ ops[i].b_ ];
500  ops[i].c_ = lookupOldRowsFromNew[ ops[i].c_ ];
501  }
502 
503  for ( unsigned int i = 0; i < diagVal.size(); ++i ) {
504  diagVal[ lookupOldRowsFromNew[i] ] = oldDiag[i];
505  }
506 }
507 
508 
509 // Build up colIndices for each row.
510 void buildColIndex( unsigned int nrows,
511  const vector< unsigned int >& parentVoxel,
512  vector< vector< unsigned int > >& colIndex
513  )
514 {
515  colIndex.clear();
516  colIndex.resize( nrows );
517  for ( unsigned int i = 0; i < nrows; ++i ) {
518  if ( parentVoxel[i] != EMPTY_VOXEL ) {
519  colIndex[i].push_back( parentVoxel[i] ); // parent
520  colIndex[ parentVoxel[i] ].push_back( i ); // children
521  }
522  colIndex[i].push_back( i ); // diagonal: self
523  }
524  for ( unsigned int i = 0; i < nrows; ++i ) {
525  vector< unsigned int >& c = colIndex[i];
526  sort( c.begin(), c.end() );
527  // Should check that there are no duplicates.
528  for ( unsigned int j = 1; j < c.size(); ++j ) {
529  assert( c[j -1 ] != c[j] );
530  }
531  }
532 }
533 
538 void findAreaProportion( vector< double >& areaProportion,
539  const vector< unsigned int >& parentVoxel,
540  const vector< double >& area )
541 {
542  unsigned int nrows = parentVoxel.size();
543  vector< double > sumAreaOfChildren( nrows, 0.0 );
544  for ( unsigned int i = 0; i < nrows; ++i ) {
545  if ( parentVoxel[i] != EMPTY_VOXEL )
546  sumAreaOfChildren[ parentVoxel[i] ] += area[i];
547  }
548  for ( unsigned int i = 0; i < nrows; ++i ) {
549  if ( parentVoxel[i] != EMPTY_VOXEL )
550  areaProportion[i] = area[i]/sumAreaOfChildren[ parentVoxel[i] ];
551  else
552  areaProportion[i] = 1.0;
553  }
554 }
555 
558  const vector< unsigned int >& parentVoxel,
559  const vector< double >& volume,
560  const vector< double >& area,
561  const vector< double >& length,
562  double diffConst, double motorConst, double dt )
563 {
564  // Too slow to matter.
565  if ( diffConst < 1e-18 && fabs( motorConst ) < 1e-12 )
566  return false;
567  assert( nrows_ == parentVoxel.size() );
568  assert( nrows_ == volume.size() );
569  assert( nrows_ == area.size() );
570  assert( nrows_ == length.size() );
571  vector< vector< unsigned int > > colIndex;
572 
573  buildColIndex( nrows_, parentVoxel, colIndex );
574  vector< bool > isTwig( nrows_, true );
575  for ( unsigned int i = 0; i < nrows_; ++i ) {
576  if ( parentVoxel[i] != EMPTY_VOXEL )
577  isTwig[ parentVoxel[i] ] = false;
578  }
579 
580  vector< double > areaProportion( nrows_, 1.0 );
581  findAreaProportion( areaProportion, parentVoxel, area );
582 
583  // Fill in the matrix entries for each colIndex
584  for ( unsigned int i = 0; i < nrows_; ++i ) {
585  vector< unsigned int >& c = colIndex[i];
586  vector< double > e( c.size(), 0.0 );
587 
588  for ( unsigned int j = 0; j < c.size(); ++j ) {
589  unsigned int k = c[j]; // This is the colIndex, in order.
590  double vol = volume[k];
591  double a = area[k];
592  double len = length[k];
593  if ( k == i ) { // Diagonal term
594  e[j] = 0.0 ;
595  // Fill in diffusion from all connected voxels.
596  for ( unsigned int p = 0; p < c.size(); ++p ) {
597  unsigned int q = c[p];
598  if ( q != i ) { // Skip self
599  e[j] -= (area[q] + a)/(length[q] + len )/vol;
600  }
601  }
602  e[j] *= diffConst;
603  // Fill in motor transport
604  if ( i > 0 && motorConst < 0 ) { // Towards soma
605  e[j] += motorConst / len;
606  }
607  if ( !isTwig[i] && motorConst > 0 ) { // Toward twigs
608  e[j] -= motorConst / len;
609  }
610  e[j] *= -dt; // The previous lot is the rate, so scale by dt
611  e[j] += 1.0; // term for self.
612  } else { // Not diagonal.
613  // Fill in diffusion from this entry to i.
614  e[j] = diffConst *
615  (area[i] + a)/(length[i] + len )/vol;
616 
617  // Fill in motor transport
618  if ( k == parentVoxel[i] && motorConst > 0 ) { //toward twig
619  e[j] += areaProportion[i] * motorConst / len;
620  }
621  if ( i == parentVoxel[k] && motorConst < 0 ) { //toward soma
622  e[j] -= motorConst / length[k];
623  }
624  e[j] *= -dt; // Scale the whole thing by dt.
625  }
626  }
627  // Now put it all in the sparase matrix.
628  addRow( i, e, c );
629  }
630  return true;
631 }
void testSorting()
unsigned int ncolumns_
Definition: SparseMatrix.h:702
void buildBackwardSub(vector< unsigned int > &diag, vector< Triplet< double > > &bops, vector< double > &diagVal)
static void advance(vector< double > &y, const vector< Triplet< double > > &ops, const vector< double > &diagVal)
unsigned int getRow(unsigned int row, const T **entry, const unsigned int **colIndex) const
Definition: SparseMatrix.h:288
bool hinesReorder(const vector< unsigned int > &parentVoxel, vector< unsigned int > &lookupOldRowsFromNew)
void transpose()
Definition: SparseMatrix.h:456
void shuffleRows(const vector< unsigned int > &lookupOldRowFromNew)
bool doubleEq(double x, double y)
Definition: doubleEq.cpp:16
vector< double > N_
Definition: SparseMatrix.h:703
void findAreaProportion(vector< double > &areaProportion, const vector< unsigned int > &parentVoxel, const vector< double > &area)
void addRow(unsigned int rowNum, const vector< double > &row)
Definition: SparseMatrix.h:387
const vector< unsigned int > & colIndex() const
Definition: SparseMatrix.h:431
bool operator==(const FastMatrixElim &other) const
static void opsReorder(const vector< unsigned int > &lookupOldRowsFromNew, vector< Triplet< double > > &ops, vector< double > &diagVal)
bool checkSymmetricShape() const
void setSize(unsigned int nrows, unsigned int ncolumns)
Definition: SparseMatrix.h:126
void makeTestMatrix(const double *test, unsigned int numCompts)
void setDiffusionAndTransport(const vector< unsigned int > &parentVoxel, double diffConst, double motorConst, double dt)
bool buildForDiffusion(const vector< unsigned int > &parentVoxel, const vector< double > &volume, const vector< double > &area, const vector< double > &length, double diffConst, double motorConst, double dt)
This function makes the diffusion matrix.
vector< unsigned int > colIndex_
Non-zero entries in the SparseMatrix.
Definition: SparseMatrix.h:709
bool isSymmetric() const
static const unsigned int EMPTY_VOXEL(-1)
void buildColIndex(unsigned int nrows, const vector< unsigned int > &parentVoxel, vector< vector< unsigned int > > &colIndex)
void buildForwardElim(vector< unsigned int > &diag, vector< Triplet< double > > &fops)
vector< unsigned int > rowStart_
Start index in the N_ and colIndex_ vectors, of each row.
Definition: SparseMatrix.h:712
void sortByColumn(vector< unsigned int > &col, vector< double > &entry)