High-precision calculations for one- and two-valence atomic systems
Matrix.hpp
1#pragma once
2#include "qip/Vector.hpp"
3#include <array>
4#include <cassert>
5#include <complex>
6#include <gsl/gsl_blas.h>
7#include <gsl/gsl_complex.h>
8#include <gsl/gsl_complex_math.h>
9#include <gsl/gsl_eigen.h>
10#include <gsl/gsl_linalg.h>
11#include <gsl/gsl_math.h>
12#include <iostream>
13#include <type_traits>
14#include <utility>
15#include <vector>
16
17//! Type trait: true iff T is std::complex<U> for some U
18template <typename T>
19struct is_complex : std::false_type {};
20
21template <typename T>
22struct is_complex<std::complex<T>> : std::true_type {};
23
24//! Convenience variable template: is_complex_v<T> == is_complex<T>::value
25template <typename T>
26constexpr bool is_complex_v = is_complex<T>::value;
27
28//==============================================================================
29/*!
30 @brief Linear algebra: matrices, vectors, views, and solvers.
31 @details
32 Provides matrix and vector types, non-owning views, and a set of
33 linear algebra solvers wrapping LAPACK and GSL.
34
35 ### Core types:
36 - @ref LinAlg::Matrix<T> : owning row-major matrix; supports real and complex
37 element types. Provides arithmetic, GSL interop, and element-wise access.
38 - @ref LinAlg::Vector<T> : owning 1D array; used for eigenvectors, right-hand sides,
39 and eigenvalue arrays.
40
41 ### Views:
42 - @ref LinAlg::View<T> : non-owning strided view over a 1D segment of an array.
43 Used to provide row and column access into a Matrix without copying.
44 Obtained via @ref Matrix::row_view() and @ref Matrix::column_view()
45 - @ref LinAlg::Matrix_view<T> : non-owning view of a 2D subblock of a Matrix.
46
47 ### Solvers:
48 - @ref LinAlg::solve_Axeqb() : solves `Ax = b`.
49 - @ref LinAlg::symmhEigensystem() : generalised problem `Av = eBv` or `Av = ev`
50 Several overloads, and template for real/complex types.
51 - @ref LinAlg::genEigensystem() : general non-symmetric real matrix.
52*/
53namespace LinAlg {
54
55// Forward declaration (Matrix_view constructors accept Matrix<T> by reference)
56template <typename T>
57class Matrix;
58
59/*!
60 @brief Non-owning strided view onto a 1D segment of an array.
61 @details
62 Provides indexed read/write access into an existing array without ownership.
63 The stride allows views over non-contiguous memory (e.g., a column of a
64 row-major matrix).
65
66 Used by @ref Matrix::row_view() and @ref Matrix::column_view().
67*/
68template <typename T>
69class View {
70 std::size_t m_size;
71 std::size_t m_stride;
72 T *m_data;
73
74public:
75 //! Construct view over @p size elements starting at offset @p start, with @p stride
76 View(T *data, std::size_t start, std::size_t size, std::size_t stride)
77 : m_size(size), m_stride(stride), m_data(data + long(start)) {}
78
79 //! Number of elements in the view
80 std::size_t size() const { return m_size; }
81
82 //! [] index access (no range checking), mutable
83 T &operator[](std::size_t i) { return m_data[i * m_stride]; }
84 //! [] index access (no range checking), const
85 T operator[](std::size_t i) const { return m_data[i * m_stride]; }
86
87 //! at(i): element access with range checking, mutable
88 T &at(std::size_t i) {
89 assert(i < m_size);
90 return m_data[i * m_stride];
91 }
92 //! at(i): element access with range checking, const
93 T at(std::size_t i) const {
94 assert(i < m_size);
95 return m_data[i * m_stride];
96 }
97 //! () index access with range checking, mutable
98 T &operator()(std::size_t i) { return at(i); }
99 //! () index access with range checking, const
100 T operator()(std::size_t i) const { return at(i); }
101
102 //! Raw pointer to first viewed element
103 T *data() { return m_data; }
104};
105
106//==============================================================================
107/*!
108 @brief Non-owning 2D view onto a Matrix.
109 @details
110 Provides row/column element access to an existing matrix buffer without
111 ownership or resize capability. Supports both mutable (`Matrix_view<T>`)
112 and read-only (`Matrix_view<const T>`) access.
113
114 Implicitly constructible from `Matrix<T>` (mutable view) and from
115 `const Matrix<T>` (const view only).
116
117 @note Iterators (`begin()`/`end()`) yield raw pointers into the flat buffer.
118*/
119template <typename T>
121 std::size_t m_rows;
122 std::size_t m_cols;
123 T *m_data;
124
125public:
126 //! Construct from raw pointer and dimensions (no ownership)
127 Matrix_view(T *data, std::size_t rows, std::size_t cols)
128 : m_rows(rows), m_cols(cols), m_data(data) {}
129
130 //! Implicit conversion from mutable Matrix (works for both mutable and const view)
131 Matrix_view(Matrix<std::remove_const_t<T>> &m)
132 : m_rows(m.rows()), m_cols(m.cols()), m_data(m.data()) {}
133
134 //! Implicit conversion from const Matrix (only for Matrix_view<const T>)
135 template <typename U = T, typename = std::enable_if_t<std::is_const_v<U>>>
136 Matrix_view(const Matrix<std::remove_const_t<T>> &m)
137 : m_rows(m.rows()), m_cols(m.cols()), m_data(m.data()) {}
138
139 //! Return rows [major index size]
140 std::size_t rows() const { return m_rows; }
141 //! Return columns [minor index size]
142 std::size_t cols() const { return m_cols; }
143 //! Return rows*columns [total array size]
144 std::size_t size() const { return m_rows * m_cols; }
145 //! Bool - is empty? (same as size==0)
146 bool empty() const { return m_rows == 0 || m_cols == 0; }
147
148 //! Raw pointer to first element, mutable
149 T *data() { return m_data; }
150 //! Raw pointer to first element, const
151 const T *data() const { return m_data; }
152
153 //! [] index access (no range checking). [i] returns pointer to ith row, mutable
154 T *operator[](std::size_t i) { return m_data + i * m_cols; }
155 //! [] index access (no range checking). [i] returns const pointer to ith row
156 const T *operator[](std::size_t i) const { return m_data + i * m_cols; }
157
158 //! at(i,j): element access with range checking, mutable
159 T &at(std::size_t i, std::size_t j) {
160 assert(i < m_rows && j < m_cols);
161 return m_data[i * m_cols + j];
162 }
163 //! at(i,j): element access with range checking, const
164 T at(std::size_t i, std::size_t j) const {
165 assert(i < m_rows && j < m_cols);
166 return m_data[i * m_cols + j];
167 }
168 //! at(i,j): element access with range checking, const ref
169 const T &atc(std::size_t i, std::size_t j) const {
170 assert(i < m_rows && j < m_cols);
171 return m_data[i * m_cols + j];
172 }
173
174 //! () index access with range checking, mutable
175 T &operator()(std::size_t i, std::size_t j) { return at(i, j); }
176 //! () index access with range checking, const
177 T operator()(std::size_t i, std::size_t j) const { return at(i, j); }
178
179 //! Iterators over flat data buffer
180 auto begin() { return m_data; }
181 auto end() { return m_data + m_rows * m_cols; }
182 auto cbegin() const { return m_data; }
183 auto cend() const { return m_data + m_rows * m_cols; }
184};
185
186//==============================================================================
187/*!
188 @brief Row-major dense matrix with arithmetic and linear algebra support.
189 @details
190 Owns its data as a contiguous `std::vector<T>` stored in row-major order.
191 Supports scalar types `T = float`, `double`, `std::complex<float>`, and
192 `std::complex<double>`.
193
194 Provides:
195 - Element access via `operator[]` (no bounds checking) and `at()`/`operator()` (bounds-checked).
196 - Basic arithmetic: addition, subtraction, scalar multiply/divide, and matrix
197 multiplication via BLAS.
198 - Linear algebra via GSL: `determinant()`, `inverse()`, `transpose()`.
199 - Conversion utilities: `conj()`, `real()`, `imag()`, `complex()`.
200 - Non-owning views: `row_view()`, `column_view()`, `matrix_view()`.
201 - GSL interop: `as_gsl_view()`.
202
203 @note The scalar `+=` and `-=` operators treat the scalar as a multiple of
204 the identity: `M += a` adds `a` to all diagonal elements. Only valid
205 for square matrices.
206*/
207template <typename T = double>
208class Matrix {
209
210protected:
211 std::size_t m_rows;
212 std::size_t m_cols;
213 std::vector<T> m_data{};
214
215public:
216 //! Default initialiser
217 Matrix() : m_rows(0), m_cols(0) {}
218
219 //! Initialise a blank matrix rows*cols, filled with 0
220 Matrix(std::size_t rows, std::size_t cols)
221 : m_rows(rows), m_cols(cols), m_data(rows * cols) {}
222
223 //! Initialise a matrix rows*cols, filled with 'value'
224 Matrix(std::size_t rows, std::size_t cols, const T &value)
225 : m_rows(rows), m_cols(cols), m_data(rows * cols, value) {}
226
227 //! Initialise a blank square matrix dimension*dimension, filled with 0
228 // excplicit, since don't alow flot->int converions
229 explicit Matrix(std::size_t dimension)
230 : m_rows(dimension), m_cols(dimension), m_data(dimension * dimension) {}
231
232 //! Initialise a matrix from initialiser list. {{},{},{}}. Each row must be
233 //! same length
234 Matrix(std::initializer_list<std::initializer_list<T>> ll)
235 : m_rows(ll.size()), m_cols(ll.begin()->size()), m_data{} {
236 // way to avoid copy?
237 m_data.reserve(m_rows * m_cols);
238 for (auto &l : ll) {
239 m_data.insert(m_data.end(), l.begin(), l.end());
240 }
241 }
242
243 //! Initialise a matrix from single initialiser list. {...}.
244 Matrix(std::size_t rows, std::size_t cols, std::initializer_list<T> l)
245 : m_rows(rows), m_cols(cols), m_data{l} {
246 assert(m_data.size() == rows * cols &&
247 "initializer_list must be rows*cols");
248 }
249
250 //! Initialise a matrix from single initialiser list. {...}.
251 Matrix(std::size_t rows, std::size_t cols, std::vector<T> &&v)
252 : m_rows(rows), m_cols(cols), m_data{std::forward<std::vector<T>>(v)} {
253 assert(m_data.size() == rows * cols &&
254 "initializer_list must be rows*cols");
255 }
256 //! Initialise a matrix from single initialiser list. {...}.
257 Matrix(std::size_t rows, std::size_t cols, const std::vector<T> &v)
258 : m_rows(rows), m_cols(cols), m_data{v} {
259 assert(m_data.size() == rows * cols &&
260 "initializer_list must be rows*cols");
261 }
262
263 //============================================================================
264 //! Resizes matrix to new dimension; all values reset to default
265 void resize(std::size_t rows, std::size_t cols) {
266 m_rows = rows;
267 m_cols = cols;
268 m_data.assign(rows * cols, T{});
269 }
270
271 //! Resizes matrix to new dimension; all values reset to 'value'
272 void resize(std::size_t rows, std::size_t cols, const T &value) {
273 m_rows = rows;
274 m_cols = cols;
275 m_data.assign(rows * cols, value);
276 }
277
278 //============================================================================
279
280 //! Return rows [major index size]
281 std::size_t rows() const { return m_rows; }
282 //! Return columns [minor index size]
283 std::size_t cols() const { return m_cols; }
284 //! Return rows*columns [total array size]
285 std::size_t size() const { return m_data.size(); }
286
287 //! Bool - is empty? (same as size==0)
288 bool empty() const { return m_data.empty(); }
289
290 //! Pointer to first element; for std::complex<T> this is complex<T>*, not T*
291 T *data() { return m_data.data(); }
292 //! Const pointer to first element; for std::complex<T> this is const complex<T>*, not const T*
293 const T *data() const { return m_data.data(); }
294
295 //============================================================================
296
297 //! [] index access (no range checking). [i] returns pointer to ith row, mutable
298 T *operator[](std::size_t i) { return &(m_data[i * m_cols]); }
299 //! [] index access (no range checking). [i] returns const pointer to ith row
300 const T *operator[](std::size_t i) const { return &(m_data[i * m_cols]); }
301
302 //! at(i,j): element access with range checking, mutable
303 T &at(std::size_t row_i, std::size_t col_j) {
304 assert(row_i < m_rows && col_j < m_cols);
305 return m_data[row_i * m_cols + col_j];
306 }
307 //! at(i,j): element access with range checking, const
308 T at(std::size_t row_i, std::size_t col_j) const {
309 assert(row_i < m_rows && col_j < m_cols);
310 return m_data[row_i * m_cols + col_j];
311 }
312
313 //! at(i,j): element access with range checking, const ref
314 const T &atc(std::size_t row_i, std::size_t col_j) const {
315 assert(row_i < m_rows && col_j < m_cols);
316 return m_data[row_i * m_cols + col_j];
317 }
318
319 //! () index access with range checking, mutable
320 T &operator()(std::size_t i, std::size_t j) { return at(i, j); }
321 //! () index access with range checking, const
322 T operator()(std::size_t i, std::size_t j) const { return at(i, j); }
323
324 //============================================================================
325
326 //! iterators for underlying std::vector (entire data)
327 auto begin() { return m_data.begin(); }
328 auto cbegin() const { return m_data.cbegin(); }
329 auto end() { return m_data.end(); }
330 auto cend() const { return m_data.cend(); }
331
332 //! Returns raw c pointer to start of a row
333 // [[deprecated]]
334 const T *row(std::size_t row) const {
335 return m_data.data() + long(row * m_cols);
336 }
337
338 //! Returns a mutable 'View' of a row
339 [[nodiscard]] View<T> row_view(std::size_t row) {
340 return View<T>(this->data(), row * m_cols, m_cols, 1ul);
341 }
342 //! Returns an immutable 'View' of a row
343 [[nodiscard]] View<const T> row_view(std::size_t row) const {
344 return View<const T>(this->data(), row * m_cols, m_cols, 1ul);
345 }
346 //! Returns a mutable 'View' of a column
347 [[nodiscard]] View<T> column_view(std::size_t col) {
348 return View<T>(this->data(), col, m_rows, m_rows);
349 }
350 //! Returns an immutable 'View' of a column
351 [[nodiscard]] View<const T> column_view(std::size_t col) const {
352 return View<const T>(this->data(), col, m_rows, m_rows);
353 }
354
355 //! Returns a mutable view of the entire matrix (no ownership, no resize)
356 [[nodiscard]] Matrix_view<T> matrix_view() {
357 return Matrix_view<T>(m_data.data(), m_rows, m_cols);
358 }
359 //! Returns an immutable view of the entire matrix (no ownership, no resize)
360 [[nodiscard]] Matrix_view<const T> matrix_view() const {
361 return Matrix_view<const T>(m_data.data(), m_rows, m_cols);
362 }
363
364 //============================================================================
365 /*!
366 @brief Returns a GSL matrix view for use with GSL functions (no copy).
367 @details
368 Returns a `gsl_matrix_view` (or `_float_view`, `_complex_view`,
369 `_complex_float_view`) wrapping the matrix data in place. Access the
370 underlying GSL matrix via `.matrix` on the returned object. Allows
371 use of all GSL/BLAS built-in functions without any data copy.
372
373 Supported types: `double`, `float`, `std::complex<double>`,
374 `std::complex<float>`.
375
376 @note Both the `Matrix` and the returned view must remain in scope
377 during use; the view is non-owning.
378 @warning Fails at runtime (assert) for unsupported scalar types.
379 */
380 [[nodiscard]] auto as_gsl_view();
381
382 //! Const GSL matrix view; see as_gsl_view()
383 [[nodiscard]] auto as_gsl_view() const;
384
385 //============================================================================
386 // Basic matrix operations:
387 //============================================================================
388 //============================================================================
389
390 /*!
391 @brief Returns the determinant.
392 @details
393 The original matrix is not modified.
394
395 @return Determinant of the matrix.
396 @note Only available for `T = double` or `T = std::complex<double>`.
397 @warning Only defined for square matrices; asserts otherwise.
398 */
399 [[nodiscard]] T determinant() const;
400
401 /*!
402 @brief Inverts the matrix in place.
403
404 @return Reference to `*this` (the inverted matrix).
405 @note Only available for `T = double` or `T = std::complex<double>`.
406 @warning Only defined for square matrices; asserts otherwise.
407 */
409
410 /*!
411 @brief Returns inverse of the matrix; original is unchanged.
412 @details
413 Copies `*this` and calls `invert_in_place()` on the copy.
414
415 @note Only available for `T = double` or `T = std::complex<double>`.
416 */
417 [[nodiscard]] Matrix<T> inverse() const {
418 auto inv = *this;
419 return inv.invert_in_place();
420 }
421
422 /*!
423 @brief Returns the transpose of the matrix.
424 @details
425 Allocates and returns a new `Matrix<T>` of size `cols() x rows()` with
426 elements transposed. Uses GSL `gsl_matrix_transpose_memcpy` for `double`,
427 `float`, and their complex variants; falls back to a naive loop for other
428 types.
429
430 @return New matrix of dimensions `cols() x rows()`.
431 */
432 [[nodiscard]] Matrix<T> transpose() const;
433
434 //============================================================================
435
436 //! Constructs a diagonal unit matrix (identity), in place; only for square
438 //! Sets all elements to zero, in place
439 Matrix<T> &zero();
440 // //! M -> M + aI, for I=identity (adds a to diag elements), in place
441 // Matrix<T> &plusIdent(T a = T(1));
442 //============================================================================
443
444 //! Returns conjugate of matrix
445 [[nodiscard]] Matrix<T> conj() const;
446 //! Returns real part of complex matrix (changes type; returns a real matrix)
447 [[nodiscard]] auto real() const;
448 //! Returns imag part of complex matrix (changes type; returns a real matrix)
449 [[nodiscard]] auto imag() const;
450 //! Converts a real to complex matrix (changes type; returns a complex matrix)
451 [[nodiscard]] auto complex() const;
452
453 //! Conjugates matrix, in place
455
456 //============================================================================
457 /*!
458 @brief Elementwise multiply in place: M_ij *= a_ij
459 @details
460 Each element of `*this` is multiplied by the corresponding element of @p a.
461 Matrices must have the same dimensions.
462
463 @param a Matrix whose elements multiply `*this`.
464 @return Reference to `*this`.
465 @warning Dimensions must match; asserts otherwise.
466 */
468
469 /*!
470 @brief Returns new matrix C with C_ij = A_ij * B_ij (elementwise product).
471 @details Calls `a.mult_elements_by(b)` on a copy of @p a.
472 */
473 [[nodiscard]] friend Matrix<T> mult_elements(Matrix<T> a,
474 const Matrix<T> &b) {
475 return a.mult_elements_by(b);
476 }
477
478 //============================================================================
479 //! In-place elementwise addition; dimensions must match
480 Matrix<T> &operator+=(const Matrix<T> &rhs);
481 //! In-place elementwise subtraction; dimensions must match
482 Matrix<T> &operator-=(const Matrix<T> &rhs);
483 //! In-place scalar multiply: M_ij *= x
484 Matrix<T> &operator*=(const T x);
485 //! In-place scalar divide: M_ij /= x
486 Matrix<T> &operator/=(const T x);
487
488 //============================================================================
489 //! Elementwise addition: returns M + N
490 [[nodiscard]] friend Matrix<T> operator+(Matrix<T> lhs,
491 const Matrix<T> &rhs) {
492 return (lhs += rhs);
493 }
494 //! Elementwise subtraction: returns M - N
495 [[nodiscard]] friend Matrix<T> operator-(Matrix<T> lhs,
496 const Matrix<T> &rhs) {
497 return (lhs -= rhs);
498 }
499 //! Scalar multiply: returns x * M
500 [[nodiscard]] friend Matrix<T> operator*(const T x, Matrix<T> rhs) {
501 return (rhs *= x);
502 }
503 //! Scalar multiply: returns M * x
504 [[nodiscard]] friend Matrix<T> operator*(Matrix<T> lhs, const T x) {
505 return (lhs *= x);
506 }
507 //! Scalar divide: returns M / x
508 [[nodiscard]] friend Matrix<T> operator/(Matrix<T> lhs, const T x) {
509 return (lhs /= x);
510 }
511
512 //============================================================================
513 //! M += aI: adds a to diagonal elements (scalar treated as a*Identity)
514 Matrix<T> &operator+=(T aI);
515 //! M -= aI: subtracts a from diagonal elements (scalar treated as a*Identity)
516 Matrix<T> &operator-=(T aI);
517
518 //! Returns M + aI (adds a to diagonal)
519 [[nodiscard]] friend Matrix<T> operator+(Matrix<T> M, T aI) {
520 return (M += aI);
521 }
522 //! Returns M - aI (subtracts a from diagonal)
523 [[nodiscard]] friend Matrix<T> operator-(Matrix<T> M, T aI) {
524 return (M -= aI);
525 }
526
527 //============================================================================
528 //! Matrix multiplication: C_ij = sum_k A_ik*B_kj
529 template <typename U>
530 friend Matrix<U> operator*(const Matrix<U> &a, const Matrix<U> &b);
531
532 //! Prints matrix elements row by row to output stream
533 template <typename U>
534 friend std::ostream &operator<<(std::ostream &os, const Matrix<U> &a);
535};
536
537//==============================================================================
538//==============================================================================
539//==============================================================================
540
541//! Converts bool to CBLAS_TRANSPOSE enum (CblasTrans if true, CblasNoTrans if false)
542inline CBLAS_TRANSPOSE to_cblas_trans(bool trans) {
543 return trans ? CblasTrans : CblasNoTrans;
544}
545
546/*!
547 @brief Matrix multiplication C = op(A) * op(B) via CBLAS GEMM (row-major).
548 @details
549 Computes \f$ C = \mathrm{op}(A)\,\mathrm{op}(B) \f$ where
550 \f$ \mathrm{op}(X) = X \f$ or \f$ X^T \f$ depending on @p trans_A /
551 @p trans_B. @p c is overwritten with the result.
552
553 Wraps CBLAS `gemm` for `double`, `float`, `std::complex<double>`, and
554 `std::complex<float>`.
555
556 @param a Left-hand matrix.
557 @param b Right-hand matrix.
558 @param[out] c Output matrix; must be pre-allocated to the correct size.
559 @param trans_A If true, use \f$A^T\f$ in the product.
560 @param trans_B If true, use \f$B^T\f$ in the product.
561 @warning @p c must not alias @p a or @p b.
562*/
563template <typename T>
564void GEMM(const Matrix<T> &a, const Matrix<T> &b, Matrix<T> *c,
565 bool trans_A = false, bool trans_B = false);
566
567//------------------------------------------------------------------------------
568
569//! 5-matrix contraction for N*N matrices: M_ab = A_ai B_aj C_ij D_ib E_jb, with BLAS
570/*!
571 @details
572 All input matrices are assumed square with the same dimension N.
573 The contraction sums over indices i and j:
574 \f[
575 M_{ab} = \sum_{i,j} A_{ai}\,B_{aj}\,C_{ij}\,D_{ib}\,E_{jb}.
576 \f]
577
578 This implementation evaluates the contraction by looping over the
579 shared index i and using a BLAS GEMM for the inner j-summation:
580 \f[
581 X^{(i)}_{jb} = C_{ij}\,E_{jb}, \qquad
582 Y^{(i)}_{ab} = \sum_j B_{aj}\,X^{(i)}_{jb},
583 \f]
584 followed by the accumulation
585 \f[
586 M_{ab} \mathrel{+}= A_{ai}\,Y^{(i)}_{ab}\,D_{ib}.
587 \f]
588
589 Thus each i-iteration performs one dense matrix multiplication
590 (B * X^{(i)}) via BLAS, while the remaining operations are
591 elementwise scalings and accumulations.
592
593 The routine allocates temporary work matrices X and Y of size NN
594 (reused across i) and writes the result into @p M, which must be
595 preallocated and is overwritten on entry (i.e., initialised to zero).
596
597 @note
598 ~20% faster than Naiive implementation, BUT not easily parallelisable.
599
600 @tparam T Scalar type (float, double, std::complex<...>)
601 @param A,B,C,D,E Input NN matrices
602 @param[out] M Output NN matrix receiving the contraction. Must be previously sized
603*/
604template <typename T>
605void PENTA_GEMM(const Matrix<T> &A, const Matrix<T> &B, const Matrix<T> &C,
606 const Matrix<T> &D, const Matrix<T> &E, Matrix<T> *M);
607
608//! M_ab = A_ai B_aj C_ij D_ib E_jb. Assuming all square matrices. Uses blas.
609/*!
610 @details
611 Calls:
612 \ref PENTA_GEMM(const Matrix<T>&, const Matrix<T>&,
613 const Matrix<T>&, const Matrix<T>&,
614 const Matrix<T>&, Matrix<T>*).
615*/
616template <typename T>
617Matrix<T> PENTA_GEMM(const Matrix<T> &A, const Matrix<T> &B, const Matrix<T> &C,
618 const Matrix<T> &D, const Matrix<T> &E) {
619 Matrix<T> M(A.rows(), A.cols());
620 PENTA_GEMM<T>(A, B, C, D, E, &M);
621 return M;
622}
623
624//-----------
625
626//! 5-matrix contraction for N*N matrices: M_ab = A_ai B_aj C_ij D_ib E_jb, without BLAS, but with optional OpenMP parallelisation
627/*! @details
628 Computes the tensor contraction
629 \f[
630 M_{ab} = \sum_{i,j} A_{ai}\,B_{aj}\,C_{ij}\,D_{ib}\,E_{jb},
631 \f]
632 assuming all input matrices are square with the same dimension.
633
634 This implementation uses explicit nested loops (no BLAS).
635 If @tparam PARALLEL is true, OpenMP pragmas are enabled to
636 parallelise the outer loops.
637
638 For a BLAS-accelerated implementation, see
639 \ref PENTA_GEMM(const Matrix<T>&, const Matrix<T>&,
640 const Matrix<T>&, const Matrix<T>&,
641 const Matrix<T>&, Matrix<T>*).
642
643 Usually, this version will be significantly faster than the BLAS one.
644 However, if it iself is being called in a parallel region, the BLAS one will be faster.
645
646 @tparam T Scalar type: double, float, std::complex
647 @tparam PARALLEL Enable OpenMP parallelisation when true (compile-time)
648 @param A,B,C,D,E Input NN matrices
649 @param[out] M Output NN matrix receiving the contraction. Must be previously sized
650*/
651template <typename T, bool PARALLEL = false>
652void PENTA(const Matrix<T> &A, const Matrix<T> &B, const Matrix<T> &C,
653 const Matrix<T> &D, const Matrix<T> &E, Matrix<T> *M);
654
655//! 5-index contraction for N*N matrices: M_ab = A_ai B_aj C_ij D_ib E_jb, without BLAS, but with optional OpenMP parallelisation
656//! @details calls above
657template <typename T, bool PARALLEL = false>
658Matrix<T> PENTA(const Matrix<T> &A, const Matrix<T> &B, const Matrix<T> &C,
659 const Matrix<T> &D, const Matrix<T> &E) {
660 Matrix<T> M(A.rows(), A.cols());
661 PENTA<T, PARALLEL>(A, B, C, D, E, &M);
662 return M;
663}
664
665//==============================================================================
666//==============================================================================
667//==============================================================================
668
669//! Default relative tolerance for `equal()`: 1e-6 for float, 1e-12 for double
670template <typename T>
671constexpr auto myEps();
672
673/*!
674 @brief Compares two matrices element-wise to within a relative tolerance.
675 @details
676 Returns true iff all elements satisfy
677 \f[
678 |A_{ij} - B_{ij}| \leq |\varepsilon\,(A_{ij} + B_{ij})|.
679 \f]
680 Matrices of different dimensions compare as not equal.
681
682 @param lhs Left matrix.
683 @param rhs Right matrix.
684 @param eps Relative tolerance (default: `myEps<T>()`).
685 @return `true` if all elements agree within @p eps; `false` otherwise.
686*/
687template <typename T>
688bool equal(const Matrix<T> &lhs, const Matrix<T> &rhs, T eps = myEps<T>());
689
690} // namespace LinAlg
691
692#include "Matrix.ipp"
Non-owning 2D view onto a Matrix.
Definition Matrix.hpp:120
std::size_t size() const
Return rows*columns [total array size].
Definition Matrix.hpp:144
T operator()(std::size_t i, std::size_t j) const
() index access with range checking, const
Definition Matrix.hpp:177
T * data()
Raw pointer to first element, mutable.
Definition Matrix.hpp:149
auto begin()
Iterators over flat data buffer.
Definition Matrix.hpp:180
T * operator[](std::size_t i)
[] index access (no range checking). [i] returns pointer to ith row, mutable
Definition Matrix.hpp:154
const T * operator[](std::size_t i) const
[] index access (no range checking). [i] returns const pointer to ith row
Definition Matrix.hpp:156
const T & atc(std::size_t i, std::size_t j) const
at(i,j): element access with range checking, const ref
Definition Matrix.hpp:169
T & at(std::size_t i, std::size_t j)
at(i,j): element access with range checking, mutable
Definition Matrix.hpp:159
Matrix_view(T *data, std::size_t rows, std::size_t cols)
Construct from raw pointer and dimensions (no ownership)
Definition Matrix.hpp:127
T & operator()(std::size_t i, std::size_t j)
() index access with range checking, mutable
Definition Matrix.hpp:175
std::size_t rows() const
Return rows [major index size].
Definition Matrix.hpp:140
bool empty() const
Bool - is empty? (same as size==0)
Definition Matrix.hpp:146
Matrix_view(Matrix< std::remove_const_t< T > > &m)
Implicit conversion from mutable Matrix (works for both mutable and const view)
Definition Matrix.hpp:131
std::size_t cols() const
Return columns [minor index size].
Definition Matrix.hpp:142
T at(std::size_t i, std::size_t j) const
at(i,j): element access with range checking, const
Definition Matrix.hpp:164
const T * data() const
Raw pointer to first element, const.
Definition Matrix.hpp:151
Matrix_view(const Matrix< std::remove_const_t< T > > &m)
Implicit conversion from const Matrix (only for Matrix_view<const T>)
Definition Matrix.hpp:136
Row-major dense matrix with arithmetic and linear algebra support.
Definition Matrix.hpp:208
T at(std::size_t row_i, std::size_t col_j) const
at(i,j): element access with range checking, const
Definition Matrix.hpp:308
const T * row(std::size_t row) const
Returns raw c pointer to start of a row.
Definition Matrix.hpp:334
Matrix< T > & make_identity()
Constructs a diagonal unit matrix (identity), in place; only for square.
Definition Matrix.ipp:126
Matrix< T > & invert_in_place()
Inverts the matrix in place.
Definition Matrix.ipp:54
const T & atc(std::size_t row_i, std::size_t col_j) const
at(i,j): element access with range checking, const ref
Definition Matrix.hpp:314
friend Matrix< T > operator/(Matrix< T > lhs, const T x)
Scalar divide: returns M / x.
Definition Matrix.hpp:508
Matrix(std::size_t rows, std::size_t cols, const std::vector< T > &v)
Initialise a matrix from single initialiser list. {...}.
Definition Matrix.hpp:257
std::size_t size() const
Return rows*columns [total array size].
Definition Matrix.hpp:285
const T * data() const
Const pointer to first element; for std::complex<T> this is const complex<T>*, not const T*.
Definition Matrix.hpp:293
friend std::ostream & operator<<(std::ostream &os, const Matrix< U > &a)
Prints matrix elements row by row to output stream.
Matrix_view< const T > matrix_view() const
Returns an immutable view of the entire matrix (no ownership, no resize)
Definition Matrix.hpp:360
void resize(std::size_t rows, std::size_t cols, const T &value)
Resizes matrix to new dimension; all values reset to 'value'.
Definition Matrix.hpp:272
friend Matrix< T > operator+(Matrix< T > lhs, const Matrix< T > &rhs)
Elementwise addition: returns M + N.
Definition Matrix.hpp:490
T * data()
Pointer to first element; for std::complex<T> this is complex<T>*, not T*.
Definition Matrix.hpp:291
std::size_t rows() const
Return rows [major index size].
Definition Matrix.hpp:281
friend Matrix< T > operator+(Matrix< T > M, T aI)
Returns M + aI (adds a to diagonal)
Definition Matrix.hpp:519
friend Matrix< U > operator*(const Matrix< U > &a, const Matrix< U > &b)
Matrix multiplication: C_ij = sum_k A_ik*B_kj.
Matrix< T > conj() const
Returns conjugate of matrix.
Definition Matrix.ipp:146
View< T > row_view(std::size_t row)
Returns a mutable 'View' of a row.
Definition Matrix.hpp:339
Matrix< T > & operator+=(const Matrix< T > &rhs)
In-place elementwise addition; dimensions must match.
Definition Matrix.ipp:201
friend Matrix< T > operator*(const T x, Matrix< T > rhs)
Scalar multiply: returns x * M.
Definition Matrix.hpp:500
auto begin()
iterators for underlying std::vector (entire data)
Definition Matrix.hpp:327
T & at(std::size_t row_i, std::size_t col_j)
at(i,j): element access with range checking, mutable
Definition Matrix.hpp:303
auto complex() const
Converts a real to complex matrix (changes type; returns a complex matrix)
Definition Matrix.ipp:188
auto imag() const
Returns imag part of complex matrix (changes type; returns a real matrix)
Definition Matrix.ipp:177
auto as_gsl_view()
Returns a GSL matrix view for use with GSL functions (no copy).
Definition Matrix.ipp:434
Matrix< T > & conj_in_place()
Conjugates matrix, in place.
Definition Matrix.ipp:157
T * operator[](std::size_t i)
[] index access (no range checking). [i] returns pointer to ith row, mutable
Definition Matrix.hpp:298
Matrix(std::size_t dimension)
Initialise a blank square matrix dimension*dimension, filled with 0.
Definition Matrix.hpp:229
Matrix()
Default initialiser.
Definition Matrix.hpp:217
Matrix(std::size_t rows, std::size_t cols, std::initializer_list< T > l)
Initialise a matrix from single initialiser list. {...}.
Definition Matrix.hpp:244
friend Matrix< T > operator*(Matrix< T > lhs, const T x)
Scalar multiply: returns M * x.
Definition Matrix.hpp:504
T & operator()(std::size_t i, std::size_t j)
() index access with range checking, mutable
Definition Matrix.hpp:320
Matrix< T > & operator/=(const T x)
In-place scalar divide: M_ij /= x.
Definition Matrix.ipp:234
Matrix< T > transpose() const
Returns the transpose of the matrix.
Definition Matrix.ipp:94
Matrix< T > & zero()
Sets all elements to zero, in place.
Definition Matrix.ipp:137
Matrix< T > inverse() const
Returns inverse of the matrix; original is unchanged.
Definition Matrix.hpp:417
View< const T > row_view(std::size_t row) const
Returns an immutable 'View' of a row.
Definition Matrix.hpp:343
Matrix< T > & operator*=(const T x)
In-place scalar multiply: M_ij *= x.
Definition Matrix.ipp:224
bool empty() const
Bool - is empty? (same as size==0)
Definition Matrix.hpp:288
friend Matrix< T > operator-(Matrix< T > lhs, const Matrix< T > &rhs)
Elementwise subtraction: returns M - N.
Definition Matrix.hpp:495
std::size_t cols() const
Return columns [minor index size].
Definition Matrix.hpp:283
View< T > column_view(std::size_t col)
Returns a mutable 'View' of a column.
Definition Matrix.hpp:347
T determinant() const
Returns the determinant.
Definition Matrix.ipp:23
Matrix< T > & mult_elements_by(const Matrix< T > &a)
Elementwise multiply in place: M_ij *= a_ij.
Definition Matrix.ipp:268
const T * operator[](std::size_t i) const
[] index access (no range checking). [i] returns const pointer to ith row
Definition Matrix.hpp:300
friend Matrix< T > operator-(Matrix< T > M, T aI)
Returns M - aI (subtracts a from diagonal)
Definition Matrix.hpp:523
auto real() const
Returns real part of complex matrix (changes type; returns a real matrix)
Definition Matrix.ipp:166
Matrix(std::initializer_list< std::initializer_list< T > > ll)
Initialise a matrix from initialiser list. {{},{},{}}. Each row must be same length.
Definition Matrix.hpp:234
Matrix_view< T > matrix_view()
Returns a mutable view of the entire matrix (no ownership, no resize)
Definition Matrix.hpp:356
Matrix(std::size_t rows, std::size_t cols, std::vector< T > &&v)
Initialise a matrix from single initialiser list. {...}.
Definition Matrix.hpp:251
T operator()(std::size_t i, std::size_t j) const
() index access with range checking, const
Definition Matrix.hpp:322
Matrix(std::size_t rows, std::size_t cols)
Initialise a blank matrix rows*cols, filled with 0.
Definition Matrix.hpp:220
friend Matrix< T > mult_elements(Matrix< T > a, const Matrix< T > &b)
Returns new matrix C with C_ij = A_ij * B_ij (elementwise product).
Definition Matrix.hpp:473
Matrix(std::size_t rows, std::size_t cols, const T &value)
Initialise a matrix rows*cols, filled with 'value'.
Definition Matrix.hpp:224
void resize(std::size_t rows, std::size_t cols)
Resizes matrix to new dimension; all values reset to default.
Definition Matrix.hpp:265
View< const T > column_view(std::size_t col) const
Returns an immutable 'View' of a column.
Definition Matrix.hpp:351
Matrix< T > & operator-=(const Matrix< T > &rhs)
In-place elementwise subtraction; dimensions must match.
Definition Matrix.ipp:212
Non-owning strided view onto a 1D segment of an array.
Definition Matrix.hpp:69
T operator[](std::size_t i) const
[] index access (no range checking), const
Definition Matrix.hpp:85
T * data()
Raw pointer to first viewed element.
Definition Matrix.hpp:103
T operator()(std::size_t i) const
() index access with range checking, const
Definition Matrix.hpp:100
View(T *data, std::size_t start, std::size_t size, std::size_t stride)
Construct view over size elements starting at offset start, with stride.
Definition Matrix.hpp:76
T & operator[](std::size_t i)
[] index access (no range checking), mutable
Definition Matrix.hpp:83
std::size_t size() const
Number of elements in the view.
Definition Matrix.hpp:80
T at(std::size_t i) const
at(i): element access with range checking, const
Definition Matrix.hpp:93
T & at(std::size_t i)
at(i): element access with range checking, mutable
Definition Matrix.hpp:88
T & operator()(std::size_t i)
() index access with range checking, mutable
Definition Matrix.hpp:98
constexpr bool is_complex_v
Type trait: true if T is std::complex<U> for some U, false otherwise.
Definition AdamsMoulton.hpp:80
Linear algebra: matrices, vectors, views, and solvers.
Definition Matrix.hpp:53
void PENTA(const Matrix< T > &A, const Matrix< T > &B, const Matrix< T > &C, const Matrix< T > &D, const Matrix< T > &E, Matrix< T > *M)
5-matrix contraction for N*N matrices: M_ab = A_ai B_aj C_ij D_ib E_jb, without BLAS,...
Definition Matrix.ipp:386
void PENTA_GEMM(const Matrix< T > &A, const Matrix< T > &B, const Matrix< T > &C, const Matrix< T > &D, const Matrix< T > &E, Matrix< T > *M)
5-matrix contraction for N*N matrices: M_ab = A_ai B_aj C_ij D_ib E_jb, with BLAS
Definition Matrix.ipp:352
void GEMM(const Matrix< T > &a, const Matrix< T > &b, Matrix< T > *c, bool trans_A=false, bool trans_B=false)
Matrix multiplication C = op(A) * op(B) via CBLAS GEMM (row-major).
Definition Matrix.ipp:292
CBLAS_TRANSPOSE to_cblas_trans(bool trans)
Converts bool to CBLAS_TRANSPOSE enum (CblasTrans if true, CblasNoTrans if false)
Definition Matrix.hpp:542
constexpr auto myEps()
Default relative tolerance for equal(): 1e-6 for float, 1e-12 for double.
Definition Matrix.ipp:490
bool equal(const Matrix< T > &lhs, const Matrix< T > &rhs, T eps=myEps< T >())
Compares two matrices element-wise to within a relative tolerance.
Definition Matrix.ipp:505
Type trait: true iff T is std::complex<U> for some U.
Definition Matrix.hpp:19