Nektar++
Loading...
Searching...
No Matches
MatrixFuncs.h
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: MatrixFuncs.h
4//
5// For more information, please see: http://www.nektar.info
6//
7// The MIT License
8//
9// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10// Department of Aeronautics, Imperial College London (UK), and Scientific
11// Computing and Imaging Institute, University of Utah (USA).
12//
13// Permission is hereby granted, free of charge, to any person obtaining a
14// copy of this software and associated documentation files (the "Software"),
15// to deal in the Software without restriction, including without limitation
16// the rights to use, copy, modify, merge, publish, distribute, sublicense,
17// and/or sell copies of the Software, and to permit persons to whom the
18// Software is furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included
21// in all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29// DEALINGS IN THE SOFTWARE.
30//
31// Description: Matrix functions that depend on storage policy. Putting
32// methods in these separate classes makes it easier to use them from
33// normal, scaled, and block matrices.
34//
35///////////////////////////////////////////////////////////////////////////////
36
37#ifndef NEKTAR_LIB_UTILITIES_LINEAR_ALGEBRA_MATRIX_FUNCS_H
38#define NEKTAR_LIB_UTILITIES_LINEAR_ALGEBRA_MATRIX_FUNCS_H
39
44#include <algorithm>
45#include <cmath>
46#include <limits>
47#include <tuple>
48#include <type_traits>
49
50namespace Nektar
51{
53{
54 /// \brief Calculates and returns the storage size required.
55 ///
56 /// This method assumes that the matrix will be used with LU factorizationa
57 /// and allocates additional storage as appropriate.
58 static unsigned int GetRequiredStorageSize(unsigned int totalRows,
59 unsigned int totalColumns,
60 unsigned int subDiags,
61 unsigned int superDiags);
62
63 static unsigned int CalculateNumberOfDiags(unsigned int totalRows,
64 unsigned int diags);
65
66 static unsigned int CalculateNumberOfRows(unsigned int totalRows,
67 unsigned int subDiags,
68 unsigned int superDiags);
69
70 static unsigned int CalculateIndex(unsigned int totalRows,
71 unsigned int totalColumns,
72 unsigned int row, unsigned int column,
73 unsigned int sub, unsigned int super);
74
75 static std::tuple<unsigned int, unsigned int> Advance(
76 const unsigned int totalRows, const unsigned int totalColumns,
77 const unsigned int curRow, const unsigned int curColumn);
78};
79
81{
82
83 static unsigned int GetRequiredStorageSize(unsigned int rows,
84 unsigned int columns);
85 static unsigned int CalculateIndex(unsigned int totalRows,
86 unsigned int totalColumns,
87 unsigned int curRow,
88 unsigned int curColumn);
89
90 static std::tuple<unsigned int, unsigned int> Advance(
91 const unsigned int totalRows, const unsigned int totalColumns,
92 const unsigned int curRow, const unsigned int curColumn);
93
94 template <typename DataType>
95 static void Invert(unsigned int rows, unsigned int columns,
96 Array<OneD, DataType> &data, const char transpose)
97 {
98 ASSERTL0(rows == columns, "Only square matrices can be inverted.");
99 ASSERTL0(transpose == 'N',
100 "Only untransposed matrices may be inverted.");
101
102 int m = rows;
103 int n = columns;
104 int info = 0;
105 Array<OneD, int> ipivot(n);
106 Array<OneD, DataType> work(n);
107
108 if (std::is_floating_point_v<DataType>)
109 {
110 switch (sizeof(DataType))
111 {
112 case sizeof(NekDouble):
113 break;
114 case sizeof(NekSingle):
115 break;
116 default:
117 ASSERTL0(
118 false,
119 "Invert DataType is neither NekDouble nor NekSingle");
120 break;
121 }
122 }
123 else
124 {
125 ASSERTL0(false,
126 "FullMatrixFuncs::Invert DataType is not floating point");
127 }
128
129 Lapack::DoSgetrf(m, n, data.data(), m, ipivot.data(), info);
130
131 if (info < 0)
132 {
133 std::string message =
134 "ERROR: The " + std::to_string(-info) +
135 "th parameter had an illegal parameter for dgetrf";
136 ASSERTL0(false, message.c_str());
137 }
138 else if (info > 0)
139 {
140 std::string message = "ERROR: Element u_" + std::to_string(info) +
141 std::to_string(info) + " is 0 from dgetrf";
142 ASSERTL0(false, message.c_str());
143 }
144
145 Lapack::DoSgetri(n, data.data(), n, ipivot.data(), work.data(), n,
146 info);
147
148 if (info < 0)
149 {
150 std::string message =
151 "ERROR: The " + std::to_string(-info) +
152 "th parameter had an illegal parameter for dgetri";
153 ASSERTL0(false, message.c_str());
154 }
155 else if (info > 0)
156 {
157 std::string message = "ERROR: Element u_" + std::to_string(info) +
158 std::to_string(info) + " is 0 from dgetri";
159 ASSERTL0(false, message.c_str());
160 }
161 }
162
163 // add a method to do the pseudo inverse of a full matrix, using SVD routine
164 // from Lapack.
165 template <typename DataType>
166 static void PseudoInverse(unsigned int rows, unsigned int columns,
168 {
169 int m = rows;
170 int n = columns;
171 int lda = m;
172 int ldu = m;
173 int ldvt = n;
174 int info = 0;
175 int min_mn = std::min(m, n);
176 // Copy input data since SVD destroys it
177 Array<OneD, DataType> a(data);
178 // SVD outputs
179 Array<OneD, DataType> s(min_mn, 0.0);
180 Array<OneD, DataType> u(ldu * m, 0.0);
181 Array<OneD, DataType> vt(ldvt * n, 0.0);
182 // Workspace query
183 DataType wkopt = 0.0;
184 int lwork = -1;
185 // SVD job: all singular vectors
186 char jobu = 'A';
187 char jobvt = 'A';
188 //
189 // DGESVD computes the singular value decomposition (SVD) of a real
190 // M-by-N matrix A, optionally computing the left and/or right singular
191 // vectors. The SVD is written
192 //
193 // A = U * SIGMA * transpose(V)
194 //
195 // where SIGMA is an M-by-N matrix which is zero except for its
196 // min(m,n) diagonal elements, U is an M-by-M orthogonal matrix, and
197 // V is an N-by-N orthogonal matrix. The diagonal elements of SIGMA
198 // are the singular values of A; they are real and non-negative, and
199 // are returned in descending order. The first min(m,n) columns of
200 // U and V are the left and right singular vectors of A.
201 //
202 // Note that the routine returns V**T, not V.
203 //
204 if (sizeof(DataType) == sizeof(NekDouble))
205 {
206 Lapack::Dgesvd(jobu, jobvt, m, n, (double *)a.data(), lda,
207 (double *)s.data(), (double *)u.data(), ldu,
208 (double *)vt.data(), ldvt, (double *)&wkopt, lwork,
209 info);
210 }
211 else if (sizeof(DataType) == sizeof(NekSingle))
212 {
213 Lapack::Sgesvd(jobu, jobvt, m, n, (float *)a.data(), lda,
214 (float *)s.data(), (float *)u.data(), ldu,
215 (float *)vt.data(), ldvt, (float *)&wkopt, lwork,
216 info);
217 }
218 else
219 {
220 ASSERTL0(
221 false,
222 "PseudoInverse DataType is neither NekDouble nor NekSingle");
223 }
224 lwork = static_cast<int>(wkopt);
225 Array<OneD, DataType> work(lwork);
226 if (sizeof(DataType) == sizeof(NekDouble))
227 {
228 Lapack::Dgesvd(jobu, jobvt, m, n, (double *)a.data(), lda,
229 (double *)s.data(), (double *)u.data(), ldu,
230 (double *)vt.data(), ldvt, (double *)work.data(),
231 lwork, info);
232 }
233 else if (sizeof(DataType) == sizeof(NekSingle))
234 {
235 Lapack::Sgesvd(jobu, jobvt, m, n, (float *)a.data(), lda,
236 (float *)s.data(), (float *)u.data(), ldu,
237 (float *)vt.data(), ldvt, (float *)work.data(),
238 lwork, info);
239 }
240 if (info < 0)
241 {
242 std::string message = "ERROR: The " + std::to_string(-info) +
243 "th parameter had an illegal value for gesvd";
244 ASSERTL0(false, message.c_str());
245 }
246 else if (info > 0)
247 {
248 std::string message = "ERROR: SVD did not converge in gesvd";
249 ASSERTL0(false, message.c_str());
250 }
251 // Compute pseudo-inverse: A^+ = V * S^+ * U^T
252 // S^+ is diagonal with 1/s_i for s_i > tol, 0 otherwise
253 DataType tol = std::numeric_limits<DataType>::epsilon() *
254 std::max(m, n) * std::abs(s[0]);
255 Array<OneD, DataType> sinv(min_mn, 0.0);
256 for (int i = 0; i < min_mn; ++i)
257 {
258 if (std::abs(s[i]) > tol)
259 {
260 sinv[i] = 1.0 / s[i];
261 }
262 else
263 {
264 sinv[i] = 0.0;
265 }
266 }
267 // temp = S^+ * U^T (size min_mn x m)
268 Array<OneD, DataType> temp(min_mn * m, 0.0);
269 // scale each row of U^T by sinv
270 for (int i = 0; i < min_mn; ++i)
271 {
272 for (int j = 0; j < m; ++j)
273 {
274 temp[i + j * min_mn] = sinv[i] * u[j + i * ldu];
275 }
276 }
277 // Use BLAS GEMM: data = V (n x min_mn) * temp (min_mn x m)
278 data = Array<OneD, DataType>(n * m, 0.0);
279 char transa = 'T';
280 char transb = 'N';
281 DataType alpha = 1.0;
282 DataType beta = 0.0;
283 if (sizeof(DataType) == sizeof(NekDouble))
284 {
285 Blas::Gemm(transa, transb, n, m, min_mn, alpha, (double *)vt.data(),
286 ldvt, (double *)temp.data(), min_mn, beta,
287 (double *)data.data(), n);
288 }
289 else if (sizeof(DataType) == sizeof(NekSingle))
290 {
291 Blas::Gemm(transa, transb, n, m, min_mn, (float)alpha,
292 (float *)vt.data(), ldvt, (float *)temp.data(), min_mn,
293 (float)beta, (float *)data.data(), n);
294 }
295 }
296
297 template <typename DataType>
298 static void EigenSolve(unsigned int n, const Array<OneD, const DataType> &A,
299 Array<OneD, DataType> &EigValReal,
300 Array<OneD, DataType> &EigValImag,
301 Array<OneD, DataType> &EigVecs)
302 {
303 int lda = n, info = 0;
304 DataType dum;
305 char uplo = 'N';
306
307 if (EigVecs.size() != 0) // calculate Right Eigen Vectors
308 {
309 int lwork = 4 * lda;
310 Array<OneD, DataType> work(4 * lda);
311 char lrev = 'V';
312 Lapack::DoSgeev(uplo, lrev, lda, A.data(), lda, EigValReal.data(),
313 EigValImag.data(), &dum, 1, EigVecs.data(), lda,
314 &work[0], lwork, info);
315 }
316 else
317 {
318 int lwork = 3 * lda;
319 Array<OneD, DataType> work(3 * lda);
320 char lrev = 'N';
321 Lapack::DoSgeev(uplo, lrev, lda, A.data(), lda, EigValReal.data(),
322 EigValImag.data(), &dum, 1, &dum, 1, &work[0],
323 lwork, info);
324 }
325 ASSERTL0(info == 0, "Info is not zero");
326 }
327};
328
330{
331 static unsigned int GetRequiredStorageSize(unsigned int rows,
332 unsigned int columns);
333};
334
336 : public TriangularMatrixFuncs
337{
338 static unsigned int CalculateIndex(unsigned int curRow,
339 unsigned int curColumn);
340
341 static std::tuple<unsigned int, unsigned int> Advance(
342 const unsigned int totalRows, const unsigned int totalColumns,
343 const unsigned int curRow, const unsigned int curColumn);
344};
345
347 : public TriangularMatrixFuncs
348{
349 static unsigned int CalculateIndex(unsigned int totalColumns,
350 unsigned int curRow,
351 unsigned int curColumn);
352
353 static std::tuple<unsigned int, unsigned int> Advance(
354 const unsigned int totalRows, const unsigned int totalColumns,
355 const unsigned int curRow, const unsigned int curColumn,
356 char transpose = 'N');
357};
358
359/// \internal
360/// Symmetric matrices use upper triangular packed storage.
362{
363 using TriangularMatrixFuncs::GetRequiredStorageSize;
364
365 static unsigned int CalculateIndex(unsigned int curRow,
366 unsigned int curColumn);
367
368 template <typename DataType>
369 static void Invert(unsigned int rows, unsigned int columns,
371 {
372 ASSERTL0(rows == columns, "Only square matrices can be inverted.");
373
374 int n = columns;
375 int info = 0;
376 Array<OneD, int> ipivot(n);
377 Array<OneD, DataType> work(n);
378
379 Lapack::DoSsptrf('U', n, data.data(), ipivot.data(), info);
380
381 if (info < 0)
382 {
383 std::string message =
384 "ERROR: The " + std::to_string(-info) +
385 "th parameter had an illegal parameter for dsptrf";
386 ASSERTL0(false, message.c_str());
387 }
388 else if (info > 0)
389 {
390 std::string message = "ERROR: Element u_" + std::to_string(info) +
391 std::to_string(info) + " is 0 from dsptrf";
392 ASSERTL0(false, message.c_str());
393 }
394
395 Lapack::DoSsptri('U', n, data.data(), ipivot.data(), work.data(), info);
396
397 if (info < 0)
398 {
399 std::string message =
400 "ERROR: The " + std::to_string(-info) +
401 "th parameter had an illegal parameter for dsptri";
402 ASSERTL0(false, message.c_str());
403 }
404 else if (info > 0)
405 {
406 std::string message = "ERROR: Element u_" + std::to_string(info) +
407 std::to_string(info) + " is 0 from dsptri";
408 ASSERTL0(false, message.c_str());
409 }
410 }
411
412 static std::tuple<unsigned int, unsigned int> Advance(
413 const unsigned int totalRows, const unsigned int totalColumns,
414 const unsigned int curRow, const unsigned int curColumn);
415};
416
418{
419 static std::tuple<unsigned int, unsigned int> Advance(
420 const unsigned int totalRows, const unsigned int totalColumns,
421 const unsigned int curRow, const unsigned int curColumn);
422
423 template <typename DataType>
424 static void Invert(unsigned int rows, unsigned int columns,
426 {
427 ASSERTL0(rows == columns, "Only square matrices can be inverted.");
428 for (unsigned int i = 0; i < rows; ++i)
429 {
430 data[i] = 1.0 / data[i];
431 }
432 }
433
434 static unsigned int GetRequiredStorageSize(unsigned int rows,
435 unsigned int columns);
436
437 static unsigned int CalculateIndex(unsigned int row, unsigned int col);
438};
439
441{
442 static unsigned int GetRequiredStorageSize(unsigned int rows,
443 unsigned int columns,
444 unsigned int nSubSuperDiags);
445};
446
451
456
457/// \internal
458/// Symmetric banded matrices use upper triangular banded packed storage.
461{
462 using TriangularBandedMatrixFuncs::GetRequiredStorageSize;
463
464 static unsigned int CalculateIndex(unsigned int curRow,
465 unsigned int curColumn,
466 unsigned int nSuperDiags);
467};
468
469} // namespace Nektar
470
471#endif // NEKTAR_LIB_UTILITIES_LINEAR_ALGEBRA_MATRIX_FUNCS_H
#define ASSERTL0(condition, msg)
#define LIB_UTILITIES_EXPORT
static void Gemm(const char &transa, const char &transb, const int &m, const int &n, const int &k, const double &alpha, const double *a, const int &lda, const double *b, const int &ldb, const double &beta, double *c, const int &ldc)
BLAS level 3: Matrix-matrix multiply C = A x B where op(A)[m x k], op(B)[k x n], C[m x n] DGEMM perfo...
Definition Blas.hpp:296
static void DoSsptrf(const char &uplo, const int &n, double *ap, int *ipiv, int &info)
factor a real packed-symmetric matrix using Bunch-Kaufman pivoting.
Definition Lapack.hpp:140
static void DoSsptri(const char &uplo, const int &n, const double *ap, const int *ipiv, double *work, int &info)
Invert a real packed-symmetric matrix problem.
Definition Lapack.hpp:190
static void DoSgetrf(const int &m, const int &n, double *a, const int &lda, int *ipiv, int &info)
General matrix LU factorisation.
Definition Lapack.hpp:276
static void Sgesvd(const char &jobu, const char &jobvt, const int &m, const int &n, float *a, const int &lda, float *s, float *u, const int &ldu, float *vt, const int &ldvt, float *work, const int &lwork, int &info)
Singular Value Decomposition (SVD) of a real matrix (single precision)
Definition Lapack.hpp:397
static void DoSgetri(const int &n, double *a, const int &lda, const int *ipiv, double *wk, const int &lwk, int &info)
General matrix inverse.
Definition Lapack.hpp:305
static void DoSgeev(const char &uplo, const char &lrev, const int &n, const double *a, const int &lda, double *wr, double *wi, double *rev, const int &ldr, double *lev, const int &ldv, double *work, const int &lwork, int &info)
Solve general real matrix eigenproblem.
Definition Lapack.hpp:335
static void Dgesvd(const char &jobu, const char &jobvt, const int &m, const int &n, double *a, const int &lda, double *s, double *u, const int &ldu, double *vt, const int &ldvt, double *work, const int &lwork, int &info)
Singular Value Decomposition (SVD) of a real matrix (double precision)
Definition Lapack.hpp:385
@ beta
Gauss Radau pinned at x=-1,.
Definition PointsType.h:59
static void Invert(unsigned int rows, unsigned int columns, Array< OneD, DataType > &data)
static void PseudoInverse(unsigned int rows, unsigned int columns, Array< OneD, DataType > &data)
static void EigenSolve(unsigned int n, const Array< OneD, const DataType > &A, Array< OneD, DataType > &EigValReal, Array< OneD, DataType > &EigValImag, Array< OneD, DataType > &EigVecs)
static void Invert(unsigned int rows, unsigned int columns, Array< OneD, DataType > &data, const char transpose)
Definition MatrixFuncs.h:95
static void Invert(unsigned int rows, unsigned int columns, Array< OneD, DataType > &data)