HILA
Loading...
Searching...
No Matches
matrix.h
Go to the documentation of this file.
1/**
2 * @file matrix.h
3 * @brief Definition of Matrix types
4 * @details This file contains base matrix type Matrix_t which defines all general matrix type
5 * operations Matrix types are Matrix, #Vector, #RowVector, #SquareMatrix of which Matrix is defined
6 * as a class and the rest are special cases of the Matrix class.
7 *
8 */
9
10#ifndef MATRIX_H_
11#define MATRIX_H_
12
13#include <type_traits>
14#include <sstream>
15#include "plumbing/defs.h"
16#include "datatypes/cmplx.h"
17
18// forward definitions of needed classes
19template <const int n, const int m, typename T, typename Mtype>
20class Matrix_t;
21
22template <const int n, const int m, typename T = double>
23class Array;
24
25template <int n, int m, typename T>
26class Matrix;
27
28template <int n, typename T>
29class DiagonalMatrix;
30
31/**
32 * @brief Vector is defined as 1-column Matrix
33 */
34template <int n, typename T>
36
37/**
38 * @brief RowVector is a 1-row Matrix
39 */
40template <int n, typename T>
42
43/**
44 * @brief Square matrix is defined as alias with special case of Matrix<n,n,T>
45 */
46template <int n, typename T>
48
49// template <const int n, const int m, typename T>
50// class DaggerMatrix;
51
52// Special case - m.column(), column of a matrix (not used now)
53// #include "matrix_column.h"
54
55
56/// @brief type to store the return combo of svd:
57/// {U, D, V} where U and V are nxn unitary / orthogonal,
58/// and D is real diagonal singular value matrices.
59/// @tparam M - type of input matrix
60template <typename M>
61struct svd_result {
62 static_assert(M::is_matrix() && M::rows() == M::columns(), "SVD only for square matrix");
63 M U;
64 DiagonalMatrix<M::size(), hila::arithmetic_type<M>> singularvalues;
65 M V;
66};
67
68/// @brief type to store the return value of eigen_hermitean():
69/// {E, U} where E is nxn DiagonalMatrix containing eigenvalues and
70/// U nxn unitary matrix, with eigenvector columns
71/// @tparam M - type of input matrix
72template <typename M>
74 static_assert(M::is_matrix() && M::rows() == M::columns(),
75 "Eigenvalues only for square matrix");
76 DiagonalMatrix<M::size(), hila::arithmetic_type<M>> eigenvalues;
77 M eigenvectors;
78};
79
80/**
81 * @brief The main \f$ n \times m \f$ matrix type template Matrix_t. This is a base class type for
82 * "useful" types which are derived from this.
83 *
84 * @details Uses curiously recurring template pattern (CRTP), where the last template parameter is
85 * the template itself
86 *
87 * Example: the Matrix type below is defined as
88 * @code{.cpp}
89 * template <int n, int m, typename T>
90 * class Matrix : public Matrix_t<n, m, T, Matrix<n, m, T>> { .. }
91 * @endcode
92 *
93 * This pattern is used because stupid c++ makes it complicated to write generic code, in this case
94 * derived functions to return derived type
95 *
96 * @tparam n Number of rows
97 * @tparam m Number of columns
98 * @tparam T Matrix element type
99 * @tparam Mtype Specific "Matrix" type for CRTP
100 */
101template <const int n, const int m, typename T, typename Mtype>
102class Matrix_t {
103
104 public:
105 /// The data as a one dimensional array
106 T c[n * m];
107
108 public:
110 "Matrix requires Complex or arithmetic type");
111
112 // std incantation for field types
113 using base_type = hila::arithmetic_type<T>;
114 using argument_type = T;
115
116 // help for templates, can use T::is_matrix()
117 // Not very useful outside template parameters
118 static constexpr bool is_matrix() {
119 return true;
120 }
121
122 /**
123 * @brief Returns true if Matrix is a vector
124 *
125 * @return true
126 * @return false
127 */
128 static constexpr bool is_vector() {
129 return (n == 1 || m == 1);
130 }
131
132 /**
133 * @brief Returns true if matrix is a square matrix
134 *
135 * @return true
136 * @return false
137 */
138 static constexpr bool is_square() {
139 return (n == m);
140 }
141
142 /// Define default constructors to ensure std::is_trivial
143 Matrix_t() = default;
144 ~Matrix_t() = default;
145 Matrix_t(const Matrix_t &v) = default;
146
147 // constructor from scalar -- keep it explicit! Not good for auto use
148 template <typename S, int nn = n, int mm = m,
149 std::enable_if_t<(hila::is_assignable<T &, S>::value && nn == mm), int> = 0>
150 explicit inline Matrix_t(const S rhs) {
151 for (int i = 0; i < n; i++)
152 for (int j = 0; j < n; j++) {
153 if (i == j)
154 e(i, j) = rhs;
155 else
156 e(i, j) = 0;
157 }
158 }
159
160 // Construct from a different type matrix
161 // template <typename S, typename MT,
162 // std::enable_if_t<hila::is_assignable<T &, S>::value, int> = 0>
163 // Matrix_t(const Matrix_t<n, m, S, MT> &rhs) out_only {
164 // for (int i = 0; i < n * m; i++) {
165 // c[i] = rhs.c[i];
166 // }
167 // }
168
169 // construct from 0
170 inline Matrix_t(const std::nullptr_t &z) {
171 for (int i = 0; i < n * m; i++) {
172 c[i] = 0;
173 }
174 }
175
176 // Construct matrix automatically from right-size initializer list
177 // This does not seem to be dangerous, so keep non-explicit
178 template <typename S, std::enable_if_t<hila::is_assignable<T &, S>::value, int> = 0>
179 inline Matrix_t(std::initializer_list<S> rhs) {
180 assert(rhs.size() == n * m &&
181 "Matrix/Vector initializer list size must match variable size");
182 int i = 0;
183 for (auto it = rhs.begin(); it != rhs.end(); it++, i++) {
184 c[i] = *it;
185 }
186 }
187
188 // cast to curious type
189 // #pragma hila loop_function
190 template <typename Tm = Mtype,
191 std::enable_if_t<!std::is_same<Tm, Matrix<n, m, T>>::value, int> = 0>
192 inline operator Mtype &() {
193 return *reinterpret_cast<Mtype *>(this);
194 }
195
196 // #pragma hila loop_function
197 template <typename Tm = Mtype,
198 std::enable_if_t<!std::is_same<Tm, Matrix<n, m, T>>::value, int> = 0>
199 inline operator const Mtype &() const {
200 return *reinterpret_cast<const Mtype *>(this);
201 }
202
203 // automatically cast to generic matrix
204 // #pragma hila loop_function
205 inline operator Matrix<n, m, T> &() {
206 return *reinterpret_cast<Matrix<n, m, T> *>(this);
207 }
208 // #pragma hila loop_function
209 inline operator const Matrix<n, m, T> &() const {
210 return *reinterpret_cast<const Matrix<n, m, T> *>(this);
211 }
212
213 /// Define constant methods rows(), columns() - may be useful in template code
214
215 /**
216 * @brief Returns row length
217 *
218 * @return constexpr int
219 */
220 static constexpr int rows() {
221 return n;
222 }
223 /**
224 * @brief Returns column length
225 *
226 * @return constexpr int
227 */
228 static constexpr int columns() {
229 return m;
230 }
231
232
233 /**
234 * @brief Returns size of #Vector or square Matrix
235 *
236 * @tparam q row size n
237 * @tparam p column size m
238 * @return constexpr int
239 */
240 // size for row vector
241 template <int q = n, int p = m, std::enable_if_t<q == 1, int> = 0>
242 static constexpr int size() {
243 return p;
244 }
245 // size for column vector
246 template <int q = n, int p = m, std::enable_if_t<p == 1, int> = 0>
247 static constexpr int size() {
248 return q;
249 }
250 // size for square matrix
251 template <int q = n, int p = m, std::enable_if_t<q == p, int> = 0>
252 static constexpr int size() {
253 return q;
254 }
255
256 /**
257 * @brief Standard array indexing operation for matrices
258 * @details Accessing singular elements is insufficient, but Matrix elements are often quite
259 * small.
260 *
261 * Exammple for matrix:
262 * \code
263 * Matrix<n,m,MyType> M;
264 * MyType a = M.e(i,j); \\ i <= n, j <= m
265 * \endcode
266 *
267 *
268 * @param i Row index
269 * @param j Column index
270 * @return T matrix element type
271 */
272 inline T e(const int i, const int j) const {
273 // return elem[i][j];
274 return c[i * m + j];
275 }
276
277 /// @internal const_function implementation. See const_function for details
278 inline T &e(const int i, const int j) const_function {
279 // return elem[i][j];
280 return c[i * m + j];
281 }
282 // declare single e here too in case we have a vector
283 // (n || m == 1)
284 /**
285 * @brief Standard array indexing operation for vectors
286 * @details Accessing singular elements is insufficient, but Vector elements are often quite
287 * small.
288 * \code {.cpp}
289 * Vector<n,MyType> V;
290 * MyType a = V.e(i) \\ i <= n
291 * \endcode
292 * @tparam q Number of rows
293 * @tparam p Number of columns
294 * @param i Index
295 * @return T
296 */
297 template <int q = n, int p = m, std::enable_if_t<(q == 1 || p == 1), int> = 0>
298 inline T e(const int i) const {
299 return c[i];
300 }
301
302 /// @internal const_function implementation. See const_function for details
303 template <int q = n, int p = m, std::enable_if_t<(q == 1 || p == 1), int> = 0>
304 inline T &e(const int i) const_function {
305 return c[i];
306 }
307
308 /**
309 * @brief Indexing operation [] defined only for vectors.
310 *
311 * @details Example:
312 *
313 * \code {.cpp}
314 * Vector<n,MyType> V;
315 * MyType a = V[i] \\ i <= n
316 * \endcode
317 *
318 * @tparam q row size n
319 * @tparam p column size m
320 * @param i row or vector index depending on which is being indexed
321 * @return T
322 */
323 template <int q = n, int p = m, std::enable_if_t<(q == 1 || p == 1), int> = 0>
324 inline T operator[](const int i) const {
325 return c[i];
326 }
327 /// @internal const_function implementation. See const_function for details
328 template <int q = n, int p = m, std::enable_if_t<(q == 1 || p == 1), int> = 0>
329 inline T &operator[](const int i) const_function {
330 return c[i];
331 }
332
333 // /// get row of a matrix
334 // RowVector<m, T> row(int r) const {
335 // RowVector<m, T> v;
336 // for (int i = 0; i < m; i++)
337 // v[i] = e(r, i);
338 // return v;
339 // }
340
341
342 /**
343 * @brief Return reference to row in a matrix
344 * @details Since the Matrix data is ordered in a row major order accessing a row returns a
345 * reference to the row.
346 *
347 * \code{.cpp}
348 * Matrix<n,n,T> M;
349 * M.random();
350 * RowVector<n,T> V = M.row(i);
351 * \endcode
352 *
353 * @param r index of row to be referenced
354 * @return const RowVector<m, T>&
355 */
356 const RowVector<m, T> &row(int r) const {
357 return *(reinterpret_cast<const RowVector<m, T> *>(this) + r);
358 }
359
360 /**
361 * @brief Set row of Matrix with #RowVector if types are assignable
362 * @details
363 * \code{.cpp}
364 * RowVector<n,T> V;
365 * V.random();
366 * Matrix<n,n,T> M;
367 * M.set_row(i,V);
368 * \endcode
369 * @tparam S RowVector type
370 * @param r Index of row to be set
371 * @param v RowVector to be set
372 */
373 template <typename S, std::enable_if_t<hila::is_assignable<T &, S>::value, int> = 0>
374 void set_row(int r, const RowVector<m, S> &v) {
375 for (int i = 0; i < m; i++)
376 e(r, i) = v[i];
377 }
378
379 /**
380 * @brief Returns column vector as value at index c
381 * \code{.cpp}
382 * Matrix<n,n,T> M;
383 * M.random();
384 * Vector<n,T> V = M.column(i);
385 * \endcode
386 * @param c index of column vector to be returned
387 * @return const Vector<n, T>
388 */
389 Vector<n, T> column(int c) const {
390 Vector<n, T> v;
391 for (int i = 0; i < n; i++)
392 v[i] = e(i, c);
393 return v;
394 }
395
396 /// get column of a matrix
397 // hila_matrix_column_t<n, T, Mtype> column(int c) {
398 // return hila_matrix_column_t<n, T, Mtype>(*this, c);
399 // }
400
401 /**
402 * @brief Set column of Matrix with #Vector if types are assignable
403 * @details
404 * \code{.cpp}
405 * Vector<n,T> V;
406 * V.random();
407 * Matrix<n,n,T> M;
408 * M.set_column(i,V);
409 * \endcode
410 * @tparam S Vector type
411 * @param c Index of column to be set
412 * @param v #Vector to be set
413 */
414 template <typename S, std::enable_if_t<hila::is_assignable<T &, S>::value, int> = 0>
415 void set_column(int c, const Vector<n, S> &v) {
416 for (int i = 0; i < n; i++)
417 e(i, c) = v[i];
418 }
419
420 /**
421 * @brief Return diagonal of square matrix
422 * @details If called for non square matrix the program will throw an error.
423 *
424 * \code{.cpp}
425 * Matrix<n,n,T> M = 1;
426 * DiagonalMatrix<n,T> D = M.diagonal();
427 * \endcode
428 * @return Vector<n, T> returned vector.
429 */
431 static_assert(n == m, "diagonal() method defined only for square matrices");
433 for (int i = 0; i < n; i++)
434 res.e(i) = (*this).e(i, i);
435 return res;
436 }
437
438 /**
439 * @brief Set diagonal of square matrix to #Vector which is passed to the method
440 * @details If called for non square matrix the program will throw an error.
441 *
442 * Example:
443 *
444 * \code {.cpp}
445 * SquareMatrix<n,MyType> S = 0; \\ Zero matrix
446 * Vector<n,MyType> V = 1; \\ Vector assigned to 1 at all elements
447 * S.set_diagonal(V); \\ Results in Identity matrix of size n
448 * \endcode
449 *
450 * @tparam S type vector to assign values to
451 * @param v Vector to assign to diagonal
452 */
453 template <typename S, std::enable_if_t<hila::is_assignable<T &, S>::value, int> = 0>
454 void set_diagonal(const Vector<n, S> &v) {
455 static_assert(n == m, "set_diagonal() method defined only for square matrices");
456 for (int i = 0; i < n; i++)
457 (*this).e(i, i) = v.e(i);
458 }
459
460
461 /**
462 * @brief Cast Matrix to Array
463 * @details used for array operations
464 *
465 * @return Array<n, m, T>&
466 */
467 const Array<n, m, T> &asArray() const {
468 return *reinterpret_cast<const Array<n, m, T> *>(this);
469 }
470 // Same as above but with const_function, see const_function for details
471 Array<n, m, T> &asArray() const_function {
472 return *reinterpret_cast<Array<n, m, T> *>(this);
473 }
474
475 /**
476 * @brief Cast Vector to DiagonalMatrix
477 *
478 * @return DiagonalMatrix<n,T>
479 */
480 template <int mm = m, std::enable_if_t<mm == 1, int> = 0>
482 return *reinterpret_cast<const DiagonalMatrix<n, T> *>(this);
483 }
484 // Same as above but with const_function, see const_function for details
485 template <int mm = m, std::enable_if_t<mm == 1, int> = 0>
486 DiagonalMatrix<n, T> &asDiagonalMatrix() const_function {
487 return *reinterpret_cast<DiagonalMatrix<n, T> *>(this);
488 }
489
490
491 // casting from one Matrix (number) type to another: do not do this automatically.
492 // but require an explicit cast operator. This makes it easier to write code.
493 // or should it be automatic? keep/remove explicit?
494 // TODO: CHECK AVX CONVERSIONS
495
496 // template <typename S, typename Rtype,
497 // std::enable_if_t<
498 // Rtype::is_matrix() && Rtype::rows() == n && Rtype::columns() == m
499 // &&
500 // hila::is_assignable<typename (Rtype::argument_type) &,
501 // T>::value,
502 // int> = 0>
503 // explicit operator Rtype() const {
504 // Rtype res;
505 // for (int i = 0; i < n * m; i++)
506 // res.c[i] = c[i];
507 // return res;
508 // }
509
510 /**
511 * @brief Unary operator
512 * @details
513 *
514 * Negation operation
515 *
516 * \code {.cpp}
517 * M == -M;
518 * \endcode
519 *
520 */
521 inline Mtype operator-() const {
522 Mtype res;
523 for (int i = 0; i < n * m; i++) {
524 res.c[i] = -c[i];
525 }
526 return res;
527 }
528
529 /**
530 * @brief Addition operator
531 * @memberof Matrix_t
532 * @details
533 *
534 * Identity operation
535 *
536 * \code {.cpp}
537 * M == +M;
538 * \endcode
539 *
540 * @tparam Mtype1 Matrix type for a
541 * @tparam Mtype2 Matrix type for b
542 * @param a Left matrix
543 * @param b Right matrix
544 * @return Rtype
545 */
546 inline const Mtype &operator+() const {
547 return *this;
548 }
549
550 /**
551 * @brief Boolean operator == to determine if two matrices are exactly the same
552 * @details Tolerance for equivalence is zero, meaning that the matrices must be exactly the
553 * same.
554 *
555 * @tparam S Type for Matrix which is being compared to
556 * @param rhs right hand side Matrix which we are comparing
557 * @return true
558 * @return false
559 */
560 template <typename S>
561 bool operator==(const Matrix<n, m, S> &rhs) const {
562 for (int i = 0; i < n; i++)
563 for (int j = 0; j < m; j++) {
564 if (e(i, j) != rhs.e(i, j))
565 return false;
566 }
567 return true;
568 }
569
570 /**
571 * @brief Boolean operator != to check if matrices are exactly different
572 * @details if matrices are exactly the same then this will return false
573 *
574 * @tparam S Type for MAtrix which is being compared to
575 * @param rhs right hand side Matrix which we are comparing
576 * @return true
577 * @return false
578 */
579 template <typename S>
580 bool operator!=(const Matrix<n, m, S> &rhs) const {
581 return !(*this == rhs);
582 }
583
584 /**
585 * @brief Copy matrix assignment
586 * @details
587 * \code {.cpp}
588 * Matrix<n,m,MyType> M_0;
589 * .
590 * . M_0 has values assigned to it
591 * .
592 * Matrix<n,m,MyType> M; \\ undefined matrix
593 * M = M_0; \\ Assignment from M_0
594 * \endcode
595 * @param rhs Matrix to assign
596 * @return
597 */
598 // #pragma hila loop_function
599 template <typename S, typename MT,
600 std::enable_if_t<hila::is_assignable<T &, S>::value, int> = 0>
601 inline Mtype &operator=(const Matrix_t<n, m, S, MT> &rhs) out_only {
602 for (int i = 0; i < n * m; i++) {
603 c[i] = rhs.c[i];
604 }
605 return *this;
606 }
607
608 // assign from 0
609 // #pragma hila loop_function
610 /**
611 * @brief Zero assignment
612 *
613 * \code {.cpp}
614 * Matrix<n,m,MyType> M;
615 * M = 0; Zero matrix;
616 * \endcode
617 *
618 * @param z 0
619 * @return Mtype&
620 */
621 inline Mtype &operator=(const std::nullptr_t &z) out_only {
622 for (int i = 0; i < n * m; i++) {
623 c[i] = 0;
624 }
625 return *this;
626 }
627
628 // Assign from "scalar" for square matrix
629 // #pragma hila loop_function
630 /**
631 * @brief Assignment from scalar
632 * @details Assigns the scalar to the diagonal elements as \f$ M = I\cdot a\f$
633 *
634 * \code {.cpp}
635 * MyType a = hila::random();
636 * Matrix<n,m,MyType> M;
637 * M = a; M = I*a
638 * \endcode
639
640 * @tparam S Scalar type to assign
641 * @param rhs Scalar to assign
642 * @return Mtype&
643 */
644 template <typename S, std::enable_if_t<hila::is_assignable<T &, S>::value && n == m, int> = 0>
645 inline Mtype &operator=(const S rhs) out_only {
646
647 for (int i = 0; i < n; i++)
648 for (int j = 0; j < n; j++) {
649 if (i == j)
650 e(i, j) = rhs;
651 else
652 e(i, j) = 0;
653 }
654 return *this;
655 }
656
657 // Assign from diagonal matrix
658 // #pragma hila loop_function
659
660 /**
661 * @brief Assignment from diagonal matrix
662 *
663 * @tparam S Element type of Diagonal matrix
664 * @param rhs Diagonal matrix to assign
665 * @return Mtype&
666 */
667 template <typename S, std::enable_if_t<hila::is_assignable<T &, S>::value, int> = 0>
668 inline Mtype &operator=(const DiagonalMatrix<n, S> &rhs) out_only {
669 static_assert(n == m,
670 "Assigning DiagonalMatrix to Matrix possible only for square matrices");
671
672 for (int i = 0; i < n; i++)
673 for (int j = 0; j < n; j++) {
674 if (i == j)
675 e(i, j) = rhs.e(i);
676 else
677 e(i, j) = 0;
678 }
679 return *this;
680 }
681
682 /**
683 * @brief Initializer list assignment
684 * @details
685 * \code{.cpp}
686 * Matrix<2,2,int> M ;
687 * M = {1, 0
688 * 0, 1};
689 * \endcode
690 * @tparam S Element type of initializer list
691 * @param rhs Initializer list to assign
692 * @return Mtype&
693 */
694 template <typename S, std::enable_if_t<hila::is_assignable<T &, S>::value, int> = 0>
695 inline Mtype &operator=(std::initializer_list<S> rhs) out_only {
696 assert(rhs.size() == n * m && "Initializer list has a wrong size in assignment");
697 int i = 0;
698 for (auto it = rhs.begin(); it != rhs.end(); it++, i++) {
699 c[i] = *it;
700 }
701 return *this;
702 }
703
704
705 /**
706 * @brief Add assign operator Matrix to Matrix
707 * @details
708 *
709 * \code {.cpp}
710 * Matrix<n,m,MyType> M,N;
711 * M = 1;
712 * N = 1;
713 * M += N; \\M = 2*I
714 * \endcode
715 * @tparam S Element type of rhs
716 * @tparam MT Matrix type of rhs
717 * @param rhs Matrix to add
718 * @return Mtype&
719 */
720 // #pragma hila loop_function
721 template <typename S, typename MT,
722 std::enable_if_t<hila::is_assignable<T &, S>::value, int> = 0>
724 for (int i = 0; i < n * m; i++) {
725 c[i] += rhs.c[i];
726 }
727 return *this;
728 }
729
730 /**
731 * @brief Subtract assign operator Matrix to MAtrix
732 * @details
733 *
734 * \code {.cpp}
735 * Matrix<n,m,MyType> M,N;
736 * M = 3;
737 * N = 1;
738 * M -= N; \\M = 2*I
739 * \endcode
740 *
741 * @param rhs Matrix to subtract with
742 * @return Mtype&
743 */
744 // #pragma hila loop_function
745 template <typename S, typename MT,
746 std::enable_if_t<hila::is_assignable<T &, S>::value, int> = 0>
748 for (int i = 0; i < n * m; i++) {
749 c[i] -= rhs.c[i];
750 }
751 return *this;
752 }
753
754 // add assign a scalar to square matrix
755 // #pragma hila loop_function
756 /**
757 * @brief Add assign operator scalar to Matrix
758 * @details
759 * Adds scalar \f$ a \f$ to __square__ matrix as \f$ M + a\cdot\mathbb{1} \f$
760 *
761 * \code {.cpp}
762 * Matrix<n,m,MyType> M = 1;
763 * M += 1 ; \\ M = 2*I
764 * \endcode
765 *
766 * @tparam S Element type of scalar rhs
767 * @param rhs Sclar to add
768 * @return Mtype&
769 */
770 template <typename S,
771 std::enable_if_t<hila::is_assignable<T &, hila::type_plus<T, S>>::value, int> = 0>
772 Mtype &operator+=(const S &rhs) {
773
774 static_assert(n == m, "rows != columns : scalar addition possible for square matrix only!");
775
776 for (int i = 0; i < n; i++) {
777 e(i, i) += rhs;
778 }
779 return *this;
780 }
781
782 // subtract assign type T and convertible
783 // #pragma hila loop_function
784 /**
785 * @brief Subtract assign operator scalar to Matrix
786 * @details Subtract scalar \f$ a \f$ to __square__ matrix as \f$ M - a\cdot\mathbb{1} \f$
787 *
788 * \code {.cpp}
789 * Matrix<n,m,MyType> M = 3;
790 * M -= 1 ; \\ M = 2*I
791 * \endcode
792 * @tparam S Element type of scalar rhs
793 * @param rhs scalar to add
794 * @return Mtype&
795 */
796 template <typename S,
797 std::enable_if_t<hila::is_assignable<T &, hila::type_minus<T, S>>::value, int> = 0>
798 Mtype &operator-=(const S rhs) {
799 static_assert(n == m,
800 "rows != columns : scalar subtraction possible for square matrix only!");
801 for (int i = 0; i < n; i++) {
802 e(i, i) -= rhs;
803 }
804 return *this;
805 }
806
807 /**
808 * @brief Multiply assign scalar or matrix
809 * @details Multiplication works as defined for matrix multiplication
810 *
811 * Matrix multiply assign only defined for square matrices, since the matrix dimensions would
812 * change otherwise.
813 *
814 * \code {.cpp}
815 * Matrix<n,m,MyType> M,N;
816 * .
817 * . Fill matrices M and N
818 * .
819 * M *= N; \\ M = M*N
820 * \endcode
821 *
822 * @param rhs Matrix to multiply with
823 * @return template <int p, typename S, typename MT,
824 * std::enable_if_t<hila::is_assignable<T &, hila::type_mul<T, S>>::value, int> = 0>&
825 */
826 // #pragma hila loop_function
827 template <int p, typename S, typename MT,
828 std::enable_if_t<hila::is_assignable<T &, hila::type_mul<T, S>>::value, int> = 0>
830 static_assert(m == p, "can't assign result of *= to lhs Matrix, because doing so "
831 "would change it's dimensions");
832 *this = *this * rhs;
833 return *this;
834 }
835
836 // multiply assign with scalar
837 // #pragma hila loop_function
838 /**
839 * @brief Multiply assign operator scalar to Matrix
840 * @details Multiply Matrix uniformly with scalar
841 *
842 * \code {.cpp}
843 * Matrix<n,m,MyType> M;
844 * .
845 * . Fill whole matrix with 1
846 * .
847 * M *= 2 ; \\ M is filled with 2
848 * \endcode
849 * @tparam S Scalar type of rhs
850 * @param rhs Scalar to multiply
851 * @return Mtype&
852 */
853 template <typename S,
854 std::enable_if_t<hila::is_assignable<T &, hila::type_mul<T, S>>::value, int> = 0>
855 Mtype &operator*=(const S rhs) {
856 for (int i = 0; i < n * m; i++) {
857 c[i] *= rhs;
858 }
859 return *this;
860 }
861
862 /**
863 * @brief Divide assign oprator scalar with matrix
864 * @details Divide works as defined for scalar matrix division.
865 *
866 *
867 * \code {.cpp}
868 * Matrix<n,m,MyType> M;
869 * .
870 * . Fill whole matrix with 2
871 * .
872 * M /= 2 ; \\ M is filled with 1
873 * \endcode
874 * @tparam S Scalar type of rhs
875 * @param rhs Scalar to divide with
876 * @return
877 */
878 // #pragma hila loop_function
879 template <typename S,
880 std::enable_if_t<hila::is_assignable<T &, hila::type_div<T, S>>::value, int> = 0>
881 Mtype &operator/=(const S rhs) {
882 for (int i = 0; i < n * m; i++) {
883 c[i] /= rhs;
884 }
885 return *this;
886 }
887
888 /**
889 * @brief Addition assign operator for DiagonalMatrix to Matrix
890 * @details Works as long as Matrix to assign to is square
891 * @tparam S Element type of rhs
892 * @param rhs DiagonalMatrix to add
893 * @return Mtype&
894 */
895 template <typename S,
896 std::enable_if_t<hila::is_assignable<T &, hila::type_plus<T, S>>::value, int> = 0>
898 static_assert(n == m, "Assigning DiagonalMatrix possible only for square matrix");
899
900 for (int i = 0; i < n; i++)
901 e(i, i) += rhs.e(i);
902 return *this;
903 }
904
905 /**
906 * @brief Subtract assign operator for DiagonalMatrix to Matrix
907 * @details Works as long as Matrix to assign to is square
908 * @tparam S Element type of rhs
909 * @param rhs DiagonalMatrix to add
910 * @return Mtype&
911 */
912 template <typename S,
913 std::enable_if_t<hila::is_assignable<T &, hila::type_plus<T, S>>::value, int> = 0>
915 static_assert(n == m, "Assigning DiagonalMatrix possible only for square matrix");
916
917 for (int i = 0; i < n; i++)
918 e(i, i) -= rhs.e(i);
919 return *this;
920 }
921
922 /**
923 * @brief Multiply assign operator for DiagonalMatrix to Matrix
924 * @details Simply defined as matrix multiplication, but since rhs is guaranteed to be diagonal
925 * the method is optimized to skip most of the elements.
926 *
927 * @tparam S Element type of rhs
928 * @param rhs DiagonalMatrix to multiply
929 * @return Mtype&
930 */
931 template <typename S,
932 std::enable_if_t<hila::is_assignable<T &, hila::type_mul<T, S>>::value, int> = 0>
934
935 for (int i = 0; i < n; i++)
936 for (int j = 0; j < m; j++)
937 e(i, j) *= rhs.e(j);
938
939 return *this;
940 }
941
942 /**
943 * @brief Divide assign operator for DiagonalMatrix to Matrix
944 * @details Well defined since rhs is guaranteed to be Diagonal.
945 *
946 * Let M be the Matrix which we divide and D be the DiagonalMatrix which we divide with then the
947 * operation is defined as \f$ M \cdot D^{-1} \f$.
948 * @tparam S Element type of rhs
949 * @param rhs DiagonalMatrix to divide
950 * @return Mtype&
951 */
952 template <typename S,
953 std::enable_if_t<hila::is_assignable<T &, hila::type_div<T, S>>::value, int> = 0>
955
956 for (int i = 0; i < n; i++)
957 for (int j = 0; j < m; j++)
958 e(i, j) /= rhs.e(j);
959
960 return *this;
961 }
962
963
964 /**
965 * @brief Matrix fill
966 * @details Fills the matrix with element if it is assignable to matrix type T
967 *
968 * Works as follows:
969 *
970 * \code {.cpp}
971 * Matrix<n,m,MyType> M;
972 * M.fill(2) \\ Matrix is filled with 2
973 * \endcode
974 *
975 * @tparam S Scalar type to of rhs
976 * @param rhs Element to fill matrix with
977 * @return const Mtype&
978 */
979 template <typename S, std::enable_if_t<hila::is_assignable<T &, S>::value, int> = 0>
980 const Mtype &fill(const S rhs) out_only {
981 for (int i = 0; i < n * m; i++)
982 c[i] = rhs;
983 return *this;
984 }
985
986 /**
987 * @brief Transpose of matrix
988 * @details Return type for square matrix is same input, for non square return type is
989 * Matrix<n,m,MyType>
990 * @tparam mm
991 * @tparam std::conditional<n, Mtype, Matrix<m, n, T>>::type
992 * @return const Rtype
993 */
994 template <int mm = m,
995 typename Rtype = typename std::conditional<n == m, Mtype, Matrix<m, n, T>>::type,
996 std::enable_if_t<(mm != 1), int> = 0>
997 inline Rtype transpose() const {
998 Rtype res;
999 for (int i = 0; i < n; i++)
1000 for (int j = 0; j < m; j++) {
1001 res.e(j, i) = e(i, j);
1002 }
1003 return res;
1004 }
1005
1006 /**
1007 * @brief Transpose of vector
1008 * @details Returns reference
1009 *
1010 * @tparam mm
1011 * @return const RowVector<n, T>&
1012 */
1013 template <int mm = m, std::enable_if_t<mm == 1, int> = 0>
1014 inline const RowVector<n, T> &transpose() const {
1015 return *reinterpret_cast<const RowVector<n, T> *>(this);
1016 }
1017
1018 /**
1019 * @brief Transpose of RowVector
1020 * @details Returns reference
1021 *
1022 * @tparam mm
1023 * @return const RowVector<n, T>&
1024 */
1025 template <int nn = n, std::enable_if_t<nn == 1 && m != 1, int> = 0>
1026 inline const Vector<n, T> &transpose() const {
1027 return *reinterpret_cast<const Vector<n, T> *>(this);
1028 }
1029
1030
1031 /**
1032 * @brief Returns complex conjugate of Matrix
1033 *
1034 * @return const Mtype
1035 */
1036 inline Mtype conj() const {
1037 Mtype res;
1038 for (int i = 0; i < n * m; i++) {
1039 res.c[i] = ::conj(c[i]);
1040 }
1041 return res;
1042 }
1043
1044 /**
1045 * @brief Hermitian conjugate of matrix
1046 * @details for square matrix return type is same, for non square it is Matrix<m,n,MyType>
1047 *
1048 * @tparam std::conditional<n, Mtype, Matrix<m, n, T>>::type
1049 * @return const Rtype
1050 */
1051 template <typename Rtype = typename std::conditional<n == m, Mtype, Matrix<m, n, T>>::type>
1052 inline Rtype dagger() const {
1053 Rtype res;
1054 for (int i = 0; i < n; i++)
1055 for (int j = 0; j < m; j++) {
1056 res.e(j, i) = ::conj(e(i, j));
1057 }
1058 return res;
1059 }
1060
1061 /**
1062 * @brief Adjoint of matrix
1063 * @details Alias to dagger
1064 *
1065 * @tparam std::conditional<n, Mtype, Matrix<m, n, T>>::type
1066 * @return Rtype
1067 */
1068 template <typename Rtype = typename std::conditional<n == m, Mtype, Matrix<m, n, T>>::type>
1069 inline Rtype adjoint() const {
1070 return dagger();
1071 }
1072
1073 /**
1074 * @brief Returns absolute value of Matrix
1075 * @details For Matrix<n,m,Complex<T>> case type is changed to Matrix<n,m,T> as expected since
1076 * absolute value of a complex number is real
1077 *
1078 * @tparam M
1079 * @return Mtype
1080 */
1081 auto abs() const {
1083 for (int i = 0; i < n * m; i++) {
1084 res.c[i] = ::abs(c[i]);
1085 }
1086 return res;
1087 }
1088
1089
1090 // It seems that using special "Dagger" type makes the code slower!
1091 // Disable it now
1092 // inline const DaggerMatrix<m,n,T> & dagger() const {
1093 // return *reinterpret_cast<const DaggerMatrix<m,n,T>*>(this);
1094 // }
1095
1096 /**
1097 * @brief Returns real part of Matrix or #Vector
1098 *
1099 * @return Matrix<n, m, hila::arithmetic_type<T>>
1100 */
1103 for (int i = 0; i < m * n; i++) {
1104 res.c[i] = ::real(c[i]);
1105 }
1106 return res;
1107 }
1108
1109 /**
1110 * @brief Returns imaginary part of Matrix or #Vector
1111 *
1112 * @return Matrix<n, m, hila::arithmetic_type<T>>
1113 */
1116 for (int i = 0; i < m * n; i++) {
1117 res.c[i] = ::imag(c[i]);
1118 }
1119 return res;
1120 }
1121
1122 /**
1123 * @brief Computes Trace for Matrix
1124 * @details Not define for non square matrices. Static assert will stop code from compiling if
1125 * executed for non square matrices.
1126 *
1127 * @return T
1128 */
1129 T trace() const {
1130 static_assert(n == m, "trace not defined for non square matrices");
1131 T result(0);
1132 for (int i = 0; i < n; i++) {
1133 result += e(i, i);
1134 }
1135 return result;
1136 }
1137
1138 /**
1139 * @brief Multiply with given matrix and compute trace of result
1140 * @details Slightly cheaper operation than \code (M*N).trace() \endcode
1141 *
1142 * @tparam p
1143 * @tparam q
1144 * @tparam S
1145 * @tparam MT
1146 * @param rm
1147 * @return hila::type_mul<T, S>
1148 */
1149 template <int p, int q, typename S, typename MT>
1150 hila::type_mul<T, S> mul_trace(const Matrix_t<p, q, S, MT> &rm) const {
1151
1152 static_assert(p == m && q == n, "mul_trace(): argument matrix size mismatch");
1153
1154 hila::type_mul<T, S> res = 0;
1155 for (int i = 0; i < n; i++)
1156 for (int j = 0; j < m; j++) {
1157 res += e(i, j) * rm.e(j, i);
1158 }
1159 return res;
1160 }
1161
1162 /**
1163 * @brief Calculate square norm - sum of squared elements
1164 *
1165 * @return hila::arithmetic_type<T>
1166 */
1167 hila::arithmetic_type<T> squarenorm() const {
1168 hila::arithmetic_type<T> result(0);
1169 for (int i = 0; i < n * m; i++) {
1170 result += ::squarenorm(c[i]);
1171 }
1172 return result;
1173 }
1174
1175 /**
1176 * @brief Calculate vector norm - sqrt of squarenorm
1177 *
1178 * @tparam S
1179 * @return hila::arithmetic_type<T>
1180 */
1181 template <typename S = T,
1182 std::enable_if_t<hila::is_floating_point<hila::arithmetic_type<S>>::value, int> = 0>
1183 hila::arithmetic_type<T> norm() const {
1184 return sqrt(squarenorm());
1185 }
1186
1187 template <typename S = T,
1188 std::enable_if_t<!hila::is_floating_point<hila::arithmetic_type<S>>::value, int> = 0>
1189 double norm() const {
1190 return sqrt(static_cast<double>(squarenorm()));
1191 }
1192
1193 /**
1194 * @brief Find max of Matrix only for arithmetic types
1195 */
1196 template <typename S = T, std::enable_if_t<hila::is_arithmetic<S>::value, int> = 0>
1197 T max() const {
1198 T res = c[0];
1199 for (int i = 1; i < n * m; i++) {
1200 if (res < c[i])
1201 res = c[i];
1202 }
1203 return res;
1204 }
1205
1206 /**
1207 * @brief Find min of Matrix only for arithmetic types
1208 */
1209 template <typename S = T, std::enable_if_t<hila::is_arithmetic<S>::value, int> = 0>
1210 T min() const {
1211 T res = c[0];
1212 for (int i = 1; i < n * m; i++) {
1213 if (res > c[i])
1214 res = c[i];
1215 }
1216 return res;
1217 }
1218
1219
1220 // dot product - (*this).dagger() * rhs
1221 // could be done as well by writing the operation as above!
1222 /**
1223 * @brief Dot product
1224 * @details Only works between two #Vector objects
1225 *
1226 * \code {.cpp}
1227 * Vector<m,MyType> V,W;
1228 * .
1229 * .
1230 * .
1231 * V.dot(W); // valid operation
1232 * \endcode
1233 *
1234 * \code {.cpp}
1235 * RowVector<m,MyType> V,W;
1236 * .
1237 * .
1238 * .
1239 * V.dot(W); // not valid operation
1240 * \endcode
1241 *
1242 *
1243 * @tparam p Row length for rhs
1244 * @tparam q Column length for rhs
1245 * @tparam S Type for rhs
1246 * @tparam R Gives resulting type of lhs and rhs multiplication
1247 * @param rhs Vector to compute dot product with
1248 * @return R Value of dot product
1249 */
1250 template <int p, int q, typename S, typename R = hila::type_mul<T, S>>
1251 inline R dot(const Matrix<p, q, S> &rhs) const {
1252 static_assert(m == 1 && q == 1 && p == n,
1253 "dot() product only for vectors of the same length");
1254
1255 R r = 0;
1256 for (int i = 0; i < n; i++) {
1257 r += ::conj(c[i]) * rhs.e(i);
1258 }
1259 return r;
1260 }
1261
1262 // outer product - (*this) * rhs.dagger(), sizes (n,1) and (p,1)
1263 // gives n * p matrix
1264 // could be done as well by the above operation!
1265
1266 /**
1267 * @brief Outer product
1268 * @details Only works between two #Vector objects
1269 *
1270 * \code{.cpp}
1271 * Vector<n,MyType> V,W;
1272 * .
1273 * .
1274 * .
1275 * V.outer_product(W); \\ Valid operation
1276 * \endcode
1277 *
1278 * \code {.cpp}
1279 * RowVector<m,MyType> V,W;
1280 * .
1281 * .
1282 * .
1283 * V.outer_product(W); // not valid operation
1284 * \endcode
1285 *
1286 * @tparam p Row length for rhs
1287 * @tparam q Column length for rhs
1288 * @tparam S Element type for rhs
1289 * @tparam R Type between lhs and rhs multiplication
1290 * @param rhs Vector to compute outer product with
1291 * @return Matrix<n, p, R>
1292 */
1293 template <int p, int q, typename S, typename R = hila::type_mul<T, S>>
1295 static_assert(m == 1 && q == 1, "outer_product() only for vectors");
1296
1297 Matrix<n, p, R> res;
1298 for (int i = 0; i < n; i++) {
1299 for (int j = 0; j < p; j++) {
1300 res.e(i, j) = c[i] * ::conj(rhs.e(j));
1301 }
1302 }
1303 return res;
1304 }
1305
1306
1307 /// dot with matrix - matrix
1308 // template <int p, std::enable_if_t<(p > 1 || m > 1), int> = 0>
1309 // inline Matrix<m, p, T> dot(const Matrix<n, p, T> &rhs) const {
1310 // Matrix<m, p, T> res;
1311 // for (int i = 0; i < m; i++)
1312 // for (int j = 0; j < p; j++) {
1313 // res.e(i, j) = 0;
1314 // for (int k = 0; k < n; j++)
1315 // res.e(i, j) += ::conj(e(k, i)) * rhs.e(k, j);
1316 // }
1317 // }
1318
1319 /// Generate random elements
1320
1321 /**
1322 * @brief Fills Matrix with random elements from uniform distribution
1323 * @details
1324 * \code{.cpp}
1325 * Matrix<n,m,T> M;
1326 * M.random();
1327 * \endcode
1328 * @return Mtype&
1329 */
1330 Mtype &random() out_only {
1331
1332 static_assert(hila::is_floating_point<hila::arithmetic_type<T>>::value,
1333 "Matrix/Vector random() requires non-integral type elements");
1334
1335 for (int i = 0; i < n * m; i++) {
1336 hila::random(c[i]);
1337 }
1338 return *this;
1339 }
1340
1341 /**
1342 * @brief Fills Matrix with gaussian random elements from gaussian distribution
1343 * @details
1344 * \code {.cpp}
1345 * Matrix<n,m,T> M;
1346 * M.gaussian_random();
1347 * \endcode
1348 *
1349 * @param width
1350 * @return Mtype&
1351 */
1352 Mtype &gaussian_random(double width = 1.0) out_only {
1353
1354 static_assert(hila::is_floating_point<hila::arithmetic_type<T>>::value,
1355 "Matrix/Vector gaussian_random() requires non-integral type elements");
1356
1357 // for Complex numbers gaussian_random fills re and im efficiently
1358 if constexpr (hila::is_complex<T>::value) {
1359 for (int i = 0; i < n * m; i++) {
1360 c[i].gaussian_random(width);
1361 }
1362 } else {
1363 // now not complex matrix
1364 // if n*m even, max i in loop below is n*m-2.
1365 // if n*m odd, max i is n*m-3
1366 double gr;
1367 for (int i = 0; i < n * m - 1; i += 2) {
1368 c[i] = hila::gaussrand2(gr) * width;
1369 c[i + 1] = gr * width;
1370 }
1371 if constexpr ((n * m) % 2 > 0) {
1372 c[n * m - 1] = hila::gaussrand() * width;
1373 }
1374 }
1375 return *this;
1376 }
1377
1378 /**
1379 * @brief permute columns of Matrix
1380 * @details Reordering is done based off of permutation vector
1381 *
1382 * @param permutation Vector of integers to permute columns
1383 * @return Mtype
1384 */
1385 Mtype permute_columns(const Vector<m, int> &permutation) const {
1386 Mtype res;
1387 for (int i = 0; i < m; i++)
1388 res.set_column(i, this->column(permutation[i]));
1389 return res;
1390 }
1391
1392 /**
1393 * @brief permute rows of Matrix
1394 * @details Reordering is done based off of permutation vector
1395 *
1396 * @param permutation Vector of integers to permute rows
1397 * @return Mtype
1398 */
1399 Mtype permute_rows(const Vector<n, int> &permutation) const {
1400 Mtype res;
1401 for (int i = 0; i < n; i++)
1402 res.set_row(i, this->row(permutation[i]));
1403 return res;
1404 }
1405
1406
1407 /**
1408 * @brief Permute elements of vector
1409 * @details Reordering is done based off of permutation vector
1410 *
1411 * @param permutation Vector of integers to permute rows
1412 * @return Mtype
1413 */
1414 template <int N>
1415 Mtype permute(const Vector<N, int> &permutation) const {
1416 static_assert(
1417 n == 1 || m == 1,
1418 "permute() only for vectors, use permute_rows() or permute_columns() for matrices");
1419 static_assert(N == Mtype::size(), "Incorrect size of permutation vector");
1420
1421 Mtype res;
1422 for (int i = 0; i < N; i++) {
1423 res[i] = (*this)[permutation[i]];
1424 }
1425 return res;
1426 }
1427
1428 /**
1429 * @brief Sort method for #Vector which returns permutation order
1430 * @details Order can be past as argument as either ascending (default) or descending
1431 *
1432 * __Ascending__
1433 * @code {.cpp}
1434 * Vector<n,MyType> V;
1435 * Vector<n,int> perm;
1436 * V.random();
1437 * V.sort(perm);
1438 * V.permute(perm);
1439 * @endcode
1440 *
1441 * __Descending__
1442 * @code {.cpp}
1443 * Vector<n,MyType> V;
1444 * Vector<n,int> perm;
1445 * V.random();
1446 * V.sort(perm,hila::sort::descending);
1447 * V.permute(perm);
1448 * @endcode
1449 * @tparam N
1450 * @param permutation
1451 * @param order
1452 * @return Mtype
1453 */
1454#pragma hila novector
1455 template <int N>
1456 Mtype sort(Vector<N, int> &permutation, hila::sort order = hila::sort::ascending) const {
1457
1458 static_assert(n == 1 || m == 1, "Sorting possible only for vectors");
1459 static_assert(hila::is_arithmetic<T>::value,
1460 "Sorting possible only for arithmetic vector elements");
1461 static_assert(N == Mtype::size(), "Incorrect size of permutation vector");
1462
1463 for (int i = 0; i < N; i++)
1464 permutation[i] = i;
1465 if (hila::sort::unsorted == order) {
1466 return *this;
1467 }
1468
1469 if (hila::sort::ascending == order) {
1470 for (int i = 1; i < N; i++) {
1471 for (int j = i; j > 0 && c[permutation[j - 1]] > c[permutation[j]]; j--)
1472 hila::swap(permutation[j], permutation[j - 1]);
1473 }
1474 } else {
1475 for (int i = 1; i < N; i++) {
1476 for (int j = i; j > 0 && c[permutation[j - 1]] < c[permutation[j]]; j--)
1477 hila::swap(permutation[j], permutation[j - 1]);
1478 }
1479 }
1480
1481 return this->permute(permutation);
1482 }
1483
1484 /**
1485 * @brief Sort method for #Vector
1486 * @details Order can be past as argument as either ascending (default) or descending
1487 *
1488 * __Ascending__
1489 * @code {.cpp}
1490 * Vector<n,MyType> V;
1491 * V.random();
1492 * V.sort(); // V is sorted in ascending order
1493 * @endcode
1494 *
1495 * __Descending__
1496 * @code {.cpp}
1497 * V.sort(hila::sort::descending);
1498 * @endcode
1499 *
1500 * @param order Order to sort in
1501 * @return Mtype
1502 */
1503#pragma hila novector
1504 Mtype sort(hila::sort order = hila::sort::ascending) const {
1505 static_assert(n == 1 || m == 1, "Sorting possible only for vectors");
1506
1507 Vector<Mtype::size(), int> permutation;
1508 return sort(permutation, order);
1509 }
1510
1511
1512 /**
1513 * @brief Multiply \f$ n \times m \f$-matrix from the left by \f$ n \times m \f$ matrix defined
1514 * by \f$ 2 \times 2 \f$ sub matrix
1515 * @details The multiplication is defined as follows, let \f$M\f$ as the \f$ 2 \times 2 \f$
1516 * input matrix and \f$B\f$ be `(this)` matrix, being the matrix stored in the object this
1517 * method is called for. Let \f$A = I\f$ be a \f$ n \times m \f$ unit matrix. We then set the
1518 * values of A to be: \f{align}{ A_{p,p} = M_{0,0}, \hspace{5px} A_{p,q} = M_{0,1}, \hspace{5px}
1519 * A_{q,p} = M_{1,0}, \hspace{5px} A_{q,q} = M_{1,1}. \f}
1520 *
1521 * Then the resulting matrix will be:
1522 *
1523 * \f{align}{ B = A \cdot B \f}
1524 *
1525 * @tparam R Element type of M
1526 * @tparam Mt Matrix type of M
1527 * @param p First row and column
1528 * @param q Second row and column
1529 * @param M \f$ 2 \times 2\f$ Matrix to multiply with
1530 */
1531 template <typename R, typename Mt>
1532 void mult_by_2x2_left(int p, int q, const Matrix_t<2, 2, R, Mt> &M) {
1533 static_assert(hila::contains_complex<T>::value || !hila::contains_complex<R>::value,
1534 "Cannot multiply real matrix with complex 2x2 block matrix");
1535
1536 Vector<2, T> a;
1537 for (int i = 0; i < m; i++) {
1538 a.e(0) = this->e(p, i);
1539 a.e(1) = this->e(q, i);
1540
1541 a = M * a;
1542
1543 this->e(p, i) = a.e(0);
1544 this->e(q, i) = a.e(1);
1545 }
1546 }
1547
1548 /**
1549 * @brief Multiply \f$ n \times m \f$-matrix from the right by \f$ n \times m \f$ matrix
1550 * defined by \f$ 2 \times 2 \f$ sub matrix
1551 * @details See Matrix::mult_by_2x2_left, only difference being that the multiplication is from
1552 * the right.
1553 *
1554 * @tparam R Element type of M
1555 * @tparam Mt Matrix type of M
1556 * @param p First row and column
1557 * @param q Second row and column
1558 * @param M \f$ 2 \times 2\f$ Matrix to multiply with
1559 */
1560 template <typename R, typename Mt>
1561 void mult_by_2x2_right(int p, int q, const Matrix_t<2, 2, R, Mt> &M) {
1562 static_assert(hila::contains_complex<T>::value || !hila::contains_complex<R>::value,
1563 "Cannot multiply real matrix with complex 2x2 block matrix");
1564
1566 for (int i = 0; i < n; i++) {
1567 a.e(0) = this->e(i, p);
1568 a.e(1) = this->e(i, q);
1569
1570 a = a * M;
1571
1572 this->e(i, p) = a.e(0);
1573 this->e(i, q) = a.e(1);
1574 }
1575 }
1576
1577
1578 // Following methods are defined in matrix_linalg.h
1579 //
1580
1581 /**
1582 * @brief following calculate the determinant of a square matrix
1583 * det() is the generic interface, using laplace for small matrices and LU for large
1584 */
1585
1586#pragma hila novector
1587 T det_lu() const;
1588#pragma hila novector
1589 T det_laplace() const;
1590#pragma hila novector
1591 T det() const;
1592
1593 /**
1594 * @brief Calculate eigenvalues and -vectors of an hermitean (or symmetric) matrix.
1595 * @details Returns the number of Jacobi iterations, or -1 if did not converge.
1596 * Arguments will contain eigenvalues and eigenvectors in columns of the "eigenvectors" matrix.
1597 * Computation is done in double precision despite the input matrix types.
1598 * @param eigenvaluevec Vector of computed eigenvalue
1599 * @param eigenvectors Eigenvectors as columns of \f$ n \times n \f$ Matrix
1600 *
1601 */
1602
1603#pragma hila novector
1604 template <typename Et, typename Mt, typename MT>
1605 int eigen_hermitean(out_only DiagonalMatrix<n, Et> &eigenvalues,
1606 out_only Matrix_t<n, n, Mt, MT> &eigenvectors,
1607 enum hila::sort sorted = hila::sort::unsorted) const;
1608
1609 eigen_result<Mtype> eigen_hermitean(enum hila::sort sorted = hila::sort::unsorted) const;
1610
1611 // Temporary interface to eigenvalues, to be removed
1612 template <typename Et, typename Mt, typename MT>
1613 int eigen_jacobi(out_only Vector<n, Et> &eigenvaluevec,
1614 out_only Matrix_t<n, n, Mt, MT> &eigenvectors,
1615 enum hila::sort sorted = hila::sort::unsorted) const {
1616
1618 int i = eigen_hermitean(d, eigenvectors, sorted);
1619 eigenvaluevec = d.asArray().asVector();
1620 return i;
1621 }
1622
1623
1624#pragma hila novector
1625 template <typename Et, typename Mt, typename MT>
1626 int svd(out_only Matrix_t<n, n, Mt, MT> &_U, out_only DiagonalMatrix<n, Et> &_D,
1627 out_only Matrix_t<n, n, Mt, MT> &_V,
1628 enum hila::sort sorted = hila::sort::unsorted) const;
1629
1630 svd_result<Mtype> svd(enum hila::sort sorted = hila::sort::unsorted) const;
1631
1632
1633#pragma hila novector
1634 template <typename Et, typename Mt, typename MT>
1635 int svd_pivot(out_only Matrix_t<n, n, Mt, MT> &_U, out_only DiagonalMatrix<n, Et> &_D,
1636 out_only Matrix_t<n, n, Mt, MT> &_V,
1637 enum hila::sort sorted = hila::sort::unsorted) const;
1638
1639 svd_result<Mtype> svd_pivot(enum hila::sort sorted = hila::sort::unsorted) const;
1640
1641
1642 //////// matrix_linalg.h
1643
1644 /// Convert to string for printing
1645 ///
1646 std::string str(int prec = 8, char separator = ' ') const {
1647 std::stringstream text;
1648 text.precision(prec);
1649 for (int i = 0; i < n; i++) {
1650 for (int j = 0; j < m; j++) {
1651 text << e(i, j);
1652 if (i < n - 1 || j < m - 1)
1653 text << separator;
1654 }
1655 }
1656 return text.str();
1657 }
1658};
1659
1660/**
1661 * @brief \f$ n \times m \f$ Matrix class which defines matrix operations.
1662 * @details The Matrix class is a derived class of Matrix_t, which is the general definition of a
1663 * matrix class. See Matrix_t details section for reasoning.
1664 *
1665 * All mathematical operations for Matrix are inherited from Matrix_t and are visible on this page.
1666 * __NOTE__: Some method documentation is not being displayed, for example the assignment operator
1667 * documentation is not being inherited. See Matrix_t::operator= for use.
1668 *
1669 * To see all methods of initializing a matrix see constructor method #Matrix::Matrix
1670 *
1671 * __NOTE__: In the documentation examples n,m are integers and MyType is a HILA [standard
1672 * type](@ref standard) or Complex.
1673 *
1674 * @tparam n Number of rows
1675 * @tparam m Number of columns
1676 * @tparam T Data type Matrix
1677 */
1678template <int n, int m, typename T>
1679class Matrix : public Matrix_t<n, m, T, Matrix<n, m, T>> {
1680
1681 public:
1682 /// std incantation for field types
1683 using base_type = hila::arithmetic_type<T>;
1684 using argument_type = T;
1685
1686 /**
1687 * @brief Default constructo
1688 * @details
1689 * __NOTE__: n,m are integers and MyType is a HILA [standard type](@ref standard) or Complex.
1690 *
1691 * __Default constructor__:
1692 *
1693 * Allocates undefined \f$ n\times m\f$ Array.
1694 *
1695 * \code{.cpp}
1696 * Matrix<n,m,MyType> M;
1697 * \endcode
1698 */
1699 Matrix() = default;
1700 ~Matrix() = default;
1701 /// Default copy constructor
1702 Matrix(const Matrix &v) = default;
1703
1704
1705 // constructor from scalar -- keep it explicit! Not good for auto use
1706 /**
1707 * @brief Scalar constructor
1708 * @details
1709 *
1710 * Construct with given scalar at diagonal elements \f$ M = \mathbf{I}\cdot x\f$. Matrix must be
1711 * square \f$n == m\f$
1712 *
1713 * \code{.cpp}
1714 * MyType x = hila::random();
1715 * Matrix<n,m,MyType> M = x;
1716 * \endcode
1717
1718 * @tparam S Type for scalar
1719 * @tparam nn Number of rows
1720 * @tparam mm Numebr of columns
1721 * @param rhs Scalar element to assign
1722 */
1723 template <typename S, int nn = n, int mm = m,
1724 std::enable_if_t<(hila::is_assignable<T &, S>::value && nn == mm), int> = 0>
1725 explicit Matrix(const S rhs) out_only {
1726 for (int i = 0; i < n; i++)
1727 for (int j = 0; j < n; j++) {
1728 if (i == j)
1729 this->e(i, j) = rhs;
1730 else
1731 this->e(i, j) = 0;
1732 }
1733 }
1734
1735 /**
1736 * @brief Copy constructor
1737 *
1738 * Construction from previously defined matrix if types are compatible. For example the code
1739 * below only works if assignment from MyOtherType to MyType is defined.
1740 *
1741 * \code{.cpp}
1742 * Matrix<n,m,MyOtherType> M_0;
1743 * //
1744 * // M_0 is filled with content
1745 * //
1746 * Matrix<n,m,MyType> M = M_0;
1747 * \endcode
1748
1749 * @tparam S Element type for copied matrix
1750 * @tparam MT Matrix type for copied matrix
1751 * @param rhs Matrix to copy
1752 */
1753 template <typename S, typename MT,
1754 std::enable_if_t<hila::is_assignable<T &, S>::value, int> = 0>
1755 Matrix(const Matrix_t<n, m, S, MT> &rhs) out_only {
1756 for (int i = 0; i < n * m; i++) {
1757 this->c[i] = rhs.c[i];
1758 }
1759 }
1760
1761 /**
1762 * @brief Zero constructor
1763 * @details Constructing from 0 sets the whole Matrix to zero
1764 * @internal
1765 * @param z 0
1766 */
1767 Matrix(const std::nullptr_t &z) out_only {
1768 for (int i = 0; i < n * m; i++) {
1769 this->c[i] = 0;
1770 }
1771 }
1772
1773 // Construct matrix automatically from right-size initializer list
1774 // This does not seem to be dangerous, so keep non-explicit
1775 /**
1776 * @brief Initializer list constructor
1777 *
1778 * Construction from c++ initializer list.
1779 *
1780 * \code{.cpp}
1781 * Matrix<2,2,int> M = {1, 0
1782 * 0, 1};
1783 * \endcode
1784 * @tparam S Element type for initializer list
1785 * @param rhs
1786 */
1787 template <typename S, std::enable_if_t<hila::is_assignable<T &, S>::value, int> = 0>
1788 Matrix(std::initializer_list<S> rhs) {
1789 assert(rhs.size() == n * m &&
1790 "Matrix/Vector initializer list size must match variable size");
1791 int i = 0;
1792 for (auto it = rhs.begin(); it != rhs.end(); it++, i++) {
1793 this->c[i] = *it;
1794 }
1795 }
1796
1797
1798 // use the Base::Base -trick to inherit assignments
1799 using Matrix_t<n, m, T, Matrix<n, m, T>>::operator=;
1800 using Matrix_t<n, m, T, Matrix<n, m, T>>::operator+=;
1801 using Matrix_t<n, m, T, Matrix<n, m, T>>::operator-=;
1802 using Matrix_t<n, m, T, Matrix<n, m, T>>::operator*=;
1803 using Matrix_t<n, m, T, Matrix<n, m, T>>::operator/=;
1804
1805 using Matrix_t<n, m, T, Matrix<n, m, T>>::operator-;
1806};
1807
1808namespace hila {
1809
1810//////////////////////////////////////////////////////////////////////////
1811// Tool to get "right" result type for matrix (T1) + (T2) -op, where
1812// T1 and T2 are either complex or arithmetic matrices
1813// Use: hila::mat_x_mat_type<T1,T2>
1814// - T1 == T2 returns T1
1815// - If T1 or T2 contains complex, returns Matrix<rows,cols,Complex<typeof T1+T2>>
1816// - otherwise returns the "larger" type
1817
1818template <typename T1, typename T2>
1819struct matrix_op_type_s {
1820 using type = Matrix<T1::rows(), T1::columns(), hila::ntype_op<T1, T2>>;
1821};
1822
1823// if types are the same
1824template <typename T>
1825struct matrix_op_type_s<T, T> {
1826 using type = T;
1827};
1828
1829// useful format mat_x_mat_type<T1,T2>
1830template <typename T1, typename T2>
1831using mat_x_mat_type = typename matrix_op_type_s<T1, T2>::type;
1832
1833////////////////////////////////////////////////////////////////////////////////
1834// Matrix + scalar result type:
1835// hila::mat_scalar_type<Mt,S>
1836// - if result is convertible to Mt, return Mt
1837// - if Mt is not complex and S is, return
1838// Matrix<Complex<type_sum(scalar_type(Mt),scalar_type(S))>>
1839// - otherwise return Matrix<type_sum>
1840
1841template <typename Mt, typename S, typename Enable = void>
1842struct matrix_scalar_op_s {
1843 using type =
1844 Matrix<Mt::rows(), Mt::columns(),
1845 Complex<hila::type_plus<hila::arithmetic_type<Mt>, hila::arithmetic_type<S>>>>;
1846};
1847
1848template <typename Mt, typename S>
1849struct matrix_scalar_op_s<
1850 Mt, S,
1851 typename std::enable_if_t<std::is_convertible<hila::type_plus<hila::number_type<Mt>, S>,
1852 hila::number_type<Mt>>::value>> {
1853 // using type = Mt;
1854 using type = typename std::conditional<
1855 hila::is_floating_point<hila::arithmetic_type<Mt>>::value, Mt,
1856 Matrix<Mt::rows(), Mt::columns(),
1857 hila::type_plus<hila::arithmetic_type<Mt>, hila::arithmetic_type<S>>>>::type;
1858};
1859
1860template <typename Mt, typename S>
1861using mat_scalar_type = typename matrix_scalar_op_s<Mt, S>::type;
1862
1863
1864} // namespace hila
1865
1866//////////////////////////////////////////////////////////////////////////
1867
1868/**
1869 * @brief Return transposed Matrix or #Vector
1870 * @details Wrapper around Matrix::transpose
1871 *
1872 * Can be called as:
1873 * \code {.cpp}
1874 * Matrix<n,m,MyType> M,M_transposed;
1875 * M_transposed = transpose(M);
1876 * \endcode
1877 *
1878 *
1879 * @tparam Mtype Matrix type
1880 * @param arg Object to be transposed
1881 * @return auto Mtype transposed matrix
1882 */
1883template <typename Mtype, std::enable_if_t<Mtype::is_matrix(), int> = 0>
1884inline auto transpose(const Mtype &arg) {
1885 return arg.transpose();
1886}
1887
1888/**
1889 * @brief Return conjugate Matrix or #Vector
1890 * @details Wrapper around Matrix::conj
1891 *
1892 * Can be called as:
1893 * \code {.cpp}
1894 * Matrix<n,m,MyType> M,M_conjugate;
1895 * M_conjugate = conj(M);
1896 * \endcode
1897 *
1898 *
1899 * @tparam Mtype Matrix type
1900 * @param arg Object to be conjugated
1901 * @return auto Mtype conjugated matrix
1902 */
1903template <typename Mtype, std::enable_if_t<Mtype::is_matrix(), int> = 0>
1904inline auto conj(const Mtype &arg) {
1905 return arg.conj();
1906}
1907
1908/**
1909 * @brief Return adjoint Matrix
1910 * @details Wrapper around Matrix::adjoint
1911 *
1912 * Can be called as:
1913 * \code {.cpp}
1914 * Matrix<n,m,MyType> M,M_adjoint;
1915 * M_conjugate = adjoint(M);
1916 * \endcode
1917 *
1918 *
1919 * @tparam Mtype Matrix type
1920 * @param arg Object to compute adjoint of
1921 * @return auto Mtype adjoint matrix
1922 */
1923template <typename Mtype, std::enable_if_t<Mtype::is_matrix(), int> = 0>
1924inline auto adjoint(const Mtype &arg) {
1925 return arg.adjoint();
1926}
1927
1928/**
1929 * @brief Return dagger of Matrix
1930 * @details Wrapper around Matrix::adjoint
1931 *
1932 * Same as adjoint
1933 *
1934 *
1935 * @tparam Mtype Matrix type
1936 * @param arg Object to compute dagger of
1937 * @return auto Mtype dagger matrix
1938 */
1939template <typename Mtype, std::enable_if_t<Mtype::is_matrix(), int> = 0>
1940inline auto dagger(const Mtype &arg) {
1941 return arg.adjoint();
1942}
1943
1944/**
1945 * @brief Return absolute value Matrix or #Vector
1946 * @details Wrapper around Matrix::abs
1947 *
1948 * Can be called as:
1949 * \code {.cpp}
1950 * Matrix<n,m,MyType> M,M_abs;
1951 * M_abs = abs(M);
1952 * \endcode
1953 *
1954 *
1955 * @tparam Mtype Matrix type
1956 * @param arg Object to compute absolute value of
1957 * @return auto Mtype absolute value Matrix or Vector
1958 */
1959template <typename Mtype, std::enable_if_t<Mtype::is_matrix(), int> = 0>
1960inline auto abs(const Mtype &arg) {
1961 return arg.abs();
1962}
1963
1964/**
1965 * @brief Return trace of square Matrix
1966 * @details Wrapper around Matrix::trace
1967 *
1968 * Can be called as:
1969 * \code {.cpp}
1970 * Matrix<n,m,MyType> M;
1971 * MyType T;
1972 * T = trace(M);
1973 * \endcode
1974 *
1975 *
1976 * @tparam Mtype Matrix type
1977 * @param arg Object to compute trace of
1978 * @return auto Trace of Matrix
1979 */
1980template <typename Mtype, std::enable_if_t<Mtype::is_matrix(), int> = 0>
1981inline auto trace(const Mtype &arg) {
1982 return arg.trace();
1983}
1984
1985/**
1986 * @brief Return real of Matrix or #Vector
1987 * @details Wrapper around Matrix::real
1988 *
1989 * Can be called as:
1990 * \code {.cpp}
1991 * Matrix<n,m,MyType> M,M_real;
1992 * M_real = real(M);
1993 * \endcode
1994 *
1995 *
1996 * @tparam Mtype Matrix type
1997 * @param arg Object to compute real part of
1998 * @return auto Mtype real part of arg
1999 */
2000template <typename Mtype, std::enable_if_t<Mtype::is_matrix(), int> = 0>
2001inline auto real(const Mtype &arg) {
2002 return arg.real();
2003}
2004
2005/**
2006 * @brief Return imaginary of Matrix or #Vector
2007 * @details Wrapper around Matrix::imag
2008 *
2009 * Can be called as:
2010 * \code {.cpp}
2011 * Matrix<n,m,MyType> M,M_imag;
2012 * M_imag = imag(M);
2013 * \endcode
2014 *
2015 *
2016 * @tparam Mtype Matrix type
2017 * @param arg Object to compute imag part of
2018 * @return auto Mtype imag part of arg
2019 */
2020template <typename Mtype, std::enable_if_t<Mtype::is_matrix(), int> = 0>
2021inline auto imag(const Mtype &arg) {
2022 return arg.imag();
2023}
2024
2025
2026///////////////////////////////////////////////////////////////////////////
2027// Now matrix additions: matrix + matrix
2028
2029
2030/**
2031 * @brief Addition operator Matrix + Matrix
2032 * @memberof Matrix_t
2033 * @details Addition operator between matrices is defined in the usual way (element wise).
2034 *
2035 * __NOTE__: Matrices must share same dimensionality.
2036 *
2037 * \code {.cpp}
2038 * Matrix<n,m,MyType> M, N, S;
2039 * M.fill(1);
2040 * N.fill(1);
2041 * S = M + N; // Resulting matrix is uniformly 2
2042 * \endcode
2043 * @tparam Mtype1 Matrix type of a
2044 * @tparam Mtype2 Matrix type of b
2045 * @param a Matrix to add
2046 * @param b Matrix to add
2047 * @return Rtype Return matrix of compatible type between Mtype1 and Mtype2
2048 */
2049template <typename Mtype1, typename Mtype2,
2050 std::enable_if_t<Mtype1::is_matrix() && Mtype2::is_matrix(), int> = 0,
2051 typename Rtype = hila::mat_x_mat_type<Mtype1, Mtype2>,
2052 std::enable_if_t<!std::is_same<Mtype1, Rtype>::value, int> = 0>
2053inline Rtype operator+(const Mtype1 &a, const Mtype2 &b) {
2054
2055 constexpr int n = Mtype1::rows();
2056 constexpr int m = Mtype1::columns();
2057
2058 static_assert(n == Mtype2::rows() && m == Mtype2::columns(), "Matrix sizes do not match");
2059
2060 Rtype r;
2061 for (int i = 0; i < n; i++)
2062 for (int j = 0; j < m; j++)
2063 r.e(i, j) = a.e(i, j) + b.e(i, j);
2064 return r;
2065}
2066
2067/**
2068 * @brief Real micro-optimization Matrix + Matrix - no extra creation of variable and copy.
2069 * @internal
2070 * @tparam Mtype1
2071 * @tparam Mtype2
2072 * @tparam Mtype2,
2073 * std::enable_if_t<Mtype1::is_matrix() && Mtype2::is_matrix(), int,
2074 * typename Rtype, Mtype2>
2075 * @param a
2076 * @param b
2077 * @return Rtype Return matrix of compatible type between Mtype1 and Mtype2
2078 */
2079template <typename Mtype1, typename Mtype2,
2080 std::enable_if_t<Mtype1::is_matrix() && Mtype2::is_matrix(), int> = 0,
2081 typename Rtype = hila::mat_x_mat_type<Mtype1, Mtype2>,
2082 std::enable_if_t<std::is_same<Mtype1, Rtype>::value, int> = 0>
2083inline Rtype operator+(Mtype1 a, const Mtype2 &b) {
2084
2085 constexpr int n = Mtype1::rows();
2086 constexpr int m = Mtype1::columns();
2087
2088 static_assert(n == Mtype2::rows() && m == Mtype2::columns(), "Matrix sizes do not match");
2089
2090 for (int i = 0; i < n; i++)
2091 for (int j = 0; j < m; j++)
2092 a.e(i, j) += b.e(i, j);
2093 return a;
2094}
2095
2096/**
2097 * @brief Subtraction operator Matrix - Matrix
2098 * @memberof Matrix_t
2099 * @details
2100 * Subtraction operator between matrices is defined in the usual way (element wise).
2101 *
2102 * __NOTE__: Matrices must share same dimensionality.
2103 *
2104 * \code {.cpp}
2105 * Matrix<n,m,MyType> M, N, S;
2106 * M.fill(1);
2107 * N.fill(1);
2108 * S = M - N; // Resulting matrix is uniformly 0
2109 * \endcode
2110 *
2111 * @tparam Mtype1 Matrix type of a
2112 * @tparam Mtype2 Matrix type of b
2113 * @param a Matrix to subtract from
2114 * @param b Matrix to subtract
2115 * @return Rtype Return matrix of compatible type between Mtype1 and Mtype2
2116 */
2117template <typename Mtype1, typename Mtype2,
2118 std::enable_if_t<Mtype1::is_matrix() && Mtype2::is_matrix(), int> = 0,
2119 typename Rtype = hila::mat_x_mat_type<Mtype1, Mtype2>>
2120inline Rtype operator-(const Mtype1 &a, const Mtype2 &b) {
2121
2122 constexpr int n = Mtype1::rows();
2123 constexpr int m = Mtype1::columns();
2124
2125 static_assert(n == Mtype2::rows() && m == Mtype2::columns(), "Matrix sizes do not match");
2126
2127 Rtype r;
2128 for (int i = 0; i < n; i++)
2129 for (int j = 0; j < m; j++)
2130 r.e(i, j) = a.e(i, j) - b.e(i, j);
2131 return r;
2132}
2133
2134/**
2135 * @brief Addition operator Matrix + scalar
2136 * @memberof Matrix_t
2137 * @details
2138 * Addition operator between matrix and scalar is defined in the usual way, where the scalar is
2139 * added to the diagonal elements.
2140 * *
2141 * \f$ M + 2 = M + 2\cdot\mathbb{1} \f$
2142 *
2143 * \code {.cpp}
2144 * Matrix<n,m,MyType> M,S;
2145 * M.fill(0);
2146 * S = M + 1; // Resulting matrix is identity matrix
2147 * \endcode
2148 *
2149 * @tparam Mtype Matrix type of b
2150 * @tparam S Type of scalar
2151 * @param a Matrix to add to
2152 * @param b Scalar to add
2153 * @return Rtype
2154 */
2155template <typename Mtype, typename S,
2156 std::enable_if_t<Mtype::is_matrix() && hila::is_complex_or_arithmetic<S>::value, int> = 0,
2157 typename Rtype = hila::mat_scalar_type<Mtype, S>>
2158inline Rtype operator+(const Mtype &a, const S &b) {
2159
2160 static_assert(Mtype::rows() == Mtype::columns(),
2161 "Matrix + scalar possible only for square matrix");
2162
2163 Rtype r;
2164 for (int i = 0; i < Rtype::rows(); i++)
2165 for (int j = 0; j < Rtype::columns(); j++) {
2166 r.e(i, j) = a.e(i, j);
2167 if (i == j)
2168 r.e(i, j) += b;
2169 }
2170 return r;
2171}
2172
2173/**
2174 * @brief Addition operator scalar + Matrix
2175 * @memberof Matrix_t
2176 * @details
2177 * Addition operator between Scalar and Matrix is defined in the usual way, where the scalar is
2178 * treated as diagonal matrix which is then added to.
2179 * \f$ 2 + M = 2\cdot\mathbb{1} + M \f$
2180 *
2181 * \code {.cpp}
2182 * Matrix<n,m,MyType> M,R;
2183 * M = 0; // M = 0*I
2184 * R = 1 + M; // Resulting matrix is identity matrix
2185 * \endcode
2186 * @tparam Mtype Matrix type of a
2187 * @tparam S scalar type of b
2188 * @param b Matrix to add
2189 * @param a Scalar to add to
2190 * @return Rtype
2191 */
2192template <typename Mtype, typename S,
2193 std::enable_if_t<Mtype::is_matrix() && hila::is_complex_or_arithmetic<S>::value, int> = 0,
2194 typename Rtype = hila::mat_scalar_type<Mtype, S>>
2195inline Rtype operator+(const S &b, const Mtype &a) {
2196
2197 static_assert(Mtype::rows() == Mtype::columns(),
2198 "Matrix + scalar possible only for square matrix");
2199 return a + b;
2200}
2201
2202// matrix - scalar
2203/**
2204 * @brief Subtraction operator Matrix - scalar
2205 * @memberof Matrix_t
2206 * @details
2207 *
2208 * Subtraction operator between matrix and scalar is defined in the usual way, where the scalar
2209 * is subtracted from the diagonal elements.
2210 *
2211 * \f$ M - 2 = M - 2\cdot\mathbb{1} \f$
2212 *
2213 * \code {.cpp}
2214 * Matrix<n,m,MyType> M,R;
2215 * M = 2; // M = 2*I
2216 * R = M - 1; // Resulting matrix is identity matrix
2217 * \endcode
2218 * @tparam Mtype Matrix type of a
2219 * @tparam S Scalar type of b
2220 * @param a Matrix to subtract from
2221 * @param b Scalar to subtract
2222 * @return Rtype
2223 */
2224template <typename Mtype, typename S,
2225 std::enable_if_t<Mtype::is_matrix() && hila::is_complex_or_arithmetic<S>::value, int> = 0,
2226 typename Rtype = hila::mat_scalar_type<Mtype, S>>
2227inline Rtype operator-(const Mtype &a, const S &b) {
2228
2229 static_assert(Mtype::rows() == Mtype::columns(),
2230 "Matrix - scalar possible only for square matrix");
2231
2232 Rtype r;
2233 for (int i = 0; i < Rtype::rows(); i++)
2234 for (int j = 0; j < Rtype::columns(); j++) {
2235 r.e(i, j) = a.e(i, j);
2236 if (i == j)
2237 r.e(i, j) -= b;
2238 }
2239 return r;
2240}
2241
2242/**
2243 * @brief Subtraction operator Scalar - Matrix
2244 * @memberof Matrix_t
2245 * @details
2246 * Subtraction operator between Scalar and Matrix is defined in the usual way, where the scalar is
2247 * treated as diagonal matrix which is then subtracted from.
2248 *
2249 * \f$ 2 - M = 2\cdot\mathbb{1} - M \f$
2250 *
2251 * \code {.cpp}
2252 * Matrix<n,m,MyType> M,R;
2253 * M = 1; // M = 1*I
2254 * R = 2 - M; // Resulting matrix is identity matrix
2255 * \endcode
2256 * @tparam Mtype Matrix type a
2257 * @tparam S Scalar type of b
2258 * @param b Scalar to subtract from
2259 * @param a Matrix to subtract
2260 * @return Rtype
2261 */
2262template <typename Mtype, typename S,
2263 std::enable_if_t<Mtype::is_matrix() && hila::is_complex_or_arithmetic<S>::value, int> = 0,
2264 typename Rtype = hila::mat_scalar_type<Mtype, S>>
2265inline Rtype operator-(const S &b, const Mtype &a) {
2266
2267 static_assert(Mtype::rows() == Mtype::columns(),
2268 "Scalar - matrix possible only for square matrix");
2269
2270 Rtype r;
2271 for (int i = 0; i < Rtype::rows(); i++)
2272 for (int j = 0; j < Rtype::columns(); j++) {
2273 r.e(i, j) = -a.e(i, j);
2274 if (i == j)
2275 r.e(i, j) += b;
2276 }
2277 return r;
2278}
2279
2280////////////////////////////////////////
2281// matrix * matrix is the most important bit
2282
2283/**
2284 * @brief Multiplication operator Square Matrix * Square Matrix
2285 * @internal
2286 * @memberof Matrix_t
2287 * @tparam Mt Matrix type
2288 * @param a First matrix to multiply
2289 * @param b Second matrix to multiply
2290 * @return Mt
2291 */
2292template <typename Mt, std::enable_if_t<Mt::is_matrix() && Mt::rows() == Mt::columns(), int> = 0>
2293inline Mt operator*(const Mt &a, const Mt &b) {
2294
2295 constexpr int n = Mt::rows();
2296
2297 Mt res;
2298
2299 for (int i = 0; i < n; i++)
2300 for (int j = 0; j < n; j++) {
2301 res.e(i, j) = a.e(i, 0) * b.e(0, j);
2302 for (int k = 1; k < n; k++) {
2303 res.e(i, j) += a.e(i, k) * b.e(k, j);
2304 }
2305 }
2306 return res;
2307}
2308
2309/**
2310 * @brief Multiplication operator
2311 * @memberof Matrix_t
2312 * @details Multiplication type depends on the original types of the multiplied matrices. Defined
2313 * for the following operations.
2314 *
2315 * Matrix * Matrix
2316 *
2317 * [Standard matrix multiplication](https://en.wikipedia.org/wiki/Matrix_multiplication) where LHS
2318 * columns must match RHS rows
2319 *
2320 * \code {.cpp}
2321 * Matrix<2, 3, double> M;
2322 * Matrix<3, 2, double> N;
2323 * M.random();
2324 * N.random();
2325 * auto S = N * M; // Results in a 3 x 3 Matrix since N.rows() = 3 and M.columns = 3
2326 * \endcode
2327 *
2328 * #Vector * RowVector is same as outer product which is equivalent to a matrix
2329 * multiplication
2330 *
2331 * \code {.cpp}
2332 * auto S = W*V // Result in n x n Matrix of type MyType
2333 * \endcode
2334 *
2335 * @tparam Mt1 Matrix type for a
2336 * @tparam Mt2 Matrix type for b
2337 * @tparam n Number of rows
2338 * @tparam m Number of columns
2339 * @param a Left Matrix or Vector
2340 * @param b Right Matrix or RowVector
2341 * @return Matrix<n, m, R>
2342 */
2343template <typename Mt1, typename Mt2,
2344 std::enable_if_t<Mt1::is_matrix() && Mt2::is_matrix() && !std::is_same<Mt1, Mt2>::value,
2345 int> = 0,
2346 typename R = hila::ntype_op<hila::number_type<Mt1>, hila::number_type<Mt2>>,
2347 int n = Mt1::rows(), int m = Mt2::columns()>
2348inline Matrix<n, m, R> operator*(const Mt1 &a, const Mt2 &b) {
2349
2350 constexpr int p = Mt1::columns();
2351 static_assert(p == Mt2::rows(), "Matrix size: LHS columns != RHS rows");
2352
2353 Matrix<n, m, R> res;
2354
2355 if constexpr (n > 1 && m > 1) {
2356 for (int i = 0; i < n; i++)
2357 for (int j = 0; j < m; j++) {
2358 res.e(i, j) = a.e(i, 0) * b.e(0, j);
2359 for (int k = 1; k < p; k++) {
2360 res.e(i, j) += a.e(i, k) * b.e(k, j);
2361 }
2362 }
2363 } else if constexpr (m == 1) {
2364 // matrix x vector
2365 for (int i = 0; i < n; i++) {
2366 res.e(i) = a.e(i, 0) * b.e(0);
2367 for (int k = 1; k < p; k++) {
2368 res.e(i) += a.e(i, k) * b.e(k);
2369 }
2370 }
2371 } else if constexpr (n == 1) {
2372 // vector x matrix
2373 for (int j = 0; j < m; j++) {
2374 res.e(j) = a.e(0) * b.e(0, j);
2375 for (int k = 1; k < p; k++) {
2376 res.e(j) += a.e(k) * b.e(k, j);
2377 }
2378 }
2379 }
2380
2381 return res;
2382}
2383
2384/**
2385 * @brief Multiplication operator RowVector * Vector
2386 * @memberof Matrix_t
2387 * @details
2388 * Defined as standard [dot product](https://en.wikipedia.org/wiki/Dot_product) between
2389 * vectors as long as vectors are of same length
2390 *
2391 * \code {.cpp}
2392 * RowVector<n,MyType> V;
2393 * Vector<n,MyType> W;
2394 * //
2395 * // Fill V and W
2396 * //
2397 * auto S = V*W; // Results in MyType scalar
2398 * \endcode
2399 *
2400 * @tparam m Dimensions of RowVector
2401 * @tparam n Dimension of Vector
2402 * @tparam T1 Element type of RowVector
2403 * @tparam T2 Element type of Vector
2404 * @tparam Rt Return type of T1 \f$ \cdot \f$ T2 product
2405 * @param A RowVector to multiply
2406 * @param B Vector to multiply
2407 * @return Rt
2408 */
2409template <int m, int n, typename T1, typename T2, typename Rt = hila::ntype_op<T1, T2>>
2411
2412 static_assert(m == n, "Vector lengths do not match");
2413
2414 Rt res;
2415 res = A.e(0) * B.e(0);
2416 for (int i = 1; i < m; i++) {
2417 res += A.e(i) * B.e(i);
2418 }
2419 return res;
2420}
2421
2422///////////////////////////////////////////////////////////////////////////
2423
2424/**
2425 * @brief Multiplication operator Matrix * scalar
2426 * @memberof Matrix_t
2427 * @details Multiplication operator between Matrix and Scalar are defined in the usual way (element
2428 * wise)
2429 *
2430 * \code {.cpp}
2431 * Matrix<n,n,MyType> M;
2432 * M.fill(1);
2433 * auto S = M*2; // Resulting Matrix is uniformly 2
2434 * \endcode
2435 *
2436 * @tparam Mt Matrix type of mat
2437 * @tparam s Scalar type rhs
2438 * @param mat Matrix to multiply
2439 * @param rhs Scalar to multiply
2440 * @return Rt
2441 */
2442template <typename Mt, typename S,
2443 std::enable_if_t<(Mt::is_matrix() && hila::is_complex_or_arithmetic<S>::value), int> = 0,
2444 typename Rt = hila::mat_scalar_type<Mt, S>>
2445inline Rt operator*(const Mt &mat, const S &rhs) {
2446 Rt res;
2447 for (int i = 0; i < Rt::rows() * Rt::columns(); i++) {
2448 res.c[i] = mat.c[i] * rhs;
2449 }
2450 return res;
2451}
2452
2453/**
2454 * @brief Multiplication operator Scalar * Matrix
2455 * @memberof Matrix_t
2456 * @details Multiplication operator between Matrix and Scalar are defined in the usual way (element
2457 * wise)
2458 *
2459 * \code {.cpp}
2460 * Matrix<n,n,MyType> M;
2461 * M.fill(1);
2462 * auto S = 2*M; // Resulting Matrix is uniformly 2
2463 * \endcode
2464 *
2465 * @tparam Mt Matrix type of mat
2466 * @tparam s Scalar type rhs
2467 * @param mat Matrix to multiply
2468 * @param rhs Scalar to multiply
2469 * @return Rt
2470 */
2471template <typename Mt, typename S,
2472 std::enable_if_t<(Mt::is_matrix() && hila::is_complex_or_arithmetic<S>::value), int> = 0,
2473 typename Rt = hila::mat_scalar_type<Mt, S>>
2474inline Rt operator*(const S &rhs, const Mt &mat) {
2475 return mat * rhs; // assume commutes
2476}
2477
2478// matrix / scalar
2479
2480/**
2481 * @brief Division operator
2482 * @memberof Matrix_t
2483 * @details Defined for following operations
2484 *
2485 * __Matrix / Scalar:__
2486 *
2487 * Division operator between Matrix and Scalar are defined in the usual way (element wise)
2488 *
2489 * \code {.cpp}
2490 * Matrix<n,m,MyType> M;
2491 * M.fill(2);
2492 * auto S = M/2; // Resulting matrix is uniformly 1
2493 * \endcode
2494 *
2495 * @tparam Mt Matrix type
2496 * @tparam S Scalar type
2497 * @param mat Matrix to divide scalar with
2498 * @param rhs Scalar to divide with
2499 * @return Rt Resulting Matrix
2500 */
2501template <typename Mt, typename S,
2502 std::enable_if_t<(Mt::is_matrix() && hila::is_complex_or_arithmetic<S>::value), int> = 0,
2503 typename Rt = hila::mat_scalar_type<Mt, S>>
2504inline Rt operator/(const Mt &mat, const S &rhs) {
2505 Rt res;
2506 for (int i = 0; i < Rt::rows() * Rt::columns(); i++) {
2507 res.c[i] = mat.c[i] / rhs;
2508 }
2509 return res;
2510}
2511
2512
2513/**
2514 * @brief Returns trace of product of two matrices
2515 * @details Wrapper around Matrix::mul_trace in the form
2516 * \code {.cpp}
2517 * mul_trace(a,b) // -> a.mul_trace(b)
2518 * \endcode
2519 *
2520 * @tparam Mtype1 Matrix type for a
2521 * @tparam Mtype2 Matrix type for b
2522 * @param a Left Matrix
2523 * @param b Right Matrix
2524 * @return auto Resulting trace
2525 */
2526template <typename Mtype1, typename Mtype2,
2527 std::enable_if_t<Mtype1::is_matrix() && Mtype2::is_matrix(), int> = 0>
2528inline auto mul_trace(const Mtype1 &a, const Mtype2 &b) {
2529
2530 static_assert(Mtype1::columns() == Mtype2::rows() && Mtype1::rows() == Mtype2::columns(),
2531 "mul_trace(a,b): matrix sizes are not compatible");
2532 return a.mul_trace(b);
2533}
2534
2535//////////////////////////////////////////////////////////////////////////////////
2536
2537
2538// Stream operator
2539/**
2540 * @brief Stream operator
2541 * @memberof Matrix
2542 * @details Naive Stream operator for formatting Matrix for printing. Simply puts elements one after
2543 * the other in row major order
2544 *
2545 * @tparam n
2546 * @tparam m
2547 * @tparam T
2548 * @tparam MT
2549 * @param strm
2550 * @param A
2551 * @return std::ostream&
2552 */
2553template <int n, int m, typename T, typename MT>
2554std::ostream &operator<<(std::ostream &strm, const Matrix_t<n, m, T, MT> &A) {
2555 for (int i = 0; i < n; i++)
2556 for (int j = 0; j < m; j++) {
2557 strm << A.e(i, j);
2558 if (i < n - 1 || j < m - 1)
2559 strm << ' ';
2560 }
2561 return strm;
2562}
2563
2564/// Convert to string for "pretty" printing
2565///
2566
2567namespace hila {
2568
2569/**
2570 * @brief Converts Matrix_t object to string
2571 *
2572 * @tparam n Number of rows
2573 * @tparam m Number of columns
2574 * @tparam T Matrix element type
2575 * @tparam MT Matrix type
2576 * @param A Matrix to convert to string
2577 * @param prec Precision of T
2578 * @param separator Separator between elements
2579 * @return std::string
2580 */
2581template <int n, int m, typename T, typename MT>
2582std::string to_string(const Matrix_t<n, m, T, MT> &A, int prec = 8, char separator = ' ') {
2583 std::stringstream strm;
2584 strm.precision(prec);
2585
2586 for (int i = 0; i < n; i++)
2587 for (int j = 0; j < m; j++) {
2588 strm << hila::to_string(A.e(i, j), prec, separator);
2589 if (i < n - 1 || j < m - 1)
2590 strm << separator;
2591 }
2592 return strm.str();
2593}
2594
2595/**
2596 * @brief Formats Matrix_t object in a human readable way
2597 * @details Example 2 x 3 matrix is the following
2598 * \code {.cpp}
2599 * Matrix<2, 3, double> W;
2600 * W.random();
2601 * hila::out0 << hila::prettyprint(W ,4) << '\n';
2602 * // Result:
2603 * // [ 0.8555 0.006359 0.3193 ]
2604 * // [ 0.237 0.8871 0.8545 ]
2605 * \endcode
2606 *
2607 *
2608 * @tparam n Number of rows
2609 * @tparam m Number of columns
2610 * @tparam T Matrix element type
2611 * @tparam MT Matrix type
2612 * @param A Matrix to be formatted
2613 * @param prec Precision of Matrix element
2614 * @return std::string
2615 */
2616template <int n, int m, typename T, typename MT>
2617std::string prettyprint(const Matrix_t<n, m, T, MT> &A, int prec = 8) {
2618 std::stringstream strm;
2619 strm.precision(prec);
2620
2621 if constexpr (n == 1) {
2622 // print a vector, horizontally
2623 strm << '[';
2624 for (int i = 0; i < n * m; i++)
2625 strm << ' ' << prettyprint(A.e(i), prec);
2626 strm << " ]";
2627 // if (n > 1)
2628 // strm << "^T";
2629 } else {
2630 // now a matrix - split the matrix on lines.
2631 // do it so that columns are equal width...
2632 std::vector<std::string> lines, columns;
2633 lines.resize(n);
2634 columns.resize(n);
2635
2636 for (int i = 0; i < n; i++)
2637 lines[i] = "[ ";
2638 for (int j = 0; j < m; j++) {
2639 int size = 0;
2640 for (int i = 0; i < n; i++) {
2641 std::stringstream item;
2642 item << prettyprint(A.e(i, j), prec);
2643 columns[i] = item.str();
2644 if (columns[i].size() > size)
2645 size = columns[i].size();
2646 }
2647 for (int i = 0; i < n; i++) {
2648 lines[i].append(size - columns[i].size(), ' ');
2649 lines[i].append(columns[i]);
2650 lines[i].append(1, ' ');
2651 }
2652 }
2653 for (int i = 0; i < n - 1; i++) {
2654 strm << lines[i] << "]\n";
2655 }
2656 strm << lines[n - 1] << "]";
2657 }
2658 return strm.str();
2659}
2660
2661} // namespace hila
2662
2663/**
2664 * @brief Returns square norm of Matrix
2665 * @details Wrapper around Matrix::squarenorm - sum of squared elements
2666 *
2667 * Can be called as:
2668 * \code {.cpp}
2669 * Matrix<n,m,MyType> M;
2670 * auto a = squarenorm(M);
2671 * \endcode
2672 *
2673 * @tparam Mt Matrix type
2674 * @param rhs Matrix to compute square norm of
2675 * @return auto
2676 */
2677template <typename Mt, std::enable_if_t<Mt::is_matrix(), int> = 0>
2678inline auto squarenorm(const Mt &rhs) {
2679 return rhs.squarenorm();
2680}
2681
2682// Vector norm - sqrt of squarenorm()
2683
2684/**
2685 * @brief Returns vector norm of Matrix
2686 * @details Wrapper around Matrix::norm - sqrt of sum of squared elements
2687 * @tparam Mt Matrix type
2688 * @param rhs Matrix to compute norm of
2689 * @return auto
2690 */
2691template <typename Mt, std::enable_if_t<Mt::is_matrix(), int> = 0>
2692inline auto norm(const Mt &rhs) {
2693 return rhs.norm();
2694}
2695
2696
2697// Cast operators to different number type
2698// cast_to<double>(a);
2699
2700template <typename Ntype, typename T, int n, int m,
2701 std::enable_if_t<hila::is_arithmetic<T>::value, int> = 0>
2702Matrix<n, m, Ntype> cast_to(const Matrix<n, m, T> &mat) {
2704 for (int i = 0; i < n * m; i++)
2705 res.c[i] = mat.c[i];
2706 return res;
2707}
2708
2709template <typename Ntype, typename T, int n, int m,
2710 std::enable_if_t<hila::is_complex<T>::value, int> = 0>
2711Matrix<n, m, Complex<Ntype>> cast_to(const Matrix<n, m, T> &mat) {
2713 for (int i = 0; i < n * m; i++)
2714 res.c[i] = cast_to<Ntype>(mat.c[i]);
2715 return res;
2716}
2717
2718// Calculate exp of a square matrix
2719// Go to order ORDER in the exponential of the matrices
2720// matrices, since unitarity seems important.
2721// Evaluation is done as:
2722// exp(H) = 1 + H + H^2/2! + H^3/3! ..-
2723// = 1 + H*( 1 + (H/2)*( 1 + (H/3)*( ... )))
2724// Do it backwards in order to reduce accumulation of errors
2725
2726/**
2727 * @brief Calculate exp of a square matrix
2728 * @memberof Matrix
2729 * @details Computes up to certain order given as argument
2730 *
2731 * __Evaluation is done as__:
2732 * \f{align}{ \exp(H) &= 1 + H + \frac{H^2}{2!} + \frac{H^2}{2!} + \frac{H^3}{3!} \\
2733 * &= 1 + H\cdot(1 + (\frac{H}{2})\cdot (1 + (\frac{H}{3})\cdot(...))) \f}
2734 * Done backwards in order to reduce accumulation of errors
2735 * @tparam n Number of rowsMa
2736 * @tparam T Matrix element type
2737 * @tparam MT Matrix type
2738 * @param mat Matrix to compute exponential for
2739 * @param order order to compute exponential to
2740 * @return Matrix_t<n, m, T, MT>
2741 */
2742template <int n, int m, typename T, typename MT>
2743inline Matrix_t<n, m, T, MT> exp(const Matrix_t<n, m, T, MT> &mat, const int order = 20) {
2744 static_assert(n == m, "exp() only for square matrices");
2745
2747 hila::arithmetic_type<T> one = 1.0;
2748
2749 r = mat * (one / order) + one;
2750
2751 // doing the loop step-by-step should reduce temporaries
2752 for (int k = order - 1; k > 1; k--) {
2753 r *= mat;
2754 r *= (one / k);
2755 r += one;
2756 }
2757 r *= mat;
2758 r += one;
2759
2760 return r;
2761}
2762
2763#include "datatypes/array.h"
2764
2765#include "datatypes/diagonal_matrix.h"
2766
2767#include "datatypes/matrix_linalg.h"
2768
2769// #include "datatypes/dagger.h"
2770
2771#endif
Definition of Array class.
Array type
Definition array.h:43
Complex definition.
Definition cmplx.h:50
Define type DiagonalMatrix<n,T>
T e(const int i) const
Element access - e(i) gives diagonal element i.
The main matrix type template Matrix_t. This is a base class type for "useful" types which are deriv...
Definition matrix.h:102
Mtype & operator+=(const DiagonalMatrix< n, S > &rhs)
Addition assign operator for DiagonalMatrix to Matrix.
Definition matrix.h:897
void set_column(int c, const Vector< n, S > &v)
get column of a matrix
Definition matrix.h:415
void mult_by_2x2_left(int p, int q, const Matrix_t< 2, 2, R, Mt > &M)
Multiply -matrix from the left by matrix defined by sub matrix.
Definition matrix.h:1532
T trace() const
Computes Trace for Matrix.
Definition matrix.h:1129
Mtype & operator=(const Matrix_t< n, m, S, MT > &rhs)
Copy matrix assignment.
Definition matrix.h:601
Vector< n, T > column(int c) const
Returns column vector as value at index c.
Definition matrix.h:389
hila::arithmetic_type< T > norm() const
Calculate vector norm - sqrt of squarenorm.
Definition matrix.h:1183
void set_row(int r, const RowVector< m, S > &v)
Set row of Matrix with RowVector if types are assignable.
Definition matrix.h:374
static constexpr int rows()
Define constant methods rows(), columns() - may be useful in template code.
Definition matrix.h:220
static constexpr int columns()
Returns column length.
Definition matrix.h:228
Mtype & random()
dot with matrix - matrix
Definition matrix.h:1330
const Mtype & fill(const S rhs)
Matrix fill.
Definition matrix.h:980
void mult_by_2x2_right(int p, int q, const Matrix_t< 2, 2, R, Mt > &M)
Multiply -matrix from the right by matrix defined by sub matrix.
Definition matrix.h:1561
Mtype & operator-=(const Matrix_t< n, m, S, MT > &rhs)
Subtract assign operator Matrix to MAtrix.
Definition matrix.h:747
Rtype operator-(const S &b, const Mtype &a)
Subtraction operator Scalar - Matrix.
Definition matrix.h:2265
Mtype sort(Vector< N, int > &permutation, hila::sort order=hila::sort::ascending) const
Sort method for Vector which returns permutation order.
Definition matrix.h:1456
Matrix< n, m, hila::arithmetic_type< T > > imag() const
Returns imaginary part of Matrix or Vector.
Definition matrix.h:1114
void set_diagonal(const Vector< n, S > &v)
Set diagonal of square matrix to Vector which is passed to the method.
Definition matrix.h:454
Mtype & operator-=(const DiagonalMatrix< n, S > &rhs)
Subtract assign operator for DiagonalMatrix to Matrix.
Definition matrix.h:914
Mtype sort(hila::sort order=hila::sort::ascending) const
Sort method for Vector.
Definition matrix.h:1504
Rtype operator+(const Mtype &a, const S &b)
Addition operator Matrix + scalar.
Definition matrix.h:2158
const RowVector< n, T > & transpose() const
Transpose of vector.
Definition matrix.h:1014
Mtype & gaussian_random(double width=1.0)
Fills Matrix with gaussian random elements from gaussian distribution.
Definition matrix.h:1352
const Vector< n, T > & transpose() const
Transpose of RowVector.
Definition matrix.h:1026
Matrix< n, m, R > operator*(const Mt1 &a, const Mt2 &b)
Multiplication operator.
Definition matrix.h:2348
static constexpr int size()
Returns size of Vector or square Matrix.
Definition matrix.h:242
Mtype permute(const Vector< N, int > &permutation) const
Permute elements of vector.
Definition matrix.h:1415
Rtype operator+(const Mtype1 &a, const Mtype2 &b)
Addition operator Matrix + Matrix.
Definition matrix.h:2053
Mtype & operator=(const std::nullptr_t &z)
Zero assignment.
Definition matrix.h:621
bool operator==(const Matrix< n, m, S > &rhs) const
Boolean operator == to determine if two matrices are exactly the same.
Definition matrix.h:561
Matrix_t()=default
Define default constructors to ensure std::is_trivial.
Mtype & operator/=(const DiagonalMatrix< m, S > &rhs)
Divide assign operator for DiagonalMatrix to Matrix.
Definition matrix.h:954
Mtype conj() const
Returns complex conjugate of Matrix.
Definition matrix.h:1036
Mtype permute_rows(const Vector< n, int > &permutation) const
permute rows of Matrix
Definition matrix.h:1399
static constexpr bool is_vector()
Returns true if Matrix is a vector.
Definition matrix.h:128
T det() const
determinant function - if matrix size is < 5, use Laplace, otherwise LU
auto abs() const
Returns absolute value of Matrix.
Definition matrix.h:1081
const RowVector< m, T > & row(int r) const
Return reference to row in a matrix.
Definition matrix.h:356
Mtype & operator=(const DiagonalMatrix< n, S > &rhs)
Assignment from diagonal matrix.
Definition matrix.h:668
Matrix< n, p, R > outer_product(const Matrix< p, q, S > &rhs) const
Outer product.
Definition matrix.h:1294
T max() const
Find max of Matrix only for arithmetic types.
Definition matrix.h:1197
Mtype & operator*=(const S rhs)
Multiply assign operator scalar to Matrix.
Definition matrix.h:855
Rtype dagger() const
Hermitian conjugate of matrix.
Definition matrix.h:1052
T e(const int i) const
Standard array indexing operation for vectors.
Definition matrix.h:298
Matrix< n, m, hila::arithmetic_type< T > > real() const
Returns real part of Matrix or Vector.
Definition matrix.h:1101
bool operator!=(const Matrix< n, m, S > &rhs) const
Boolean operator != to check if matrices are exactly different.
Definition matrix.h:580
int eigen_hermitean(DiagonalMatrix< n, Et > &eigenvalues, Matrix_t< n, n, Mt, MT > &eigenvectors, enum hila::sort sorted=hila::sort::unsorted) const
Calculate eigenvalues and -vectors of an hermitean (or symmetric) matrix.
Mtype & operator=(std::initializer_list< S > rhs)
Initializer list assignment.
Definition matrix.h:695
const Mtype & operator+() const
Addition operator.
Definition matrix.h:546
Mtype & operator+=(const Matrix_t< n, m, S, MT > &rhs)
Add assign operator Matrix to Matrix.
Definition matrix.h:723
Rt operator*(const Matrix< 1, m, T1 > &A, const Matrix< n, 1, T2 > &B)
Multiplication operator RowVector * Vector.
Definition matrix.h:2410
int svd_pivot(Matrix_t< n, n, Mt, MT > &_U, DiagonalMatrix< n, Et > &_D, Matrix_t< n, n, Mt, MT > &_V, enum hila::sort sorted=hila::sort::unsorted) const
Singular value decomposition: divide matrix A = U S V*, where U,V unitary and S diagonal matrix of re...
Rtype operator+(const S &b, const Mtype &a)
Addition operator scalar + Matrix.
Definition matrix.h:2195
Mtype & operator+=(const S &rhs)
Add assign operator scalar to Matrix.
Definition matrix.h:772
T c[n *m]
The data as a one dimensional array.
Definition matrix.h:106
T min() const
Find min of Matrix only for arithmetic types.
Definition matrix.h:1210
T e(const int i, const int j) const
Standard array indexing operation for matrices.
Definition matrix.h:272
T det_laplace() const
Determinant of matrix, using Laplace method.
Mtype & operator=(const S rhs)
Assignment from scalar.
Definition matrix.h:645
Mtype & operator*=(const Matrix_t< m, p, S, MT > &rhs)
Multiply assign scalar or matrix.
Definition matrix.h:829
Mtype & operator-=(const S rhs)
Subtract assign operator scalar to Matrix.
Definition matrix.h:798
Mtype & operator/=(const S rhs)
Divide assign oprator scalar with matrix.
Definition matrix.h:881
const DiagonalMatrix< n, T > & asDiagonalMatrix() const
Cast Vector to DiagonalMatrix.
Definition matrix.h:481
Rtype operator-(const Mtype &a, const S &b)
Subtraction operator Matrix - scalar.
Definition matrix.h:2227
Rtype transpose() const
Transpose of matrix.
Definition matrix.h:997
hila::type_mul< T, S > mul_trace(const Matrix_t< p, q, S, MT > &rm) const
Multiply with given matrix and compute trace of result.
Definition matrix.h:1150
T operator[](const int i) const
Indexing operation [] defined only for vectors.
Definition matrix.h:324
int svd(Matrix_t< n, n, Mt, MT > &_U, DiagonalMatrix< n, Et > &_D, Matrix_t< n, n, Mt, MT > &_V, enum hila::sort sorted=hila::sort::unsorted) const
Singular value decomposition: divide matrix A = U S V*, where U,V unitary and S diagonal matrix of re...
Rtype operator-(const Mtype1 &a, const Mtype2 &b)
Subtraction operator Matrix - Matrix.
Definition matrix.h:2120
Mtype & operator*=(const DiagonalMatrix< m, S > &rhs)
Multiply assign operator for DiagonalMatrix to Matrix.
Definition matrix.h:933
Mtype permute_columns(const Vector< m, int > &permutation) const
permute columns of Matrix
Definition matrix.h:1385
R dot(const Matrix< p, q, S > &rhs) const
Dot product.
Definition matrix.h:1251
T det_lu() const
following calculate the determinant of a square matrix det() is the generic interface,...
const Array< n, m, T > & asArray() const
Cast Matrix to Array.
Definition matrix.h:467
Mtype operator-() const
Unary operator.
Definition matrix.h:521
DiagonalMatrix< n, T > diagonal()
Return diagonal of square matrix.
Definition matrix.h:430
std::string str(int prec=8, char separator=' ') const
Definition matrix.h:1646
Rtype adjoint() const
Adjoint of matrix.
Definition matrix.h:1069
hila::arithmetic_type< T > squarenorm() const
Calculate square norm - sum of squared elements.
Definition matrix.h:1167
static constexpr bool is_square()
Returns true if matrix is a square matrix.
Definition matrix.h:138
Matrix class which defines matrix operations.
Definition matrix.h:1679
Matrix(const std::nullptr_t &z)
Zero constructor.
Definition matrix.h:1767
Matrix(const Matrix &v)=default
Default copy constructor.
Matrix(const Matrix_t< n, m, S, MT > &rhs)
Copy constructor.
Definition matrix.h:1755
Matrix_t< n, m, T, MT > exp(const Matrix_t< n, m, T, MT > &mat, const int order=20)
Calculate exp of a square matrix.
Definition matrix.h:2743
Matrix(std::initializer_list< S > rhs)
Initializer list constructor.
Definition matrix.h:1788
hila::arithmetic_type< T > base_type
std incantation for field types
Definition matrix.h:1683
Matrix(const S rhs)
Scalar constructor.
Definition matrix.h:1725
Matrix()=default
Default constructo.
std::ostream & operator<<(std::ostream &strm, const Matrix_t< n, m, T, MT > &A)
Stream operator.
Definition matrix.h:2554
Definition of Complex types.
T arg(const Complex< T > &a)
Return argument of Complex number.
Definition cmplx.h:1334
This file defines all includes for HILA.
auto dagger(const Mtype &arg)
Return dagger of Matrix.
Definition matrix.h:1940
Mt operator*(const Mt &a, const Mt &b)
Multiplication operator Square Matrix * Square Matrix.
Definition matrix.h:2293
Rtype operator+(Mtype1 a, const Mtype2 &b)
Real micro-optimization Matrix + Matrix - no extra creation of variable and copy.
Definition matrix.h:2083
auto abs(const Mtype &arg)
Return absolute value Matrix or Vector.
Definition matrix.h:1960
auto norm(const Mt &rhs)
Returns vector norm of Matrix.
Definition matrix.h:2692
auto conj(const Mtype &arg)
Return conjugate Matrix or Vector.
Definition matrix.h:1904
auto squarenorm(const Mt &rhs)
Returns square norm of Matrix.
Definition matrix.h:2678
auto transpose(const Mtype &arg)
Return transposed Matrix or Vector.
Definition matrix.h:1884
auto real(const Mtype &arg)
Return real of Matrix or Vector.
Definition matrix.h:2001
auto imag(const Mtype &arg)
Return imaginary of Matrix or Vector.
Definition matrix.h:2021
auto trace(const Mtype &arg)
Return trace of square Matrix.
Definition matrix.h:1981
auto mul_trace(const Mtype1 &a, const Mtype2 &b)
Returns trace of product of two matrices.
Definition matrix.h:2528
auto adjoint(const Mtype &arg)
Return adjoint Matrix.
Definition matrix.h:1924
Invert diagonal + const. matrix using Sherman-Morrison formula.
Definition array.h:920
double random()
Real valued uniform random number generator.
Definition hila_gpu.cpp:120
double gaussrand()
Gaussian random generation routine.
Definition random.cpp:212
double gaussrand2(double &out2)
hila::gaussrand2 returns 2 Gaussian distributed random numbers with variance .
Definition random.cpp:177
decltype(std::declval< A >()+std::declval< B >()) type_plus
Definition type_tools.h:103
std::string to_string(const Array< n, m, T > &A, int prec=8, char separator=' ')
Converts Array object to string.
Definition array.h:934
std:swap() for Fields
Definition field.h:1847
type to store the return value of eigen_hermitean(): {E, U} where E is nxn DiagonalMatrix containing ...
Definition matrix.h:73
hila::is_complex_or_arithmetic<T>::value
Definition cmplx.h:750
type to store the return combo of svd: {U, D, V} where U and V are nxn unitary / orthogonal,...
Definition matrix.h:61