complex_matrices.cc
Go to the documentation of this file.
1 // LIC// ====================================================================
2 // LIC// This file forms part of oomph-lib, the object-oriented,
3 // LIC// multi-physics finite-element library, available
4 // LIC// at http://www.oomph-lib.org.
5 // LIC//
6 // LIC// Copyright (C) 2006-2024 Matthias Heil and Andrew Hazel
7 // LIC//
8 // LIC// This library is free software; you can redistribute it and/or
9 // LIC// modify it under the terms of the GNU Lesser General Public
10 // LIC// License as published by the Free Software Foundation; either
11 // LIC// version 2.1 of the License, or (at your option) any later version.
12 // LIC//
13 // LIC// This library is distributed in the hope that it will be useful,
14 // LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // LIC// Lesser General Public License for more details.
17 // LIC//
18 // LIC// You should have received a copy of the GNU Lesser General Public
19 // LIC// License along with this library; if not, write to the Free Software
20 // LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 // LIC// 02110-1301 USA.
22 // LIC//
23 // LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
24 // LIC//
25 // LIC//====================================================================
26 #include "complex_matrices.h"
27 #include <set>
28 
29 namespace oomph
30 {
31  //============================================================================
32  /// Complete LU solve (overwrites RHS with solution). This is the
33  /// generic version which should not need to be over-written.
34  //============================================================================
35  void ComplexMatrixBase::solve(Vector<std::complex<double>>& rhs)
36  {
37 #ifdef PARANOID
38  // Check Matrix is square
39  if (nrow() != ncol())
40  {
41  throw OomphLibError(
42  "This matrix is not square, the matrix MUST be square!",
43  OOMPH_CURRENT_FUNCTION,
44  OOMPH_EXCEPTION_LOCATION);
45  }
46  // Check that size of rhs = nrow()
47  if (rhs.size() != nrow())
48  {
49  std::ostringstream error_message_stream;
50  error_message_stream << "The rhs vector is not the right size. It is "
51  << rhs.size() << ", it should be " << nrow()
52  << std::endl;
53 
54  throw OomphLibError(error_message_stream.str(),
55  OOMPH_CURRENT_FUNCTION,
56  OOMPH_EXCEPTION_LOCATION);
57  }
58 #endif
59 
60  // Call the LU decomposition
61  ludecompose();
62 
63  // Call the back substitution
64  lubksub(rhs);
65  }
66 
67 
68  //============================================================================
69  /// Complete LU solve (Nothing gets overwritten!). This generic
70  /// version should never need to be overwritten
71  //============================================================================
72  void ComplexMatrixBase::solve(const Vector<std::complex<double>>& rhs,
73  Vector<std::complex<double>>& soln)
74  {
75  // Set the solution vector equal to the rhs
76  // N.B. This won't work if we change to another vector format
77  soln = rhs;
78 
79  // Overwrite the solution vector (rhs is unchanged)
80  solve(soln);
81  }
82 
83 
84  //=======================================================================
85  /// Delete the storage that has been allocated for the LU factors, if
86  /// the matrix data is not itself being overwritten.
87  //======================================================================
89  {
90  // Clean up the LU factor storage, if it has been allocated
91  // and it's not the same as the matrix storage
92  if ((!Overwrite_matrix_storage) && (LU_factors != 0))
93  {
94  // Delete the pointer to the LU factors
95  delete[] LU_factors;
96  // Null out the vector
97  LU_factors = 0;
98  }
99  }
100 
101 
102  //=======================================================================
103  /// Destructor clean up the LU factors that have been allocated
104  //======================================================================
106  {
107  // Delete the storage for the index vector
108  delete Index;
109 
110  // Delete the pointer to the LU factors
111  delete[] LU_factors;
112 
113  // Null out the vector
114  LU_factors = 0;
115  }
116 
117  //============================================================================
118  /// LU decompose a matrix, over-writing it and recording the pivots
119  /// numbers in the Index vector.
120  /// Returns the sign of the determinant.
121  //============================================================================
123  {
124 #ifdef PARANOID
125  // Check Matrix is square
126  if (N != M)
127  {
128  throw OomphLibError(
129  "This matrix is not square, the matrix MUST be square!",
130  OOMPH_CURRENT_FUNCTION,
131  OOMPH_EXCEPTION_LOCATION);
132  }
133 #endif
134 
135  // Small constant
136  const double small_number = 1.0e-20;
137 
138  // If the Index vector has not already been allocated,
139  // allocated storage for the index of permutations
140  if (Index == 0)
141  {
142  Index = new Vector<long>;
143  }
144 
145  // Resize the index vector to the correct length
146  Index->resize(N);
147 
148  // Vector scaling stores the implicit scaling of each row
149  Vector<double> scaling(N);
150 
151  // Integer to store the sign that must multiply the determinant as
152  // a consequence of the row/column interchanges
153  int signature = 1;
154 
155  // Loop over rows to get implicit scaling information
156  for (unsigned long i = 0; i < N; i++)
157  {
158  double big = 0.0;
159  for (unsigned long j = 0; j < M; j++)
160  {
161  double tmp = std::abs((*this)(i, j));
162  if (tmp > big) big = tmp;
163  }
164  if (big == 0.0)
165  {
166  throw OomphLibError(
167  "Singular Matrix", OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
168  }
169  // Save the scaling
170  scaling[i] = 1.0 / big;
171  }
172 
173  // Firsly, we shall delete any previous LU storage.
174  // If the user calls this function twice without changing the matrix
175  // then it is their own inefficiency, not ours (this time).
177 
178  // Now if we're saving the LU factors separately (the default).
180  {
181  // Allocate storage for the LU factors
182  // Assign space for the n rows
183  LU_factors = new std::complex<double>[N * M];
184 
185  // Now we know that memory has been allocated, copy over
186  // the matrix values
187  for (unsigned long i = 0; i < (N * M); i++)
188  {
189  LU_factors[i] = Matrixdata[i];
190  }
191  }
192  // Otherwise the pointer to the LU_factors is the same as the
193  // matrix data
194  else
195  {
197  }
198 
199  // Loop over columns
200  for (unsigned long j = 0; j < M; j++)
201  {
202  // Initialise imax
203  unsigned long imax = 0;
204 
205  for (unsigned long i = 0; i < j; i++)
206  {
207  std::complex<double> sum = LU_factors[M * i + j];
208  for (unsigned long k = 0; k < i; k++)
209  {
210  sum -= LU_factors[M * i + k] * LU_factors[M * k + j];
211  }
212  LU_factors[M * i + j] = sum;
213  }
214 
215  // Initialise search for largest pivot element
216  double largest_entry = 0.0;
217  for (unsigned long i = j; i < N; i++)
218  {
219  std::complex<double> sum = LU_factors[M * i + j];
220  for (unsigned long k = 0; k < j; k++)
221  {
222  sum -= LU_factors[M * i + k] * LU_factors[M * k + j];
223  }
224  LU_factors[M * i + j] = sum;
225  // Set temporary
226  double tmp = scaling[i] * std::abs(sum);
227  if (tmp >= largest_entry)
228  {
229  largest_entry = tmp;
230  imax = i;
231  }
232  }
233 
234  // Test to see if we need to interchange rows
235  if (j != imax)
236  {
237  for (unsigned long k = 0; k < N; k++)
238  {
239  std::complex<double> tmp = LU_factors[M * imax + k];
240  LU_factors[M * imax + k] = LU_factors[M * j + k];
241  LU_factors[M * j + k] = tmp;
242  }
243  // Change the parity of signature
244  signature = -signature;
245  // Interchange scale factor
246  scaling[imax] = scaling[j];
247  }
248 
249  // Set the index
250  (*Index)[j] = imax;
251  if (LU_factors[M * j + j] == 0.0)
252  {
253  LU_factors[M * j + j] = small_number;
254  }
255  // Divide by pivot element
256  if (j != N - 1)
257  {
258  std::complex<double> tmp = 1.0 / LU_factors[M * j + j];
259  for (unsigned long i = j + 1; i < N; i++)
260  {
261  LU_factors[M * i + j] *= tmp;
262  }
263  }
264 
265  } // End of loop over columns
266 
267 
268  // Now multiply all the diagonal terms together to get the determinant
269  // Note that we need to use the mantissa, exponent formulation to
270  // avoid underflow errors. This is way more tedious in complex arithmetic.
271  std::complex<double> determinant_mantissa(1.0, 0.0);
272  std::complex<int> determinant_exponent(0, 0);
273  for (unsigned i = 0; i < N; i++)
274  {
275  // Get the next entry in mantissa exponent form
276  std::complex<double> entry;
277  int iexp_real = 0, iexp_imag = 0;
278  entry =
279  std::complex<double>(frexp(LU_factors[M * i + i].real(), &iexp_real),
280  frexp(LU_factors[M * i + i].imag(), &iexp_imag));
281 
282  // Now calculate the four terms that contribute to the real
283  // and imaginary parts
284  // i.e. A + Bi * C + Di = AC - BD + i(AD + BC)
285  double temp_mantissa[4];
286  int temp_exp[4];
287 
288  // Get the first contribution to the real part, AC
289  temp_mantissa[0] = determinant_mantissa.real() * entry.real();
290  temp_exp[0] = determinant_exponent.real() + iexp_real;
291  // Get the second contribution to the real part, DB
292  temp_mantissa[1] = determinant_mantissa.imag() * entry.imag();
293  temp_exp[1] = determinant_exponent.imag() + iexp_imag;
294 
295  // Get the first contribution to the imaginary part, AD
296  temp_mantissa[2] = determinant_mantissa.real() * entry.imag();
297  temp_exp[2] = determinant_exponent.real() + iexp_imag;
298  // Get the second contribution to the imaginary part, BC
299  temp_mantissa[3] = determinant_mantissa.imag() * entry.real();
300  temp_exp[3] = determinant_exponent.imag() + iexp_real;
301 
302  // Align the exponents in the two terms for each sum (real or imaginary)
303  // We always align up to the larger exponent
304  for (unsigned i = 0; i < 3; i += 2)
305  {
306  // If the first exponent is smaller, move it up
307  if (temp_exp[i] < temp_exp[i + 1])
308  {
309  // The smaller term
310  int lower = temp_exp[i];
311  // Loop over the difference in the exponents,
312  // scaling down the mantissa each time
313  int upper = temp_exp[i + 1];
314  for (int index = lower; index < upper; ++index)
315  {
316  temp_mantissa[i] /= 2.0;
317  ++temp_exp[i];
318  }
319  }
320  // Otherwise the second exponent is smaller
321  else
322  {
323  // The smaller term is the other
324  int lower = temp_exp[i + 1];
325  // Loop over the difference in the exponents,
326  // scaling down the mantissa each time
327  int upper = temp_exp[i];
328  for (int index = lower; index < upper; ++index)
329  {
330  temp_mantissa[i + 1] /= 2.0;
331  ++temp_exp[i + 1];
332  }
333  }
334  }
335 
336  // Now combine the terms AC - BD
337  // and Combine the terms AD + BC
338  determinant_mantissa =
339  std::complex<double>(temp_mantissa[0] - temp_mantissa[1],
340  temp_mantissa[2] + temp_mantissa[3]);
341  // The exponents will be the same, so pick one
342  determinant_exponent = std::complex<int>(temp_exp[0], temp_exp[2]);
343 
344  // Finally normalise the result
345  determinant_mantissa =
346  std::complex<double>(frexp(determinant_mantissa.real(), &iexp_real),
347  frexp(determinant_mantissa.imag(), &iexp_imag));
348 
349  int temp_real = determinant_exponent.real() + iexp_real;
350  int temp_imag = determinant_exponent.imag() + iexp_imag;
351 
352  determinant_exponent = std::complex<int>(temp_real, temp_imag);
353  }
354 
355  // Integer to store the sign of the determinant
356  int sign = 0;
357  // Find the sign of the determinant (left or right half-plane)
358  if (std::abs(determinant_mantissa) > 0.0)
359  {
360  sign = 1;
361  }
362  if (std::abs(determinant_mantissa) < 0.0)
363  {
364  sign = -1;
365  }
366 
367  // Multiply the sign by the signature
368  sign *= signature;
369 
370  // Return the sign of the determinant
371  return sign;
372  }
373 
374  //============================================================================
375  /// Back substitute an LU decomposed matrix.
376  //============================================================================
377  void DenseComplexMatrix::lubksub(Vector<std::complex<double>>& rhs)
378  {
379 #ifdef PARANOID
380  // Check Matrix is square
381  if (N != M)
382  {
383  throw OomphLibError(
384  "This matrix is not square, the matrix MUST be square!",
385  OOMPH_CURRENT_FUNCTION,
386  OOMPH_EXCEPTION_LOCATION);
387  }
388  // Check that size of rhs=nrow() and index=nrow() are correct!
389  if (rhs.size() != N)
390  {
391  std::ostringstream error_message_stream;
392  error_message_stream << "The rhs vector is not the right size. It is "
393  << rhs.size() << ", it should be " << N << std::endl;
394 
395  throw OomphLibError(error_message_stream.str(),
396  OOMPH_CURRENT_FUNCTION,
397  OOMPH_EXCEPTION_LOCATION);
398  }
399  if (Index == 0)
400  {
401  throw OomphLibError(
402  "Index vector has not been allocated. Have you called ludecompse()\n",
403  OOMPH_CURRENT_FUNCTION,
404  OOMPH_EXCEPTION_LOCATION);
405  }
406  if (Index->size() != N)
407  {
408  std::ostringstream error_message_stream;
409  error_message_stream << "The Index vector is not the right size. It is "
410  << Index->size() << ", it should be " << N
411  << std::endl;
412 
413  throw OomphLibError(error_message_stream.str(),
414  OOMPH_CURRENT_FUNCTION,
415  OOMPH_EXCEPTION_LOCATION);
416  }
417 #endif
418 
419  unsigned long ii = 0;
420  for (unsigned i = 0; i < N; i++)
421  {
422  unsigned long ip = (*Index)[i];
423  std::complex<double> sum = rhs[ip];
424  rhs[ip] = rhs[i];
425 
426  if (ii != 0)
427  {
428  for (unsigned long j = ii - 1; j < i; j++)
429  {
430  sum -= LU_factors[M * i + j] * rhs[j];
431  }
432  }
433  else if (sum != 0.0)
434  {
435  ii = i + 1;
436  }
437  rhs[i] = sum;
438  }
439 
440  // Now do the back substitution
441  for (long i = long(N) - 1; i >= 0; i--)
442  {
443  std::complex<double> sum = rhs[i];
444  for (long j = i + 1; j < long(N); j++)
445  sum -= LU_factors[M * i + j] * rhs[j];
446  rhs[i] = sum / LU_factors[M * i + i];
447  }
448  }
449 
450  //============================================================================
451  /// Find the residual of Ax=b, i.e. r=b-Ax
452  //============================================================================
453  void DenseComplexMatrix::residual(const Vector<std::complex<double>>& x,
454  const Vector<std::complex<double>>& rhs,
455  Vector<std::complex<double>>& residual)
456  {
457 #ifdef PARANOID
458  // Check Matrix is square
459  if (N != M)
460  {
461  throw OomphLibError(
462  "This matrix is not square, the matrix MUST be square!",
463  OOMPH_CURRENT_FUNCTION,
464  OOMPH_EXCEPTION_LOCATION);
465  }
466  // Check that size of rhs = nrow()
467  if (rhs.size() != N)
468  {
469  std::ostringstream error_message_stream;
470  error_message_stream << "The rhs vector is not the right size. It is "
471  << rhs.size() << ", it should be " << N << std::endl;
472 
473  throw OomphLibError(error_message_stream.str(),
474  OOMPH_CURRENT_FUNCTION,
475  OOMPH_EXCEPTION_LOCATION);
476  }
477  // Check that the size of x is correct
478  if (x.size() != N)
479  {
480  std::ostringstream error_message_stream;
481  error_message_stream << "The x vector is not the right size. It is "
482  << x.size() << ", it should be " << N << std::endl;
483 
484  throw OomphLibError(error_message_stream.str(),
485  OOMPH_CURRENT_FUNCTION,
486  OOMPH_EXCEPTION_LOCATION);
487  }
488 #endif
489  // If size of residual is wrong, correct it!
490  if (residual.size() != N)
491  {
492  residual.resize(N);
493  }
494 
495  // Multiply the matrix by the vector x in residual vector
496  for (unsigned long i = 0; i < N; i++)
497  {
498  residual[i] = rhs[i];
499  for (unsigned long j = 0; j < M; j++)
500  {
501  residual[i] -= Matrixdata[M * i + j] * x[j];
502  }
503  }
504  }
505 
506 
507  //============================================================================
508  /// Multiply the matrix by the vector x: soln=Ax
509  //============================================================================
510  void DenseComplexMatrix::multiply(const Vector<std::complex<double>>& x,
511  Vector<std::complex<double>>& soln)
512  {
513 #ifdef PARANOID
514  // Check to see if x.size() = ncol().
515  if (x.size() != M)
516  {
517  std::ostringstream error_message_stream;
518  error_message_stream << "The x vector is not the right size. It is "
519  << x.size() << ", it should be " << M << std::endl;
520 
521  throw OomphLibError(error_message_stream.str(),
522  OOMPH_CURRENT_FUNCTION,
523  OOMPH_EXCEPTION_LOCATION);
524  }
525 #endif
526 
527  if (soln.size() != N)
528  {
529  // Resize and initialize the solution vector
530  soln.resize(N);
531  }
532 
533  // Multiply the matrix A, by the vector x
534  for (unsigned long i = 0; i < N; i++)
535  {
536  soln[i] = 0.0;
537  for (unsigned long j = 0; j < M; j++)
538  {
539  soln[i] += Matrixdata[M * i + j] * x[j];
540  }
541  }
542  }
543 
544  //===========================================================================
545  /// Function to multiply this matrix by the CRComplexMatrix matrix_in.
546  /// In a serial matrix, there are 3 methods available:
547  /// Method 1: First runs through this matrix and matrix_in to find the storage
548  /// requirements for result - arrays of the correct size are
549  /// then allocated before performing the calculation.
550  /// Minimises memory requirements but more costly.
551  /// Method 2: Grows storage for values and column indices of result 'on the
552  /// fly' using an array of maps. Faster but more memory
553  /// intensive.
554  /// Method 3: Grows storage for values and column indices of result 'on the
555  /// fly' using a vector of vectors. Not particularly impressive
556  /// on the platforms we tried...
557  /// Method 2 is employed by default. See also CRDoubleMatrix::multiply(...)
558  //=============================================================================
560  CRComplexMatrix& result) const
561  {
562  // NB n is number of rows!
563  const unsigned long n = this->nrow();
564  const unsigned long m = matrix_in.ncol();
565  unsigned long local_nnz = 0;
566 
567  // pointers to arrays which store result
568  int* row_start = 0;
569  std::complex<double>* value = 0;
570  int* column_index = 0;
571 
572  // get pointers to matrix_in
573  const int* matrix_in_row_start = matrix_in.row_start();
574  const int* matrix_in_column_index = matrix_in.column_index();
575  const std::complex<double>* matrix_in_value = matrix_in.value();
576 
577  // get pointers to this matrix
578  const std::complex<double>* i_value = this->value();
579  const int* i_row_start = this->row_start();
580  const int* i_column_index = this->column_index();
581 
582  // clock_t clock1 = clock();
583 
584  // METHOD 1
585  // --------
588  {
589  // allocate storage for row starts
590  row_start = new int[n + 1];
591  row_start[0] = 0;
592 
593  // a set to store number of non-zero columns in each row of result
594  std::set<unsigned> columns;
595 
596  // run through rows of this matrix and matrix_in to find number of
597  // non-zero entries in each row of result
598  for (unsigned long i_row = 0; i_row < n; i_row++)
599  {
600  // run through non-zeros in i_row of this matrix
601  int i_row_end = i_row_start[i_row + 1];
602  for (int i_non_zero = i_row_start[i_row]; i_non_zero < i_row_end;
603  i_non_zero++)
604  {
605  // find column index for non-zero
606  int matrix_in_row = i_column_index[i_non_zero];
607 
608  // run through corresponding row in matrix_in
609  int matrix_in_row_end = matrix_in_row_start[matrix_in_row + 1];
610  for (int matrix_in_index = matrix_in_row_start[matrix_in_row];
611  matrix_in_index < matrix_in_row_end;
612  matrix_in_index++)
613  {
614  // find column index for non-zero in matrix_in and store in
615  // columns
616  columns.insert(matrix_in_column_index[matrix_in_index]);
617  }
618  }
619  // update row_start
620  row_start[i_row + 1] = row_start[i_row] + columns.size();
621 
622  // wipe values in columns
623  columns.clear();
624  }
625 
626  // set local_nnz
627  local_nnz = row_start[n];
628 
629  // allocate arrays for result
630  value = new std::complex<double>[local_nnz];
631  column_index = new int[local_nnz];
632 
633  // set all values of column_index to -1
634  for (unsigned long i = 0; i < local_nnz; i++)
635  {
636  column_index[i] = -1;
637  }
638 
639  // Calculate values for result - first run through rows of this matrix
640  for (unsigned long i_row = 0; i_row < n; i_row++)
641  {
642  // run through non-zeros in i_row
643  int i_row_end = i_row_start[i_row + 1];
644  for (int i_non_zero = i_row_start[i_row]; i_non_zero < i_row_end;
645  i_non_zero++)
646  {
647  // find value of non-zero
648  std::complex<double> i_val = i_value[i_non_zero];
649 
650  // find column associated with non-zero
651  int matrix_in_row = i_column_index[i_non_zero];
652 
653  // run through corresponding row in matrix_in
654  int matrix_in_row_end = matrix_in_row_start[matrix_in_row + 1];
655  for (int matrix_in_index = matrix_in_row_start[matrix_in_row];
656  matrix_in_index < matrix_in_row_end;
657  matrix_in_index++)
658  {
659  // find column index for non-zero in matrix_in
660  int col = matrix_in_column_index[matrix_in_index];
661 
662  // find position in result to insert value
663  int row_end = row_start[i_row + 1];
664  for (int insert_position = row_start[i_row];
665  insert_position <= row_end;
666  insert_position++)
667  {
668  if (insert_position == row_end)
669  {
670  // error - have passed end of row without finding
671  // correct column
672  std::ostringstream error_message;
673  error_message << "Error inserting value in result";
674 
675  throw OomphLibError(error_message.str(),
676  OOMPH_CURRENT_FUNCTION,
677  OOMPH_EXCEPTION_LOCATION);
678  }
679  else if (column_index[insert_position] == -1)
680  {
681  // first entry for this column index
682  column_index[insert_position] = col;
683  value[insert_position] =
684  i_val * matrix_in_value[matrix_in_index];
685  break;
686  }
687  else if (column_index[insert_position] == col)
688  {
689  // column index already exists - add value
690  value[insert_position] +=
691  i_val * matrix_in_value[matrix_in_index];
692  break;
693  }
694  }
695  }
696  }
697  }
698  }
699  // METHOD 2
700  // --------
701  else if (this->Serial_matrix_matrix_multiply_method ==
703  {
704  // generate array of maps to store values for result
705  std::map<int, std::complex<double>>* result_maps =
706  new std::map<int, std::complex<double>>[n];
707 
708  // run through rows of this matrix
709  for (unsigned long i_row = 0; i_row < n; i_row++)
710  {
711  // run through non-zeros in i_row
712  int i_row_end = i_row_start[i_row + 1];
713  for (int i_non_zero = i_row_start[i_row]; i_non_zero < i_row_end;
714  i_non_zero++)
715  {
716  // find value of non-zero
717  std::complex<double> i_val = i_value[i_non_zero];
718 
719  // find column index associated with non-zero
720  int matrix_in_row = i_column_index[i_non_zero];
721 
722  // run through corresponding row in matrix_in
723  int matrix_in_row_end = matrix_in_row_start[matrix_in_row + 1];
724  for (int matrix_in_index = matrix_in_row_start[matrix_in_row];
725  matrix_in_index < matrix_in_row_end;
726  matrix_in_index++)
727  {
728  // find column index for non-zero in matrix_in
729  int col = matrix_in_column_index[matrix_in_index]; // This is the
730  // offending line
731  // insert value
732  result_maps[i_row][col] += i_val * matrix_in_value[matrix_in_index];
733  }
734  }
735  }
736 
737  // allocate row_start
738  row_start = new int[n + 1];
739 
740  // copy across row starts
741  row_start[0] = 0;
742  for (unsigned long row = 0; row < n; row++)
743  {
744  int size = result_maps[row].size();
745  row_start[row + 1] = row_start[row] + size;
746  }
747 
748  // set local_nnz
749  local_nnz = row_start[n];
750 
751  // allocate other arrays
752  value = new std::complex<double>[local_nnz];
753  column_index = new int[local_nnz];
754 
755  // copy values and column indices
756  for (unsigned long row = 0; row < n; row++)
757  {
758  unsigned insert_position = row_start[row];
759  for (std::map<int, std::complex<double>>::iterator i =
760  result_maps[row].begin();
761  i != result_maps[row].end();
762  i++)
763  {
764  column_index[insert_position] = i->first;
765  value[insert_position] = i->second;
766  insert_position++;
767  }
768  }
769  // tidy up memory
770  delete[] result_maps;
771  }
772  // METHOD 3
773  // --------
774  else if (this->Serial_matrix_matrix_multiply_method ==
776  {
777  // vectors of vectors to store results
778  std::vector<std::vector<int>> result_cols(n);
779  std::vector<std::vector<std::complex<double>>> result_vals(n);
780 
781  // run through the rows of this matrix
782  for (unsigned long i_row = 0; i_row < n; i_row++)
783  {
784  // run through non-zeros in i_row
785  int i_row_end = i_row_start[i_row + 1];
786  for (int i_non_zero = i_row_start[i_row]; i_non_zero < i_row_end;
787  i_non_zero++)
788  {
789  // find value of non-zero
790  std::complex<double> i_val = i_value[i_non_zero];
791 
792  // find column index associated with non-zero
793  int matrix_in_row = i_column_index[i_non_zero];
794 
795  // run through corresponding row in matrix_in
796  int matrix_in_row_end = matrix_in_row_start[matrix_in_row + 1];
797  for (int matrix_in_index = matrix_in_row_start[matrix_in_row];
798  matrix_in_index < matrix_in_row_end;
799  matrix_in_index++)
800  {
801  // find column index for non-zero in matrix_in
802  int col = matrix_in_column_index[matrix_in_index];
803 
804  // insert value
805  int size = result_cols[i_row].size();
806  for (int i = 0; i <= size; i++)
807  {
808  if (i == size)
809  {
810  // first entry for this column
811  result_cols[i_row].push_back(col);
812  result_vals[i_row].push_back(i_val *
813  matrix_in_value[matrix_in_index]);
814  }
815  else if (col == result_cols[i_row][i])
816  {
817  // column already exists
818  result_vals[i_row][i] +=
819  i_val * matrix_in_value[matrix_in_index];
820  break;
821  }
822  }
823  }
824  }
825  }
826 
827  // allocate row_start
828  row_start = new int[n + 1];
829 
830  // copy across row starts
831  row_start[0] = 0;
832  for (unsigned long row = 0; row < n; row++)
833  {
834  int size = result_cols[row].size();
835  row_start[row + 1] = row_start[row] + size;
836  }
837 
838  // set local_nnz
839  local_nnz = row_start[n];
840 
841  // allocate other arrays
842  value = new std::complex<double>[local_nnz];
843  column_index = new int[local_nnz];
844 
845  // copy across values and column indices
846  for (unsigned long row = 0; row < n; row++)
847  {
848  unsigned insert_position = row_start[row];
849  unsigned nnn = result_cols[row].size();
850  for (unsigned i = 0; i < nnn; i++)
851  {
852  column_index[insert_position] = result_cols[row][i];
853  value[insert_position] = result_vals[row][i];
854  insert_position++;
855  }
856  }
857  }
858 
859  // build
860  const unsigned long n_nz = this->nnz();
861  result.build_without_copy(value, column_index, row_start, n_nz, m, n);
862  }
863 
864  //=============================================================================
865  /// Element-wise addition of this matrix with matrix_in.
866  //=============================================================================
867  void CRComplexMatrix::add(const CRComplexMatrix& matrix_in,
868  CRComplexMatrix& result_matrix) const
869  {
870 #ifdef PARANOID
871  // Check if the dimensions of this matrix and matrix_in are the same.
872  const unsigned long i_nrow = this->nrow();
873  const unsigned long matrix_in_nrow = matrix_in.nrow();
874  if (i_nrow != matrix_in_nrow)
875  {
876  std::ostringstream error_message;
877  error_message << "matrix_in has a different number of rows than\n"
878  << "this matrix.\n";
879  throw OomphLibError(
880  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
881  }
882 
883  const unsigned long i_ncol = this->ncol();
884  const unsigned long matrix_in_ncol = matrix_in.ncol();
885  if (i_ncol != matrix_in_ncol)
886  {
887  std::ostringstream error_message;
888  error_message << "matrix_in has a different number of columns than\n"
889  << "this matrix.\n";
890  throw OomphLibError(
891  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
892  }
893 #endif
894 
895  // To add the elements of two CRComplexMatrices, we need to know the union
896  // of the sparsity patterns. This is determined by the column indices. We
897  // add the column indices and values (entries) as a key-value pair in a map
898  // (per row). We then read these out into two column indices and values
899  // vector for the result matrix.
900 
901  unsigned nrow_local = this->nrow();
902  Vector<int> res_column_indices;
903  Vector<std::complex<double>> res_values;
904  Vector<int> res_row_start;
905  res_row_start.reserve(nrow_local + 1);
906 
907  // The row_start and column_indices
908  const int* i_column_indices = this->column_index();
909  const int* i_row_start = this->row_start();
910  const int* in_column_indices = matrix_in.column_index();
911  const int* in_row_start = matrix_in.row_start();
912 
913  // Values from this matrix and matrix_in.
914  const std::complex<double>* i_values = this->value();
915  const std::complex<double>* in_values = matrix_in.value();
916 
917  // The first entry in row_start is always zero.
918  res_row_start.push_back(0);
919 
920  // Loop through the rows of both matrices and insert the column indices and
921  // values as a key-value pair.
922  for (unsigned row_i = 0; row_i < nrow_local; row_i++)
923  {
924  // Create the map for this row.
925  std::map<int, std::complex<double>> res_row_map;
926 
927  // Insert the column and value pair for this matrix.
928  int i_row_end = i_row_start[row_i + 1];
929  for (int i = i_row_start[row_i]; i < i_row_end; i++)
930  {
931  res_row_map[i_column_indices[i]] = i_values[i];
932  }
933 
934  // Insert the column and value pair for in matrix.
935  int in_row_end = in_row_start[row_i + 1];
936  for (int i = in_row_start[row_i]; i < in_row_end; i++)
937  {
938  res_row_map[in_column_indices[i]] += in_values[i];
939  }
940 
941  // Fill in the row start
942  res_row_start.push_back(res_row_start.back() + res_row_map.size());
943 
944  // Now insert the key into res_column_indices and value into res_values
945  for (std::map<int, std::complex<double>>::iterator it =
946  res_row_map.begin();
947  it != res_row_map.end();
948  ++it)
949  {
950  res_column_indices.push_back(it->first);
951  res_values.push_back(it->second);
952  }
953  }
954 
955  // Finally build the result_matrix.
956  // Use the existing distribution.
957  result_matrix.build(res_values,
958  res_column_indices,
959  res_row_start,
960  this->ncol(),
961  this->nrow());
962  }
963 
964  //=============================================================================
965  /// Element-wise addition of this matrix with matrix_in.
966  //=============================================================================
967  void CRComplexMatrix::add(const CRDoubleMatrix& matrix_in,
968  CRComplexMatrix& result_matrix) const
969  {
970 #ifdef PARANOID
971  // Check if matrix_in is built.
972  if (!matrix_in.built())
973  {
974  std::ostringstream error_message;
975  error_message << "The matrix matrix_in is not built.\n"
976  << "Please build the matrix!\n";
977  throw OomphLibError(
978  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
979  }
980 
981  // Check if the dimensions of this matrix and matrix_in are the same.
982  const unsigned long n_row = this->nrow();
983  const unsigned long matrix_in_nrow = matrix_in.nrow();
984  if (n_row != matrix_in_nrow)
985  {
986  std::ostringstream error_message;
987  error_message << "matrix_in has a different number of rows than\n"
988  << "this matrix.\n";
989  throw OomphLibError(
990  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
991  }
992 
993  const unsigned long i_ncol = this->ncol();
994  const unsigned long matrix_in_ncol = matrix_in.ncol();
995  if (i_ncol != matrix_in_ncol)
996  {
997  std::ostringstream error_message;
998  error_message << "matrix_in has a different number of columns than\n"
999  << "this matrix.\n";
1000  throw OomphLibError(
1001  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
1002  }
1003 #endif
1004 
1005  // To add the elements of two CRMatrices, we need to know the union of
1006  // the sparsity patterns. This is determined by the column indices.
1007  // We add the column indices and values (entries) as a key-value pair in
1008  // a map (per row). We then read these out into two column indices and
1009  // values vector for the result matrix.
1010 
1011  unsigned nrow_local = this->nrow();
1012  Vector<int> res_column_indices;
1013  Vector<std::complex<double>> res_values;
1014  Vector<int> res_row_start;
1015  res_row_start.reserve(nrow_local + 1);
1016 
1017  // The row_start and column_indices
1018  const int* i_column_indices = this->column_index();
1019  const int* i_row_start = this->row_start();
1020  const int* in_column_indices = matrix_in.column_index();
1021  const int* in_row_start = matrix_in.row_start();
1022 
1023  // Values from this matrix and matrix_in.
1024  const std::complex<double>* i_values = this->value();
1025  const double* in_values = matrix_in.value();
1026 
1027  // The first entry in row_start is always zero.
1028  res_row_start.push_back(0);
1029 
1030  // Loop through the rows of both matrices and insert the column indices and
1031  // values as a key-value pair.
1032  for (unsigned row_i = 0; row_i < nrow_local; row_i++)
1033  {
1034  // Create the map for this row.
1035  std::map<int, std::complex<double>> res_row_map;
1036 
1037  // Insert the column and value pair for this matrix.
1038  int i_row_end = i_row_start[row_i + 1];
1039  for (int i = i_row_start[row_i]; i < i_row_end; i++)
1040  {
1041  res_row_map[i_column_indices[i]] = i_values[i];
1042  }
1043 
1044  // Insert the column and value pair for in matrix.
1045  int in_row_end = in_row_start[row_i + 1];
1046  for (int i = in_row_start[row_i]; i < in_row_end; i++)
1047  {
1048  res_row_map[in_column_indices[i]] += in_values[i];
1049  }
1050 
1051  // Fill in the row start
1052  res_row_start.push_back(res_row_start.back() + res_row_map.size());
1053 
1054  // Now insert the key into res_column_indices and value into res_values
1055  for (std::map<int, std::complex<double>>::iterator it =
1056  res_row_map.begin();
1057  it != res_row_map.end();
1058  ++it)
1059  {
1060  res_column_indices.push_back(it->first);
1061  res_values.push_back(it->second);
1062  }
1063  }
1064 
1065  // Finally build the result_matrix.
1066  // Use the existing distribution.
1067  result_matrix.build(res_values,
1068  res_column_indices,
1069  res_row_start,
1070  this->ncol(),
1071  this->nrow());
1072  }
1073 
1074 
1075  //=================================================================
1076  /// Multiply the transposed matrix by the vector x: soln=A^T x
1077  //=================================================================
1079  const Vector<std::complex<double>>& x, Vector<std::complex<double>>& soln)
1080  {
1081 #ifdef PARANOID
1082  // Check to see x.size() = nrow()
1083  if (x.size() != N)
1084  {
1085  std::ostringstream error_message_stream;
1086  error_message_stream << "The x vector is not the right size. It is "
1087  << x.size() << ", it should be " << N << std::endl;
1088 
1089  throw OomphLibError(error_message_stream.str(),
1090  OOMPH_CURRENT_FUNCTION,
1091  OOMPH_EXCEPTION_LOCATION);
1092  }
1093 #endif
1094 
1095  if (soln.size() != M)
1096  {
1097  // Resize and initialize the solution vector
1098  soln.resize(M);
1099  }
1100 
1101  // Initialise the solution
1102  for (unsigned long i = 0; i < M; i++)
1103  {
1104  soln[i] = 0.0;
1105  }
1106 
1107 
1108  // Matrix vector product
1109  for (unsigned long i = 0; i < N; i++)
1110  {
1111  for (unsigned long j = 0; j < M; j++)
1112  {
1113  soln[j] += Matrixdata[N * i + j] * x[i];
1114  }
1115  }
1116  }
1117 
1118  /// /////////////////////////////////////////////////////////////////////
1119  /// /////////////////////////////////////////////////////////////////////
1120  /// /////////////////////////////////////////////////////////////////////
1121 
1122 
1123  //===================================================================
1124  // Interface to SuperLU wrapper
1125  //===================================================================
1126  extern "C"
1127  {
1129  int*,
1130  int*,
1131  int*,
1132  std::complex<double>*,
1133  int*,
1134  int*,
1135  std::complex<double>*,
1136  int*,
1137  int*,
1138  int*,
1139  void*,
1140  int*);
1141  }
1142 
1143 
1144  //===================================================================
1145  /// Perform LU decomposition. Return the sign of the determinant
1146  //===================================================================
1148  {
1149 #ifdef PARANOID
1150  if (N != M)
1151  {
1152  std::ostringstream error_message_stream;
1153  error_message_stream << "Can only solve for quadratic matrices\n"
1154  << "N, M " << N << " " << M << std::endl;
1155 
1156  throw OomphLibError(error_message_stream.str(),
1157  OOMPH_CURRENT_FUNCTION,
1158  OOMPH_EXCEPTION_LOCATION);
1159  }
1160 #endif
1161 
1162  // SuperLU expects compressed column format: Set flag for
1163  // transpose (0/1) = (false/true)
1164  int transpose = 0;
1165 
1166  // Doc (0/1) = (false/true)
1167  int doc = 0;
1168  if (Doc_stats_during_solve) doc = 1;
1169 
1170  // Cast to integers for stupid SuperLU
1171  int n_aux = (int)N;
1172  int nnz_aux = (int)Nnz;
1173 
1174  // Integer to hold the sign of the determinant
1175  int sign = 0;
1176 
1177  // Just do the lu decompose phase (i=1)
1178  int i = 1;
1179  sign = superlu_complex(&i,
1180  &n_aux,
1181  &nnz_aux,
1182  0,
1183  Value,
1184  Row_index,
1185  Column_start,
1186  0,
1187  &n_aux,
1188  &transpose,
1189  &doc,
1190  &F_factors,
1191  &Info);
1192 
1193  // Return the sign of the determinant
1194  return sign;
1195  }
1196 
1197 
1198  //===================================================================
1199  /// Do the backsubstitution
1200  //===================================================================
1201  void CCComplexMatrix::lubksub(Vector<std::complex<double>>& rhs)
1202  {
1203 #ifdef PARANOID
1204  if (N != rhs.size())
1205  {
1206  std::ostringstream error_message_stream;
1207  error_message_stream << "Size of RHS doesn't match matrix size\n"
1208  << "N, rhs.size() " << N << " " << rhs.size()
1209  << std::endl;
1210 
1211  throw OomphLibError(error_message_stream.str(),
1212  OOMPH_CURRENT_FUNCTION,
1213  OOMPH_EXCEPTION_LOCATION);
1214  }
1215  if (N != M)
1216  {
1217  std::ostringstream error_message_stream;
1218  error_message_stream << "Can only solve for quadratic matrices\n"
1219  << "N, M " << N << " " << M << std::endl;
1220 
1221  throw OomphLibError(error_message_stream.str(),
1222  OOMPH_CURRENT_FUNCTION,
1223  OOMPH_EXCEPTION_LOCATION);
1224  }
1225 #endif
1226 
1227  /// RHS vector
1228  std::complex<double>* b = new std::complex<double>[N];
1229 
1230  // Copy across
1231  for (unsigned long i = 0; i < N; i++)
1232  {
1233  b[i] = rhs[i];
1234  }
1235 
1236  // SuperLU expects compressed column format: Set flag for
1237  // transpose (0/1) = (false/true)
1238  int transpose = 0;
1239 
1240  // Doc (0/1) = (false/true)
1241  int doc = 0;
1242  if (Doc_stats_during_solve) doc = 1;
1243 
1244  // Number of RHSs
1245  int nrhs = 1;
1246 
1247 
1248  // Cast to integers for stupid SuperLU
1249  int n_aux = (int)N;
1250  int nnz_aux = (int)Nnz;
1251 
1252  // SuperLU in three steps (lu decompose, back subst, cleanup)
1253  // Do the solve phase
1254  int i = 2;
1255  superlu_complex(&i,
1256  &n_aux,
1257  &nnz_aux,
1258  &nrhs,
1259  Value,
1260  Row_index,
1261  Column_start,
1262  b,
1263  &n_aux,
1264  &transpose,
1265  &doc,
1266  &F_factors,
1267  &Info);
1268 
1269  // Copy b into rhs vector
1270  for (unsigned long i = 0; i < N; i++)
1271  {
1272  rhs[i] = b[i];
1273  }
1274 
1275  // Cleanup
1276  delete[] b;
1277  }
1278 
1279 
1280  //===================================================================
1281  /// Cleanup storage
1282  //===================================================================
1284  {
1285  // Only bother if we've done an LU solve
1286  if (F_factors != 0)
1287  {
1288  // SuperLU expects compressed column format: Set flag for
1289  // transpose (0/1) = (false/true)
1290  int transpose = 0;
1291 
1292  // Doc (0/1) = (false/true)
1293  int doc = 0;
1294  if (Doc_stats_during_solve) doc = 1;
1295 
1296 
1297  // Cast to integers for stupid SuperLU
1298  int n_aux = (int)N;
1299  int nnz_aux = (int)Nnz;
1300 
1301  // SuperLU in three steps (lu decompose, back subst, cleanup)
1302  // Flag to indicate which solve step to do (1, 2 or 3)
1303  int i = 3;
1304  superlu_complex(&i,
1305  &n_aux,
1306  &nnz_aux,
1307  0,
1308  Value,
1309  Row_index,
1310  Column_start,
1311  0,
1312  &n_aux,
1313  &transpose,
1314  &doc,
1315  &F_factors,
1316  &Info);
1317  }
1318  }
1319 
1320 
1321  //===================================================================
1322  /// Work out residual vector r = b-Ax for candidate solution x
1323  //===================================================================
1324  void CCComplexMatrix::residual(const Vector<std::complex<double>>& x,
1325  const Vector<std::complex<double>>& rhs,
1326  Vector<std::complex<double>>& residual)
1327  {
1328 #ifdef PARANOID
1329  // Check Matrix is square
1330  if (N != M)
1331  {
1332  throw OomphLibError(
1333  "This matrix is not square, the matrix MUST be square!",
1334  OOMPH_CURRENT_FUNCTION,
1335  OOMPH_EXCEPTION_LOCATION);
1336  }
1337  // Check that size of rhs = nrow()
1338  if (rhs.size() != N)
1339  {
1340  std::ostringstream error_message_stream;
1341  error_message_stream << "The rhs vector is not the right size. It is "
1342  << rhs.size() << ", it should be " << N << std::endl;
1343 
1344  throw OomphLibError(error_message_stream.str(),
1345  OOMPH_CURRENT_FUNCTION,
1346  OOMPH_EXCEPTION_LOCATION);
1347  }
1348  // Check that the size of x is correct
1349  if (x.size() != N)
1350  {
1351  std::ostringstream error_message_stream;
1352  error_message_stream << "The x vector is not the right size. It is "
1353  << x.size() << ", it should be " << N << std::endl;
1354 
1355  throw OomphLibError(error_message_stream.str(),
1356  OOMPH_CURRENT_FUNCTION,
1357  OOMPH_EXCEPTION_LOCATION);
1358  }
1359 #endif
1360 
1361  const unsigned long r_n = residual.size();
1362  if (r_n != N)
1363  {
1364  residual.resize(N);
1365  }
1366 
1367  // Need to do this in loop over rows
1368  for (unsigned i = 0; i < N; i++)
1369  {
1370  residual[i] = rhs[i];
1371  }
1372  // Now loop over columns
1373  for (unsigned long j = 0; j < N; j++)
1374  {
1375  int column_end = Column_start[j + 1];
1376  for (long k = Column_start[j]; k < column_end; k++)
1377  {
1378  unsigned long i = Row_index[k];
1379  std::complex<double> a_ij = Value[k];
1380  residual[i] -= a_ij * x[j];
1381  }
1382  }
1383  }
1384 
1385  //===================================================================
1386  /// Multiply the matrix by the vector x
1387  //===================================================================
1388  void CCComplexMatrix::multiply(const Vector<std::complex<double>>& x,
1389  Vector<std::complex<double>>& soln)
1390  {
1391 #ifdef PARANOID
1392  // Check to see if x.size() = ncol()
1393  if (x.size() != M)
1394  {
1395  std::ostringstream error_message_stream;
1396  error_message_stream << "The x vector is not the right size. It is "
1397  << x.size() << ", it should be " << M << std::endl;
1398 
1399  throw OomphLibError(error_message_stream.str(),
1400  OOMPH_CURRENT_FUNCTION,
1401  OOMPH_EXCEPTION_LOCATION);
1402  }
1403 #endif
1404 
1405  if (soln.size() != N)
1406  {
1407  // Resize and initialize the solution vector
1408  soln.resize(N);
1409  }
1410  for (unsigned i = 0; i < N; i++)
1411  {
1412  soln[i] = 0.0;
1413  }
1414 
1415  for (unsigned long j = 0; j < N; j++)
1416  {
1417  int column_end = Column_start[j + 1];
1418  for (long k = Column_start[j]; k < column_end; k++)
1419  {
1420  unsigned long i = Row_index[k];
1421  std::complex<double> a_ij = Value[k];
1422  soln[i] += a_ij * x[j];
1423  }
1424  }
1425  }
1426 
1427 
1428  //=================================================================
1429  /// Multiply the transposed matrix by the vector x: soln=A^T x
1430  //=================================================================
1432  const Vector<std::complex<double>>& x, Vector<std::complex<double>>& soln)
1433  {
1434 #ifdef PARANOID
1435  // Check to see x.size() = nrow()
1436  if (x.size() != N)
1437  {
1438  std::ostringstream error_message_stream;
1439  error_message_stream << "The x vector is not the right size. It is "
1440  << x.size() << ", it should be " << N << std::endl;
1441 
1442  throw OomphLibError(error_message_stream.str(),
1443  OOMPH_CURRENT_FUNCTION,
1444  OOMPH_EXCEPTION_LOCATION);
1445  }
1446 #endif
1447 
1448  if (soln.size() != M)
1449  {
1450  // Resize and initialize the solution vector
1451  soln.resize(M);
1452  }
1453 
1454  // Initialise the solution
1455  for (unsigned long i = 0; i < M; i++)
1456  {
1457  soln[i] = 0.0;
1458  }
1459 
1460  // Matrix vector product
1461  for (unsigned long i = 0; i < N; i++)
1462  {
1463  int column_end = Column_start[i + 1];
1464  for (long k = Column_start[i]; k < column_end; k++)
1465  {
1466  unsigned long j = Row_index[k];
1467  std::complex<double> a_ij = Value[k];
1468  soln[j] += a_ij * x[i];
1469  }
1470  }
1471  }
1472 
1473 
1474  /// /////////////////////////////////////////////////////////////////////
1475  /// /////////////////////////////////////////////////////////////////////
1476  /// /////////////////////////////////////////////////////////////////////
1477 
1478 
1479  //===================================================================
1480  /// Do LU decomposition and return sign of determinant
1481  //===================================================================
1483  {
1484 #ifdef PARANOID
1485  if (N != M)
1486  {
1487  std::ostringstream error_message_stream;
1488  error_message_stream << "Can only solve for quadratic matrices\n"
1489  << "N, M " << N << " " << M << std::endl;
1490 
1491  throw OomphLibError(error_message_stream.str(),
1492  OOMPH_CURRENT_FUNCTION,
1493  OOMPH_EXCEPTION_LOCATION);
1494  }
1495 #endif
1496 
1497  // SuperLU expects compressed column format: Set flag for
1498  // transpose (0/1) = (false/true)
1499  int transpose = 1;
1500 
1501  // Doc (0/1) = (false/true)
1502  int doc = 0;
1503  if (Doc_stats_during_solve) doc = 1;
1504 
1505  // Copies so that const-ness is maintained
1506  int n_aux = int(N);
1507  int nnz_aux = int(Nnz);
1508 
1509  // Integer to hold the sign of the determinant
1510  int sign = 0;
1511 
1512  // Just do the lu decompose phase (i=1)
1513  int i = 1;
1514  sign = superlu_complex(&i,
1515  &n_aux,
1516  &nnz_aux,
1517  0,
1518  Value,
1519  Column_index,
1520  Row_start,
1521  0,
1522  &n_aux,
1523  &transpose,
1524  &doc,
1525  &F_factors,
1526  &Info);
1527  // Return sign of determinant
1528  return sign;
1529  }
1530 
1531 
1532  //===================================================================
1533  /// Do back-substitution
1534  //===================================================================
1535  void CRComplexMatrix::lubksub(Vector<std::complex<double>>& rhs)
1536  {
1537 #ifdef PARANOID
1538  if (N != rhs.size())
1539  {
1540  std::ostringstream error_message_stream;
1541  error_message_stream << "Size of RHS doesn't match matrix size\n"
1542  << "N, rhs.size() " << N << " " << rhs.size()
1543  << std::endl;
1544 
1545  throw OomphLibError(error_message_stream.str(),
1546  OOMPH_CURRENT_FUNCTION,
1547  OOMPH_EXCEPTION_LOCATION);
1548  }
1549  if (N != M)
1550  {
1551  std::ostringstream error_message_stream;
1552  error_message_stream << "Can only solve for quadratic matrices\n"
1553  << "N, M " << N << " " << M << std::endl;
1554 
1555  throw OomphLibError(error_message_stream.str(),
1556  OOMPH_CURRENT_FUNCTION,
1557  OOMPH_EXCEPTION_LOCATION);
1558  }
1559 #endif
1560 
1561  /// RHS vector
1562  std::complex<double>* b = new std::complex<double>[N];
1563 
1564  // Copy across
1565  for (unsigned long i = 0; i < N; i++)
1566  {
1567  b[i] = rhs[i];
1568  }
1569 
1570  // SuperLU expects compressed column format: Set flag for
1571  // transpose (0/1) = (false/true)
1572  int transpose = 1;
1573 
1574  // Doc (0/1) = (false/true)
1575  int doc = 0;
1576  if (Doc_stats_during_solve) doc = 1;
1577 
1578  // Number of RHSs
1579  int nrhs = 1;
1580 
1581  // Copies so that const-ness is maintained
1582  int n_aux = int(N);
1583  int nnz_aux = int(Nnz);
1584 
1585  // SuperLU in three steps (lu decompose, back subst, cleanup)
1586  // Do the solve phase
1587  int i = 2;
1588  superlu_complex(&i,
1589  &n_aux,
1590  &nnz_aux,
1591  &nrhs,
1592  Value,
1593  Column_index,
1594  Row_start,
1595  b,
1596  &n_aux,
1597  &transpose,
1598  &doc,
1599  &F_factors,
1600  &Info);
1601 
1602  // Copy b into rhs vector
1603  for (unsigned long i = 0; i < N; i++)
1604  {
1605  rhs[i] = b[i];
1606  }
1607 
1608  // Cleanup
1609  delete[] b;
1610  }
1611 
1612 
1613  //===================================================================
1614  /// Cleanup memory
1615  //===================================================================
1617  {
1618  // Only bother if we've done an LU solve
1619  if (F_factors != 0)
1620  {
1621  // SuperLU expects compressed column format: Set flag for
1622  // transpose (0/1) = (false/true)
1623  int transpose = 1;
1624 
1625  // Doc (0/1) = (false/true)
1626  int doc = 0;
1627  if (Doc_stats_during_solve) doc = 1;
1628 
1629  // Copies so that const-ness is maintained
1630  int n_aux = int(N);
1631  int nnz_aux = int(Nnz);
1632 
1633  // SuperLU in three steps (lu decompose, back subst, cleanup)
1634  // Flag to indicate which solve step to do (1, 2 or 3)
1635  int i = 3;
1636  superlu_complex(&i,
1637  &n_aux,
1638  &nnz_aux,
1639  0,
1640  Value,
1641  Column_index,
1642  Row_start,
1643  0,
1644  &n_aux,
1645  &transpose,
1646  &doc,
1647  &F_factors,
1648  &Info);
1649  }
1650  }
1651 
1652 
1653  //=================================================================
1654  /// Find the residulal to x of Ax=b, ie r=b-Ax
1655  //=================================================================
1656  void CRComplexMatrix::residual(const Vector<std::complex<double>>& x,
1657  const Vector<std::complex<double>>& rhs,
1658  Vector<std::complex<double>>& residual)
1659  {
1660 #ifdef PARANOID
1661  // Check that size of rhs = nrow()
1662  if (rhs.size() != N)
1663  {
1664  std::ostringstream error_message_stream;
1665  error_message_stream << "The rhs vector is not the right size. It is "
1666  << rhs.size() << ", it should be " << N << std::endl;
1667 
1668  throw OomphLibError(error_message_stream.str(),
1669  OOMPH_CURRENT_FUNCTION,
1670  OOMPH_EXCEPTION_LOCATION);
1671  }
1672  // Check that the size of x is correct
1673  if (x.size() != M)
1674  {
1675  std::ostringstream error_message_stream;
1676  error_message_stream << "The x vector is not the right size. It is "
1677  << x.size() << ", it should be " << M << std::endl;
1678 
1679  throw OomphLibError(error_message_stream.str(),
1680  OOMPH_CURRENT_FUNCTION,
1681  OOMPH_EXCEPTION_LOCATION);
1682  }
1683 #endif
1684 
1685  if (residual.size() != N)
1686  {
1687  residual.resize(N);
1688  }
1689 
1690  for (unsigned long i = 0; i < N; i++)
1691  {
1692  residual[i] = rhs[i];
1693  int row_end = Row_start[i + 1];
1694  for (long k = Row_start[i]; k < row_end; k++)
1695  {
1696  unsigned long j = Column_index[k];
1697  std::complex<double> a_ij = Value[k];
1698  residual[i] -= a_ij * x[j];
1699  }
1700  }
1701  }
1702 
1703  //=================================================================
1704  /// Multiply the matrix by the vector x
1705  //=================================================================
1706  void CRComplexMatrix::multiply(const Vector<std::complex<double>>& x,
1707  Vector<std::complex<double>>& soln)
1708  {
1709 #ifdef PARANOID
1710  // Check to see x.size() = ncol()
1711  if (x.size() != M)
1712  {
1713  std::ostringstream error_message_stream;
1714  error_message_stream << "The x vector is not the right size. It is "
1715  << x.size() << ", it should be " << M << std::endl;
1716 
1717  throw OomphLibError(error_message_stream.str(),
1718  OOMPH_CURRENT_FUNCTION,
1719  OOMPH_EXCEPTION_LOCATION);
1720  }
1721 #endif
1722 
1723  if (soln.size() != N)
1724  {
1725  // Resize and initialize the solution vector
1726  soln.resize(N);
1727  }
1728  for (unsigned long i = 0; i < N; i++)
1729  {
1730  soln[i] = 0.0;
1731  int row_end = Row_start[i + 1];
1732  for (long k = Row_start[i]; k < row_end; k++)
1733  {
1734  unsigned long j = Column_index[k];
1735  std::complex<double> a_ij = Value[k];
1736  soln[i] += a_ij * x[j];
1737  }
1738  }
1739  }
1740 
1741 
1742  //=================================================================
1743  /// Multiply the transposed matrix by the vector x: soln=A^T x
1744  //=================================================================
1746  const Vector<std::complex<double>>& x, Vector<std::complex<double>>& soln)
1747  {
1748 #ifdef PARANOID
1749  // Check to see x.size() = nrow()
1750  if (x.size() != N)
1751  {
1752  std::ostringstream error_message_stream;
1753  error_message_stream << "The x vector is not the right size. It is "
1754  << x.size() << ", it should be " << N << std::endl;
1755 
1756  throw OomphLibError(error_message_stream.str(),
1757  OOMPH_CURRENT_FUNCTION,
1758  OOMPH_EXCEPTION_LOCATION);
1759  }
1760 #endif
1761 
1762  if (soln.size() != M)
1763  {
1764  // Resize and initialize the solution vector
1765  soln.resize(M);
1766  }
1767 
1768  // Initialise the solution
1769  for (unsigned long i = 0; i < M; i++)
1770  {
1771  soln[i] = 0.0;
1772  }
1773 
1774  // Matrix vector product
1775  for (unsigned long i = 0; i < N; i++)
1776  {
1777  int row_end = Row_start[i + 1];
1778  for (long k = Row_start[i]; k < row_end; k++)
1779  {
1780  unsigned long j = Column_index[k];
1781  std::complex<double> a_ij = Value[k];
1782  soln[j] += a_ij * x[i];
1783  }
1784  }
1785  }
1786 } // namespace oomph
cstr elem_len * i
Definition: cfortran.h:603
void multiply(const Vector< std::complex< double >> &x, Vector< std::complex< double >> &soln)
Multiply the matrix by the vector x: soln=Ax.
int Info
Info flag for the SuperLU solver.
void lubksub(Vector< std::complex< double >> &rhs)
LU back solve for given RHS.
int ludecompose()
LU decomposition using SuperLU.
void clean_up_memory()
LU clean up (perhaps this should happen in the destructor)
bool Doc_stats_during_solve
Flag to indicate if stats are to be displayed during solution of linear system with SuperLU.
void residual(const Vector< std::complex< double >> &x, const Vector< std::complex< double >> &b, Vector< std::complex< double >> &residual)
Find the residulal to x of Ax=b, ie r=b-Ax.
void * F_factors
Storage for the LU factors as required by SuperLU.
void multiply_transpose(const Vector< std::complex< double >> &x, Vector< std::complex< double >> &soln)
Multiply the transposed matrix by the vector x: soln=A^T x.
int * Column_start
Start index for column.
Definition: matrices.h:2779
A class for compressed row matrices.
unsigned long nrow() const
Return the number of rows of the matrix.
void * F_factors
Storage for the LU factors as required by SuperLU.
void multiply(const Vector< std::complex< double >> &x, Vector< std::complex< double >> &soln)
Multiply the matrix by the vector x: soln=Ax.
unsigned long ncol() const
Return the number of columns of the matrix.
bool Doc_stats_during_solve
Flag to indicate if stats are to be displayed during solution of linear system with SuperLU.
SerialMatrixMultiplyMethod Serial_matrix_matrix_multiply_method
A switch variable for selecting the matrix multiply method for serial (non-parallel) runs....
void add(const CRDoubleMatrix &matrix_in, CRComplexMatrix &result_matrix) const
Element-wise addition of this matrix with matrix_in.
int ludecompose()
LU decomposition using SuperLU.
void lubksub(Vector< std::complex< double >> &rhs)
LU back solve for given RHS.
int Info
Info flag for the SuperLU solver.
void residual(const Vector< std::complex< double >> &x, const Vector< std::complex< double >> &b, Vector< std::complex< double >> &residual)
Find the residual to x of Ax=b, i.e. r=b-Ax.
void multiply_transpose(const Vector< std::complex< double >> &x, Vector< std::complex< double >> &soln)
Multiply the transposed matrix by the vector x: soln=A^T x.
void clean_up_memory()
LU clean up (perhaps this should happen in the destructor)
A class for compressed row matrices. This is a distributable object.
Definition: matrices.h:888
int * column_index()
Access to C-style column index array.
Definition: matrices.h:1072
int * row_start()
Access to C-style row_start array.
Definition: matrices.h:1060
unsigned long ncol() const
Return the number of columns of the matrix.
Definition: matrices.h:1008
bool built() const
access function to the Built flag - indicates whether the matrix has been build - i....
Definition: matrices.h:1210
double * value()
Access to C-style value array.
Definition: matrices.h:1084
unsigned long nrow() const
Return the number of rows of the matrix.
Definition: matrices.h:1002
int * row_start()
Access to C-style row_start array.
Definition: matrices.h:785
int * Row_start
Start index for row.
Definition: matrices.h:873
void build(const Vector< T > &value, const Vector< int > &column_index, const Vector< int > &row_start, const unsigned long &n, const unsigned long &m)
Build matrix from compressed representation. Number of nonzero entries is read off from value,...
Definition: matrices.h:3404
int * column_index()
Access to C-style column index array.
Definition: matrices.h:797
void build_without_copy(T *value, int *column_index, int *row_start, const unsigned long &nnz, const unsigned long &n, const unsigned long &m)
Function to build matrix from pointers to arrays which hold the row starts, column indices and non-ze...
Definition: matrices.h:3354
virtual int ludecompose()
LU decomposition of the matrix using the appropriate linear solver. Return the sign of the determinan...
virtual unsigned long ncol() const =0
Return the number of columns of the matrix.
virtual void lubksub(Vector< std::complex< double >> &rhs)
LU backsubstitue a previously LU-decomposed matrix; The passed rhs will be over-written with the solu...
virtual unsigned long nrow() const =0
Return the number of rows of the matrix.
virtual void solve(Vector< std::complex< double >> &rhs)
Complete LU solve (replaces matrix by its LU decomposition and overwrites RHS with solution)....
bool Overwrite_matrix_storage
Boolean flag used to decided whether the LU decomposition is stored separately, or not.
void lubksub(Vector< std::complex< double >> &rhs)
Overload the LU back substitution. Note that the rhs will be overwritten with the solution vector.
virtual ~DenseComplexMatrix()
Destructor, delete the storage for Index vector and LU storage (if any)
Vector< long > * Index
Pointer to storage for the index of permutations in the LU solve.
void residual(const Vector< std::complex< double >> &x, const Vector< std::complex< double >> &rhs, Vector< std::complex< double >> &residual)
Find the residual of Ax=b, ie r=b-Ax for the "solution" x.
void multiply(const Vector< std::complex< double >> &x, Vector< std::complex< double >> &soln)
Multiply the matrix by the vector x: soln=Ax.
int ludecompose()
Overload the LU decomposition. For this storage scheme the matrix storage will be over-writeen by its...
void delete_lu_factors()
Function that deletes the storage for the LU_factors, if it is not the same as the matrix storage.
std::complex< double > * LU_factors
Pointer to storage for the LU decomposition.
void multiply_transpose(const Vector< std::complex< double >> &x, Vector< std::complex< double >> &soln)
Multiply the transposed matrix by the vector x: soln=A^T x.
std::complex< double > & entry(const unsigned long &i, const unsigned long &j)
The access function that will be called by the read-write round-bracket operator.
Definition: matrices.h:447
unsigned long N
Number of rows.
Definition: matrices.h:392
std::complex< double > * Matrixdata
Internal representation of matrix as a pointer to data.
Definition: matrices.h:389
unsigned long M
Number of columns.
Definition: matrices.h:395
An OomphLibError object which should be thrown when an run-time error is encountered....
unsigned long Nnz
Number of non-zero values (i.e. size of Value array)
Definition: matrices.h:574
T * value()
Access to C-style value array.
Definition: matrices.h:616
T * Value
Internal representation of the matrix values, a pointer.
Definition: matrices.h:565
unsigned long nnz() const
Return the number of nonzero entries.
Definition: matrices.h:640
unsigned long N
Number of rows.
Definition: matrices.h:568
unsigned long M
Number of columns.
Definition: matrices.h:571
A slight extension to the standard template vector class so that we can include "graceful" array rang...
Definition: Vector.h:58
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
int superlu_complex(int *, int *, int *, int *, std::complex< double > *, int *, int *, std::complex< double > *, int *, int *, int *, void *, int *)
///////////////////////////////////////////////////////////////////// ///////////////////////////////...