ampsci
High-precision calculations for one- and two-valence atomic systems
Matrix.hpp
1#pragma once
2#include "qip/Vector.hpp" // for std::vector overloads
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
17template <typename T>
18struct is_complex : std::false_type {};
19template <typename T>
20struct is_complex<std::complex<T>> : std::true_type {};
21template <typename T>
22constexpr bool is_complex_v = is_complex<T>::value;
23
24//==============================================================================
25//! Defines Matrix, Vector classes, and linear some algebra functions
26namespace LinAlg {
27
28//! Proved a "view" onto an array
29template <typename T>
30class View;
31
32//==============================================================================
33//! Matrix class; row-major
34template <typename T = double>
35class Matrix {
36
37protected:
38 std::size_t m_rows;
39 std::size_t m_cols;
40 std::vector<T> m_data{};
41
42public:
43 //! Default initialiser
44 Matrix() : m_rows(0), m_cols(0) {}
45
46 //! Initialise a blank matrix rows*cols, filled with 0
47 Matrix(std::size_t rows, std::size_t cols)
48 : m_rows(rows), m_cols(cols), m_data(rows * cols) {}
49
50 //! Initialise a matrix rows*cols, filled with 'value'
51 Matrix(std::size_t rows, std::size_t cols, const T &value)
52 : m_rows(rows), m_cols(cols), m_data(rows * cols, value) {}
53
54 //! Initialise a blank square matrix dimension*dimension, filled with 0
55 // excplicit, since don't alow flot->int converions
56 explicit Matrix(std::size_t dimension)
57 : m_rows(dimension), m_cols(dimension), m_data(dimension * dimension) {}
58
59 //! Initialise a matrix from initialiser list. {{},{},{}}. Each row must be
60 //! same length
61 Matrix(std::initializer_list<std::initializer_list<T>> ll)
62 : m_rows(ll.size()), m_cols(ll.begin()->size()), m_data{} {
63 // way to avoid copy?
64 m_data.reserve(m_rows * m_cols);
65 for (auto &l : ll) {
66 m_data.insert(m_data.end(), l.begin(), l.end());
67 }
68 }
69
70 //! Initialise a matrix from single initialiser list. {...}.
71 Matrix(std::size_t rows, std::size_t cols, std::initializer_list<T> l)
72 : m_rows(rows), m_cols(cols), m_data{l} {
73 assert(m_data.size() == rows * cols &&
74 "initializer_list must be rows*cols");
75 }
76
77 //! Initialise a matrix from single initialiser list. {...}.
78 Matrix(std::size_t rows, std::size_t cols, std::vector<T> &&v)
79 : m_rows(rows), m_cols(cols), m_data{std::forward<std::vector<T>>(v)} {
80 assert(m_data.size() == rows * cols &&
81 "initializer_list must be rows*cols");
82 }
83 //! Initialise a matrix from single initialiser list. {...}.
84 Matrix(std::size_t rows, std::size_t cols, const std::vector<T> &v)
85 : m_rows(rows), m_cols(cols), m_data{v} {
86 assert(m_data.size() == rows * cols &&
87 "initializer_list must be rows*cols");
88 }
89
90 //============================================================================
91 //! Resizes matrix to new dimension; all values reset to default
92 void resize(std::size_t rows, std::size_t cols) {
93 m_rows = rows;
94 m_cols = cols;
95 m_data.assign(rows * cols, T{});
96 }
97
98 //! Resizes matrix to new dimension; all values reset to 'value'
99 void resize(std::size_t rows, std::size_t cols, const T &value) {
100 m_rows = rows;
101 m_cols = cols;
102 m_data.assign(rows * cols, value);
103 }
104
105 //============================================================================
106
107 //! Return rows [major index size]
108 std::size_t rows() const { return m_rows; }
109 //! Return columns [minor index size]
110 std::size_t cols() const { return m_cols; }
111 //! Return rows*columns [total array size]
112 std::size_t size() const { return m_data.size(); }
113
114 //! Returns pointer to first element. Note: for std::complex<T>, this is a
115 //! pointer to complex<T>, not T
116 T *data() { return m_data.data(); }
117 //! As above, but const
118 const T *data() const { return m_data.data(); }
119
120 //============================================================================
121
122 //! [] index access (with no range checking). [i][j] returns ith row, jth col
123 const T *operator[](std::size_t i) const { return &(m_data[i * m_cols]); }
124 //! As above, but const
125 T *operator[](std::size_t i) { return &(m_data[i * m_cols]); }
126
127 //! () index access (with range checking). (i,j) returns ith row, jth col
128 T &at(std::size_t row_i, std::size_t col_j) {
129 assert(row_i < m_rows && col_j < m_cols);
130 return m_data[row_i * m_cols + col_j];
131 }
132 //! const () index access (with range checking). (i,j) ith row, jth col
133 T at(std::size_t row_i, std::size_t col_j) const {
134 assert(row_i < m_rows && col_j < m_cols);
135 return m_data[row_i * m_cols + col_j];
136 }
137
138 //! const ref () index access (with range checking). (i,j) ith row, jth col
139 const T &atc(std::size_t row_i, std::size_t col_j) const {
140 assert(row_i < m_rows && col_j < m_cols);
141 return m_data[row_i * m_cols + col_j];
142 }
143
144 //! () index access (with range checking). (i,j) returns ith row, jth col
145 T &operator()(std::size_t i, std::size_t j) { return at(i, j); }
146 //! As above, but const
147 T operator()(std::size_t i, std::size_t j) const { return at(i, j); }
148
149 //============================================================================
150
151 //! iterators for underlying std::vector (entire data)
152 auto begin() { return m_data.begin(); }
153 auto cbegin() const { return m_data.cbegin(); }
154 auto end() { return m_data.end(); }
155 auto cend() const { return m_data.cend(); }
156
157 //! Returns raw c pointer to start of a row
158 // [[deprecated]]
159 const T *row(std::size_t row) const {
160 return m_data.data() + long(row * m_cols);
161 }
162
163 //! Returns a mutable 'View' of a row
164 [[nodiscard]] View<T> row_view(std::size_t row) {
165 return View<T>(this->data(), row * m_cols, m_cols, 1ul);
166 }
167 //! Returns an immutable 'View' of a row
168 [[nodiscard]] View<const T> row_view(std::size_t row) const {
169 return View<const T>(this->data(), row * m_cols, m_cols, 1ul);
170 }
171 //! Returns a mutable 'View' of a column
172 [[nodiscard]] View<T> column_view(std::size_t col) {
173 return View<T>(this->data(), col, m_rows, m_rows);
174 }
175 //! Returns an immutable 'View' of a column
176 [[nodiscard]] View<const T> column_view(std::size_t col) const {
177 return View<const T>(this->data(), col, m_rows, m_rows);
178 }
179
180 //============================================================================
181 //! Returns gsl_matrix_view (or _float_view, _complex_view,
182 //! _complex_float_view). Call .matrix to use as a GSL matrix (no copy is
183 //! involved). Allows one to use all GSL built-in functions. Note: non-owning
184 //! pointer - matrix AND gsl_view must remain in scope.
185 [[nodiscard]] auto as_gsl_view();
186
187 //! As above, but const
188 [[nodiscard]] auto as_gsl_view() const;
189
190 //============================================================================
191 // Basic matrix operations:
192 //============================================================================
193 //============================================================================
194
195 //! Returns the determinant. Uses GSL; via LU decomposition. Only works for
196 //! `double/complex<double>`
197 [[nodiscard]] T determinant() const;
198
199 //! Inverts the matrix, in place. Uses GSL; via LU decomposition. Only works
200 //! for `double/complex<double>.`
202
203 //! Returns inverse of the matrix. Leaves original matrix intact. Uses GSL;
204 //! via LU decomposition. Only works for `double/complex<double>`
205 [[nodiscard]] Matrix<T> inverse() const;
206 //! Returns transpose of matrix
207 [[nodiscard]] Matrix<T> transpose() const;
208
209 //============================================================================
210
211 //! Constructs a diagonal unit matrix (identity), in place; only for square
213 //! Sets all elements to zero, in place
214 Matrix<T> &zero();
215 // //! M -> M + aI, for I=identity (adds a to diag elements), in place
216 // Matrix<T> &plusIdent(T a = T(1));
217 //============================================================================
218
219 //! Returns conjugate of matrix
220 [[nodiscard]] Matrix<T> conj() const;
221 //! Returns real part of complex matrix (changes type; returns a real matrix)
222 [[nodiscard]] auto real() const;
223 //! Returns imag part of complex matrix (changes type; returns a real matrix)
224 [[nodiscard]] auto imag() const;
225 //! Converts a real to complex matrix (changes type; returns a complex matrix)
226 [[nodiscard]] auto complex() const;
227
228 //! Conjugates matrix, in place
230
231 //============================================================================
232 //! Muplitplies all the elements by those of matrix a, in place: M_ij *= a_ij
234
235 //! Returns new matrix, C_ij = A_ij*B_ij
236 [[nodiscard]] friend Matrix<T> mult_elements(Matrix<T> a,
237 const Matrix<T> &b) {
238 return a.mult_elements_by(b);
239 }
240
241 //============================================================================
242 // Operator overloads: +,-, scalar */
243 //! Overload standard operators: do what expected
244 Matrix<T> &operator+=(const Matrix<T> &rhs);
245 Matrix<T> &operator-=(const Matrix<T> &rhs);
246 Matrix<T> &operator*=(const T x);
247 Matrix<T> &operator/=(const T x);
248
249 //============================================================================
250 // nb: these are defined inline here to avoid ambiguous overload?
251 [[nodiscard]] friend Matrix<T> operator+(Matrix<T> lhs,
252 const Matrix<T> &rhs) {
253 return (lhs += rhs);
254 }
255 [[nodiscard]] friend Matrix<T> operator-(Matrix<T> lhs,
256 const Matrix<T> &rhs) {
257 return (lhs -= rhs);
258 }
259 [[nodiscard]] friend Matrix<T> operator*(const T x, Matrix<T> rhs) {
260 return (rhs *= x);
261 }
262 [[nodiscard]] friend Matrix<T> operator*(Matrix<T> lhs, const T x) {
263 return (lhs *= x);
264 }
265 [[nodiscard]] friend Matrix<T> operator/(Matrix<T> lhs, const T x) {
266 return (lhs /= x);
267 }
268
269 //============================================================================
270 //! Matrix<T> += T : T assumed to be *Identity!
271 Matrix<T> &operator+=(T aI);
272 //! Matrix<T> -= T : T assumed to be *Identity!
273 Matrix<T> &operator-=(T aI);
274
275 [[nodiscard]] friend Matrix<T> operator+(Matrix<T> M, T aI) {
276 return (M += aI);
277 }
278 [[nodiscard]] friend Matrix<T> operator-(Matrix<T> M, T aI) {
279 return (M -= aI);
280 }
281
282 //============================================================================
283 //! Matrix multiplication: C_ij = sum_k A_ik*B_kj
284 template <typename U>
285 friend Matrix<U> operator*(const Matrix<U> &a, const Matrix<U> &b);
286
287 //============================================================================
288 template <typename U>
289 friend std::ostream &operator<<(std::ostream &os, const Matrix<U> &a);
290};
291
292//==============================================================================
293//==============================================================================
294//==============================================================================
295
296//! Matrix multiplication C = A*B. C is overwritten with product, and must be correct size already
297/*! @details Wrapper for BLAS gemm functions:
298*/
299template <typename T>
300void GEMM(const Matrix<T> &a, const Matrix<T> &b, Matrix<T> *c,
301 bool trans_A = false, bool trans_B = false);
302
303//------------------------------------------------------------------------------
304
305//! 5-matrix contraction for N*N matrices: M_ab = A_ai B_aj C_ij D_ib E_jb, with BLAS
306/*!
307 @details
308 All input matrices are assumed square with the same dimension N.
309 The contraction sums over indices i and j:
310 \f[
311 M_{ab} = \sum_{i,j} A_{ai}\,B_{aj}\,C_{ij}\,D_{ib}\,E_{jb}.
312 \f]
313
314 This implementation evaluates the contraction by looping over the
315 shared index i and using a BLAS GEMM for the inner j-summation:
316 \f[
317 X^{(i)}_{jb} = C_{ij}\,E_{jb}, \qquad
318 Y^{(i)}_{ab} = \sum_j B_{aj}\,X^{(i)}_{jb},
319 \f]
320 followed by the accumulation
321 \f[
322 M_{ab} \mathrel{+}= A_{ai}\,Y^{(i)}_{ab}\,D_{ib}.
323 \f]
324
325 Thus each i-iteration performs one dense matrix multiplication
326 (B * X^{(i)}) via BLAS, while the remaining operations are
327 elementwise scalings and accumulations.
328
329 The routine allocates temporary work matrices X and Y of size NN
330 (reused across i) and writes the result into @p M, which must be
331 preallocated and is overwritten on entry (i.e., initialised to zero).
332
333 @note
334 ~20% faster than Naiive implementation, BUT not easily parallelisable.
335
336 @tparam T Scalar type (float, double, std::complex<...>)
337 @param A,B,C,D,E Input NN matrices
338 @param[out] M Output NN matrix receiving the contraction. Must be previously sized
339*/
340template <typename T>
341void PENTA_GEMM(const Matrix<T> &A, const Matrix<T> &B, const Matrix<T> &C,
342 const Matrix<T> &D, const Matrix<T> &E, Matrix<T> *M);
343
344//! M_ab = A_ai B_aj C_ij D_ib E_jb. Assuming all square matrices. Uses blas.
345/*!
346 @details
347 Calls:
348 \ref PENTA_GEMM(const Matrix<T>&, const Matrix<T>&,
349 const Matrix<T>&, const Matrix<T>&,
350 const Matrix<T>&, Matrix<T>*).
351*/
352template <typename T>
353Matrix<T> PENTA_GEMM(const Matrix<T> &A, const Matrix<T> &B, const Matrix<T> &C,
354 const Matrix<T> &D, const Matrix<T> &E) {
355 Matrix<T> M(A.rows(), A.cols());
356 PENTA_GEMM<T>(A, B, C, D, E, &M);
357 return M;
358}
359
360//-----------
361
362//! 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
363/*! @details
364 Computes the tensor contraction
365 \f[
366 M_{ab} = \sum_{i,j} A_{ai}\,B_{aj}\,C_{ij}\,D_{ib}\,E_{jb},
367 \f]
368 assuming all input matrices are square with the same dimension.
369
370 This implementation uses explicit nested loops (no BLAS).
371 If @tparam PARALLEL is true, OpenMP pragmas are enabled to
372 parallelise the outer loops.
373
374 For a BLAS-accelerated implementation, see
375 \ref PENTA_GEMM(const Matrix<T>&, const Matrix<T>&,
376 const Matrix<T>&, const Matrix<T>&,
377 const Matrix<T>&, Matrix<T>*).
378
379 Usually, this version will be significantly faster than the BLAS one.
380 However, if it iself is being called in a parallel region, the BLAS one will be faster.
381
382 @tparam T Scalar type: double, float, std::complex
383 @tparam PARALLEL Enable OpenMP parallelisation when true (compile-time)
384 @param A,B,C,D,E Input NN matrices
385 @param[out] M Output NN matrix receiving the contraction. Must be previously sized
386*/
387template <typename T, bool PARALLEL = false>
388void PENTA(const Matrix<T> &A, const Matrix<T> &B, const Matrix<T> &C,
389 const Matrix<T> &D, const Matrix<T> &E, Matrix<T> *M);
390
391//! 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
392//! @details calls above
393template <typename T, bool PARALLEL = false>
394Matrix<T> PENTA(const Matrix<T> &A, const Matrix<T> &B, const Matrix<T> &C,
395 const Matrix<T> &D, const Matrix<T> &E) {
396 Matrix<T> M(A.rows(), A.cols());
397 PENTA<T, PARALLEL>(A, B, C, D, E, &M);
398 return M;
399}
400
401//==============================================================================
402//==============================================================================
403//==============================================================================
404
405template <typename T>
406constexpr auto myEps();
407//! Compares two matrices; returns true iff all elements compare relatively to
408//! better than eps
409template <typename T>
410bool equal(const Matrix<T> &lhs, const Matrix<T> &rhs, T eps = myEps<T>());
411
412} // namespace LinAlg
413
414#include "Matrix.ipp"
Matrix class; row-major.
Definition Matrix.hpp:35
T at(std::size_t row_i, std::size_t col_j) const
const () index access (with range checking). (i,j) ith row, jth col
Definition Matrix.hpp:133
const T * row(std::size_t row) const
Returns raw c pointer to start of a row.
Definition Matrix.hpp:159
Matrix< T > & make_identity()
Constructs a diagonal unit matrix (identity), in place; only for square.
Definition Matrix.ipp:143
Matrix< T > & invert_in_place()
Inverts the matrix, in place. Uses GSL; via LU decomposition. Only works for double/complex<double>.
Definition Matrix.ipp:77
const T & atc(std::size_t row_i, std::size_t col_j) const
const ref () index access (with range checking). (i,j) ith row, jth col
Definition Matrix.hpp:139
Matrix(std::size_t rows, std::size_t cols, const std::vector< T > &v)
Initialise a matrix from single initialiser list. {...}.
Definition Matrix.hpp:84
std::size_t size() const
Return rows*columns [total array size].
Definition Matrix.hpp:112
const T * data() const
As above, but const.
Definition Matrix.hpp:118
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:99
T * data()
Returns pointer to first element. Note: for std::complex<T>, this is a pointer to complex<T>,...
Definition Matrix.hpp:116
std::size_t rows() const
Return rows [major index size].
Definition Matrix.hpp:108
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:163
View< T > row_view(std::size_t row)
Returns a mutable 'View' of a row.
Definition Matrix.hpp:164
Matrix< T > & operator+=(const Matrix< T > &rhs)
Overload standard operators: do what expected.
Definition Matrix.ipp:218
auto begin()
iterators for underlying std::vector (entire data)
Definition Matrix.hpp:152
T & at(std::size_t row_i, std::size_t col_j)
() index access (with range checking). (i,j) returns ith row, jth col
Definition Matrix.hpp:128
auto complex() const
Converts a real to complex matrix (changes type; returns a complex matrix)
Definition Matrix.ipp:205
auto imag() const
Returns imag part of complex matrix (changes type; returns a real matrix)
Definition Matrix.ipp:194
auto as_gsl_view()
Returns gsl_matrix_view (or _float_view, _complex_view, _complex_float_view). Call ....
Definition Matrix.ipp:478
Matrix< T > & conj_in_place()
Conjugates matrix, in place.
Definition Matrix.ipp:174
T * operator[](std::size_t i)
As above, but const.
Definition Matrix.hpp:125
Matrix(std::size_t dimension)
Initialise a blank square matrix dimension*dimension, filled with 0.
Definition Matrix.hpp:56
Matrix()
Default initialiser.
Definition Matrix.hpp:44
Matrix(std::size_t rows, std::size_t cols, std::initializer_list< T > l)
Initialise a matrix from single initialiser list. {...}.
Definition Matrix.hpp:71
T & operator()(std::size_t i, std::size_t j)
() index access (with range checking). (i,j) returns ith row, jth col
Definition Matrix.hpp:145
Matrix< T > transpose() const
Returns transpose of matrix.
Definition Matrix.ipp:111
Matrix< T > & zero()
Sets all elements to zero, in place.
Definition Matrix.ipp:154
Matrix< T > inverse() const
Returns inverse of the matrix. Leaves original matrix intact. Uses GSL; via LU decomposition....
Definition Matrix.ipp:104
View< const T > row_view(std::size_t row) const
Returns an immutable 'View' of a row.
Definition Matrix.hpp:168
std::size_t cols() const
Return columns [minor index size].
Definition Matrix.hpp:110
View< T > column_view(std::size_t col)
Returns a mutable 'View' of a column.
Definition Matrix.hpp:172
T determinant() const
Returns the determinant. Uses GSL; via LU decomposition. Only works for double/complex<double>
Definition Matrix.ipp:49
Matrix< T > & mult_elements_by(const Matrix< T > &a)
Muplitplies all the elements by those of matrix a, in place: M_ij *= a_ij.
Definition Matrix.ipp:270
const T * operator[](std::size_t i) const
[] index access (with no range checking). [i][j] returns ith row, jth col
Definition Matrix.hpp:123
auto real() const
Returns real part of complex matrix (changes type; returns a real matrix)
Definition Matrix.ipp:183
Matrix(std::initializer_list< std::initializer_list< T > > ll)
Initialise a matrix from initialiser list. {{},{},{}}. Each row must be same length.
Definition Matrix.hpp:61
Matrix(std::size_t rows, std::size_t cols, std::vector< T > &&v)
Initialise a matrix from single initialiser list. {...}.
Definition Matrix.hpp:78
T operator()(std::size_t i, std::size_t j) const
As above, but const.
Definition Matrix.hpp:147
Matrix(std::size_t rows, std::size_t cols)
Initialise a blank matrix rows*cols, filled with 0.
Definition Matrix.hpp:47
friend Matrix< T > mult_elements(Matrix< T > a, const Matrix< T > &b)
Returns new matrix, C_ij = A_ij*B_ij.
Definition Matrix.hpp:236
Matrix(std::size_t rows, std::size_t cols, const T &value)
Initialise a matrix rows*cols, filled with 'value'.
Definition Matrix.hpp:51
void resize(std::size_t rows, std::size_t cols)
Resizes matrix to new dimension; all values reset to default.
Definition Matrix.hpp:92
View< const T > column_view(std::size_t col) const
Returns an immutable 'View' of a column.
Definition Matrix.hpp:176
Proved a "view" onto an array.
Definition Matrix.ipp:7
constexpr bool is_complex_v
User-defined type-trait: Checks whether T is a std::complex type.
Definition AdamsMoulton.hpp:109
Defines Matrix, Vector classes, and linear some algebra functions.
Definition Matrix.hpp:26
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:430
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:396
void GEMM(const Matrix< T > &a, const Matrix< T > &b, Matrix< T > *c, bool trans_A=false, bool trans_B=false)
Matrix multiplication C = A*B. C is overwritten with product, and must be correct size already.
Definition Matrix.ipp:336
bool equal(const Matrix< T > &lhs, const Matrix< T > &rhs, T eps=myEps< T >())
Compares two matrices; returns true iff all elements compare relatively to better than eps.
Definition Matrix.ipp:549