HILA
Loading...
Searching...
No Matches
matrix_linalg.h
1#ifndef MATRIX_LINALG_H
2#define MATRIX_LINALG_H
3
4#include "datatypes/matrix.h"
5
6#include <limits>
7
8namespace hila {
9namespace linalg {
10
11
12/// @internal Find largest offdiag element of Hermitean matrix M
13
14template <int n, typename Mtype>
15inline double find_largest_offdiag(const SquareMatrix<n, Mtype> &M, int &p, int &q) {
16 double abs_mpq = -1; // guarantees a result for trivial matrix
17
18 for (int i = 0; i < n - 1; i++) {
19 for (int j = i + 1; j < n; j++) {
20 double t = ::squarenorm(M.e(i, j));
21 if (abs_mpq < t) {
22 abs_mpq = t;
23 p = i;
24 q = j;
25 }
26 }
27 }
28 return ::sqrt(abs_mpq);
29}
30
31
32/** @internal GivensMatrix is a support class for 2x2 rotations, in SU(2)
33 *
34 * Matrix is | c s |
35 * | -s* c |
36 * s complex or real, c real, and c^2 + |s|^2 = 1.
37 *
38 * mult_by_Givens_left/right multiply matrices with Givens matrix left/right.
39 * where the "Givens" is taken to be unity except on row/col = p,q
40 */
41
42template <typename Dtype>
43struct GivensMatrix {
44 Dtype s;
45 double c;
46
47 GivensMatrix dagger() {
48 GivensMatrix res;
49 res.c = c;
50 res.s = -s;
51 return res;
52 }
53
54 Vector<2, Dtype> mult_vector_left(Vector<2, Dtype> v) const {
55 auto t0 = c * v.e(0) + s * v.e(1);
56 v.e(1) = c * v.e(1) - ::conj(s) * v.e(0);
57 v.e(0) = t0;
58 return v;
59 }
60
61 RowVector<2, Dtype> mult_row_right(RowVector<2, Dtype> v) const {
62 auto t0 = v.e(0) * c - ::conj(s) * v.e(1);
63 v.e(1) = v.e(0) * s + v.e(1) * c;
64 v.e(0) = t0;
65 return v;
66 }
67
68 template <typename Mtype>
69 void mult_by_Givens_left(Mtype &M, int p, int q) const {
71 for (int i = 0; i < M.columns(); i++) {
72 a.e(0) = M.e(p, i);
73 a.e(1) = M.e(q, i);
74
75 a = this->mult_vector_left(a);
76
77 M.e(p, i) = a.e(0);
78 M.e(q, i) = a.e(1);
79 }
80 }
81
82 template <typename Mtype>
83 void mult_by_Givens_right(Mtype &M, int p, int q) const {
85 for (int i = 0; i < M.rows(); i++) {
86 a.e(0) = M.e(i, p);
87 a.e(1) = M.e(i, q);
88
89 a = this->mult_row_right(a);
90
91 M.e(i, p) = a.e(0);
92 M.e(i, q) = a.e(1);
93 }
94 }
95};
96
97
98/** @internal Do 2x2 eigenvalue analysis for hermitean matrix
99 * return 2x2 unitary/orthogonal matrix which diagonalizes the input
100 * input matrix is
101 * | mpp mpq |
102 * | mqp mqq |
103 * where mpp and mqq are real, and mqp = mpg*
104 * Name comes from the fact that this is meant to operate on rows p,q of a larger matrix
105 *
106 * | c s | p
107 * P = |-s* c* | q = Ppq
108 *
109 * with P^+ = P^-1 -> cc* + ss* = 1, det P = 1
110 * Thus, P is SU(2) or O(2) matrix
111 *
112 * | c* -s | | mpp mpq | | c s |
113 * M = | s* c | | mqp mqq | |-s* c*|
114 *
115 * = Pip* Mij Pjq, mqp = mpq*
116 *
117 * Set now Mpq = (c*mpp - s mqp)s + (c*mpq -smqq)c* = 0
118 * = c*^2 mpq - s^2 mpq* + c*s (mpp - mqq)
119 * = |mpq| [ c*^2 e - s^2 e* + c*s (mpp-mqq)/|mpq| ]
120 * where e = exp(i arg(mpq)) = mpq/|mpq|, e* = 1/e
121 *
122 * Now the "rotation angle" (c~cos\phi, s~sin\phi)
123 * a = "cot2\phi" = c*^2 e - s^2 e* / 2c*s = (mqq - mpp) / 2|mpq|
124 * Def t = s/c*e, so that the above is
125 * t^2 + 2ta - 1 = 0 ->
126 * t = -a +- sqrt(a^2+1). Choose one with smaller |t|!
127 * This is prone to cancellation, ~ a - a, so write it as
128 * t = sgn(a)/(|a| + sqrt(a^2 + 1).
129 * Now can choose real c*
130 * c* = 1/sqrt(t^2+1)
131 * s = t c* e (and c*c + s*s = 1)
132 *
133 * Parametrize the result as a Givens matrix c, s.
134 */
135
136
137template <typename Dtype>
138GivensMatrix<Dtype> diagonalize_2x2(const double mpp, const double mqq, const Dtype mpq) {
139
140 GivensMatrix<Dtype> res;
141 double mpq2 = squarenorm(mpq);
142
143 // leave / 2|mpq| away, avoid divby0
144 double a = (mqq - mpp);
145
146 // now t is above t / |mpq|
147 double t = 2.0 / (abs(a) + sqrt(a * a + 4 * mpq2));
148 if (a < 0.0)
149 t = -t;
150 res.c = 1.0 / sqrt(mpq2 * t * t + 1.0);
151 res.s = mpq * (t * res.c);
152
153 return res;
154}
155
156
157} // namespace linalg
158} // namespace hila
159
160/**
161 * @brief Calculate eigenvalues and vectors of an hermitean or real symmetric matrix
162 *
163 * Algorithm uses fully pivoted Jacobi rotations.
164 *
165 * Two interfaces:
166 *
167 * H.eigen_hermitean(E, U, [optional: sort]);
168 * E: output is DiagnoalMatrix containing real eigenvalues
169 * U: nxn unitary matrix, columns are normalized eigenvectors
170 *
171 * auto res = H.eigen_hermitean([optional: sort]);
172 * This saves the trouble of defining E and U (see below)
173 *
174 * Thus, H = U E U^* and H U = U E
175 *
176 * Both interfaces allow optional sorting according to eigenvalues:
177 * hila::sort::unsorted [default]
178 * hila::sort::ascending / descending
179 *
180 * Example:
181 * @code {.cpp}
182 * SquareMatrix<n,Complex<double>> M;
183 * M = ... // make unitary
184 * DiagonalMatrix<n,double> eigenvalues;
185 * SquareMatrix<n,Complex<double>> eigenvectors;
186 *
187 * int rotations = M.eigen_hermitean(eigenvalues,eigenvectors,hila::sort::ascending);
188 * @endcode
189 *
190 * @note The matrix has to be hermitean/symmetric
191 *
192 * @param E diagonal matrix of real eigenvalues
193 * @param U Unitary nxn matrix of eigenvectors
194 * @param sorted sorting of eigenvalues (default:unsorted)
195 * @return int number of jacobi rotations
196 */
197
198template <int n, int m, typename T, typename Mtype>
199template <typename Et, typename Mt, typename MT>
201 out_only Matrix_t<n, n, Mt, MT> &U,
202 enum hila::sort sorted) const {
203
204 static_assert(!hila::contains_complex<T>::value || hila::contains_complex<Mt>::value,
205 "Eigenvector matrix must be complex with complex original matrix");
206
207 static_assert(n == m, "Eigensystem can be solved only for square matrices");
208
209 using Dtype =
210 typename std::conditional<hila::contains_complex<T>::value, Complex<double>, double>::type;
211
212 // std::numeric_limits does not exist on cuda/gpu, so use explicit value
213 // constexpr double eps = std::numeric_limits<double>::epsilon();
214 constexpr double eps = 2.22e-16;
215
216
217 int rot;
219 DiagonalMatrix<n, double> eigenvalues;
220
221 // Do it in double prec; copy fields
222 V = 1;
223 M = (*this);
224
225 // don't need the imag. parts of diag (are zero)
226 eigenvalues = M.diagonal().real();
227
228 for (rot = 0; rot < 100 * n * n; rot++) {
229
230 /* find the largest off-diag element */
231 int p, q;
232 double abs_mpq = hila::linalg::find_largest_offdiag(M, p, q);
233
234 // if off-diag elements are tiny return
235
236 if (abs_mpq <= eps * sqrt(::abs(eigenvalues.e(p)) * ::abs(eigenvalues.e(q)))) {
237 break;
238 }
239
240 // Find diagonalizing matrix
241 auto P = hila::linalg::diagonalize_2x2(eigenvalues.e(p), eigenvalues.e(q), M.e(p, q));
242
243 P.dagger().mult_by_Givens_left(M, p, q);
244 P.mult_by_Givens_right(M, p, q);
245
246 eigenvalues.e(p) = ::real(M.e(p, p));
247 eigenvalues.e(q) = ::real(M.e(q, q));
248
249 // p,q -elements of m should be 0 - set explictly to avoid rounding erros
250 M.e(p, q) = 0;
251 M.e(q, p) = 0;
252
253 /* Now M done, take care of the ev's too ..
254 * V' = V P = |vpp vpq vpr| | c s | = V_ik P_kj
255 * |vqp vqq vqr| |-s* c |
256 * |vrp vrq vrr| | 1|
257 * vip <- vip c - viq s*
258 * viq <- vip s + viq c
259 * vir <- vir
260 */
261
262 P.mult_by_Givens_right(V, p, q);
263 }
264
265 // we usually enter here through break
266 if (sorted == hila::sort::unsorted) {
267
268 // return values and vectors as is
269 E = eigenvalues;
270 U = V;
271
272 } else {
273 // bubble sort eigenvalues to decreasing order
274 Vector<n, int> perm;
275 E = eigenvalues.sort(perm, sorted);
276 U = V.permute_columns(perm);
277 }
278 return (rot);
279}
280
281/**
282 * @brief eigenvalues and -vectors of hermitean/symmetric matrix, alternative interface
283 *
284 * result = eigen_hermitean(hila::sort [optional]) returns
285 * struct eigen_result<Mtype>, with fields
286 * eigenvalues: DiagonalMatrix of eigenvalues
287 * eigenvectors: nxn unitary matrix of eigenvectors
288 *
289 * Example:
290 * @code {.cpp}
291 * Field<SquareMatrix<n,double>> M, sn;
292 * M[ALL] = .. // make symmetric
293 *
294 * onsites(ALL) {
295 * auto R = M[X].svd();
296 *
297 * // Use EV decomposition to evaluate function: sin(M) = U sin(eigenvals) U^T
298 * sn[X] = R.eigenvectors * sin(R.eigenvalues) * R.eigenvectors.dagger();
299 * }
300 * @endcode
301 *
302 * This interface saves the trouble of defining the eigenvalue and -vector variables.
303 *
304 */
305template <int n, int m, typename T, typename Mtype>
307
309 this->eigen_hermitean(res.eigenvalues, res.eigenvectors, sorted);
310 return res;
311}
312
313
314/**
315 * @brief Singular value decomposition: divide matrix A = U S V*, where U,V unitary and S diagonal
316 * matrix of real singular values. Fully pivoted Jacobi rotations
317 *
318 * Use:
319 * M.svd_pivot(U, S, V, [sorted]);
320 *
321 * The algorithm works by one-sided Jacobi rotations.
322 *
323 * - U = V = 1
324 * - Compute Gram matrix B = A^* A. B is Hermitean, with B_ii >= 0.
325 * - Loop:
326 * - Find largest B_ij, j > i - full pivoting
327 * if |B_ij|^2 <= tol * B_ii B_jj we're done
328 * - find J to diagonalize J^* [ B_ii B_ij ] J = diag(D_ii, D_jj)
329 * [ B_ji B_jj ]
330 * - A <- A J , Columns i and j change
331 * - V <- V J
332 * - Recompute B_ik and B_jk, k /= i,j (B_ii = D_ii, B_jj = D_jj, B_ij = 0) (n^2)
333 * - Endloop
334 * Now A = U S, A^* A = S* S = S^2
335 * - S_ii = A_ii / |A_i| (norm of column i)
336 * - U = A / S
337 *
338 * @param U
339 */
340
341#pragma hila novector
342template <int n, int m, typename T, typename Mtype>
343template <typename Et, typename Mt, typename MT>
345 out_only DiagonalMatrix<n, Et> &_S,
346 out_only Matrix_t<n, n, Mt, MT> &_V,
347 enum hila::sort sorted) const {
348
349 static_assert(!hila::contains_complex<T>::value || hila::contains_complex<Mt>::value,
350 "SVD: diagonalizing matrix must be complex with complex original matrix");
351
352 static_assert(n == m, "SVD can be solved only for square matrices");
353
354 using Dtype =
355 typename std::conditional<hila::contains_complex<T>::value, Complex<double>, double>::type;
356
357 // std::numeric_limits does not exist on cuda/gpu, so use explicit value
358 // constexpr double eps = std::numeric_limits<double>::epsilon();
359 constexpr double eps = 2.22e-16;
360
361
362 int rot;
365
366 // Do it in double prec; copy fields
367 V = 1;
368 M = (*this);
369
370 // calc Gram matrix - only upper triangle really needed
371 B = (M.dagger() * M);
372
373 for (rot = 0; rot < 100 * sqr(n); rot++) {
374
375 /* find the largest element */
376 int p, q;
377 double abs_pq = hila::linalg::find_largest_offdiag(B, p, q);
378
379 // check if we're done - only very small off-diags
380 if (abs_pq <= eps * sqrt(::abs(::real(B.e(p, p)) * ::real(B.e(q, q)))))
381 break;
382
383 auto P = hila::linalg::diagonalize_2x2(::real(B.e(p, p)), ::real(B.e(q, q)), B.e(p, q));
384
385 // now do p,q rotation
386 P.mult_by_Givens_right(M, p, q); // only columns p,q change
387 P.mult_by_Givens_right(V, p, q);
388
389 // update also B - could rotate B directly but accuracy suffers. Explicit computation
390 // expensive, n^2
391 auto col = M.column(p);
392 for (int i = 0; i < n; i++) {
393 if (i != q) {
394 B.e(p, i) = col.dot(M.column(i));
395 B.e(i, p) = ::conj(B.e(p, i));
396 }
397 }
398 col = M.column(q);
399 for (int i = 0; i < n; i++) {
400 if (i != p) {
401 B.e(q, i) = col.dot(M.column(i));
402 B.e(i, q) = ::conj(B.e(q, i));
403 }
404 }
405 B.e(q, p) = 0;
406 B.e(p, q) = 0;
407 }
408
409 // Now M = U S. Normalize columns
410
411 for (int i = 0; i < n; i++) {
412 auto col = M.column(i);
413 S.e(i) = col.norm();
414 M.set_column(i, col / S.e(i));
415 }
416
417 if (sorted == hila::sort::unsorted) {
418
419 // return values and vectors as is
420 _S = S;
421 _V = V;
422 _U = M;
423
424 } else {
425 // bubble sort eigenvalues to decreasing order
426 Vector<n, int> perm;
427 _S = S.sort(perm, sorted);
428 _V = V.permute_columns(perm);
429 _U = M.permute_columns(perm);
430 }
431 return (rot);
432}
433
434/**
435 * @brief Singular value decomposition: divide matrix A = U S V*, where U,V unitary and S diagonal
436 * matrix of real singular values. Unpivoted Jacobi rotations.
437 *
438 * Unpivoted rotation is generally faster than pivoted (esp. on gpu), but a bit less accurate
439 *
440 * Use:
441 * M.svd(U, S, V, [sorted]);
442 *
443 * Result satisfies M = U S V.dagger()
444 *
445 * The algorithm works by one-sided Jacobi rotations.
446 *
447 * - U = V = 1
448 * - Loop
449 * - for i=0..(n-2); j=(i+1)..(n-1)
450 * - calculate B_ii, B_ij, B_jj, (B_ij = (A^* A)_ij)
451 * - If B_ij > 0
452 * - diagonalize J^* [ B_ii B_ij ] J = diag(D_ii, D_jj)
453 * [ B_ji B_jj ]
454 * - A <- A J , Columns i and j change
455 * - V <- V J
456 * - endfor
457 * - if nothing changed, break Loop
458 * - Endloop
459 * Now A = U S, A^* A = S* S = S^2
460 * - S_ii = A_ii / |A_i| (norm of column i)
461 * - U = A / S
462 */
463
464
465#pragma hila novector
466template <int n, int m, typename T, typename Mtype>
467template <typename Et, typename Mt, typename MT>
469 out_only DiagonalMatrix<n, Et> &_S,
470 out_only Matrix_t<n, n, Mt, MT> &_V,
471 enum hila::sort sorted) const {
472
473 static_assert(!hila::contains_complex<T>::value || hila::contains_complex<Mt>::value,
474 "SVD: diagonalizing matrix must be complex with complex original matrix");
475
476 static_assert(n == m, "SVD can be solved only for square matrices");
477
478 using Dtype =
479 typename std::conditional<hila::contains_complex<T>::value, Complex<double>, double>::type;
480
481 // std::numeric_limits does not exist on cuda/gpu, so use explicit value
482 // constexpr double eps = std::numeric_limits<double>::epsilon();
483 constexpr double eps = 2.22e-16;
484
485 int rot;
488 Dtype Bpq;
489 double Bpp, Bqq;
490
491 // Do it in double prec; copy fields
492 V = 1;
493 M = (*this);
494
495 bool cont = true;
496 for (rot = 0; cont && rot < 100 * sqr(n);) {
497
498 cont = false;
499 for (int p = 0; p < n - 1; p++) {
500 bool need_pp = true;
501 Vector<n, Dtype> colp;
502 for (int q = p + 1; q < n; q++) {
503
504 double Bpp;
505 if (need_pp) {
506 colp = M.column(p);
507 Bpp = colp.squarenorm();
508 need_pp = false;
509 }
510
511 auto colq = M.column(q);
512 double Bqq = colq.squarenorm();
513
514 Dtype Bpq = colp.dot(colq);
515
516 rot++;
517
518 // check if need to rotate
519 if (::abs(Bpq) > eps * sqrt(Bpp * Bqq)) {
520 cont = true;
521 need_pp = true;
522
523 auto P = hila::linalg::diagonalize_2x2(Bpp, Bqq, Bpq);
524
525 // now do p,q rotation
526 P.mult_by_Givens_right(M, p, q); // only columns p,q change
527 P.mult_by_Givens_right(V, p, q);
528 }
529 }
530 }
531 }
532
533 // Now M = U S. Normalize columns
534
535 for (int i = 0; i < n; i++) {
536 auto col = M.column(i);
537 S.e(i) = col.norm();
538 M.set_column(i, col / S.e(i));
539 }
540
541 if (sorted == hila::sort::unsorted) {
542
543 // return values and vectors as is
544 _S = S;
545 _V = V;
546 _U = M;
547
548 } else {
549 // bubble sort eigenvalues to decreasing order
550 Vector<n, int> perm;
551 _S = S.sort(perm, sorted);
552 _V = V.permute_columns(perm);
553 _U = M.permute_columns(perm);
554 }
555 return (rot);
556}
557
558
559/**
560 * @brief svd and svd_pivot - alternative interface
561 *
562 * svd(hila::sort [optional]) returns
563 * struct svd_result<Mtype>, with fields
564 * U: nxn unitary matrix
565 * singularvalues: DiagonalMatrix of singular values (real, > 0)
566 * V: nxn unitary matrix
567 *
568 * auto res = M.svd();
569 * now res.U : nxn unitary
570 * res.singularvalues : DiagonalMatrix of singular values
571 * res.V : nxn unitary
572 *
573 * result satifies M = res.U res.singularvalues res.V.dagger()
574 */
575template <int n, int m, typename T, typename Mtype>
577
579 this->svd(res.U, res.singularvalues, res.V, sorted);
580 return res;
581}
582
583template <int n, int m, typename T, typename Mtype>
584svd_result<Mtype> Matrix_t<n, m, T, Mtype>::svd_pivot(enum hila::sort sorted) const {
585
587 this->svd_pivot(res.U, res.singularvalues, res.V, sorted);
588 return res;
589}
590
591
592namespace hila {
593namespace linalg {
594
595// templates needed for naive calculation of determinants
596template <
597 typename Mtype, std::enable_if_t<Mtype::is_matrix(), int> = 0,
598 typename Rtype = Matrix<Mtype::rows() - 1, Mtype::columns() - 1, hila::number_type<Mtype>>>
599Rtype Minor(const Mtype &bigger, int row, int col) {
600 constexpr int n = Mtype::rows();
601 constexpr int m = Mtype::columns();
602
603 Rtype result;
604 int ri = 0;
605 for (int i = 0; i < n; i++)
606 if (i != row) {
607 int ci = 0;
608 for (int j = 0; j < m; j++) {
609 if (j != col) {
610 result.e(ri, ci) = bigger.e(i, j);
611 ci++;
612 }
613 }
614 ri++;
615 }
616
617 return result;
618}
619
620} // namespace linalg
621} // namespace hila
622
623/**
624 * @brief Determinant of matrix, using Laplace method
625 * @details Defined only for square matrices
626 *
627 * For perfomance overloads exist for \f$ 1 \times 1 \f$, \f$ 2 \times 2 \f$ and \f$ 3 \times 3 \f$
628 * matrices.
629 *
630 * @param mat matrix to compute determinant for
631 * @return T result determinant
632 */
633
634#pragma hila novector
635template <int n, int m, typename T, typename Mtype>
637
638 static_assert(n == m, "determinants defined only for square matrices");
639
640 if constexpr (n == 1) {
641 return this->e(0.0);
642 }
643 if constexpr (n == 2) {
644 return this->e(0, 0) * this->e(1, 1) - this->e(1, 0) * this->e(0, 1);
645 }
646 if constexpr (n == 3) {
647 return this->e(0, 0) * (this->e(1, 1) * this->e(2, 2) - this->e(2, 1) * this->e(1, 2)) -
648 this->e(0, 1) * (this->e(1, 0) * this->e(2, 2) - this->e(1, 2) * this->e(2, 0)) +
649 this->e(0, 2) * (this->e(1, 0) * this->e(2, 1) - this->e(1, 1) * this->e(2, 0));
650 }
651
652 if constexpr (n > 3) {
653 T result(0);
654 hila::arithmetic_type<T> parity = 1, opposite = -1;
655 for (int i = 0; i < n; i++) {
656 Matrix<n - 1, m - 1, T> minor = hila::linalg::Minor(*this, 0, i);
657 result += parity * minor.det_laplace() * (*this).e(0, i);
658 parity *= opposite;
659 }
660 return result;
661 }
662}
663
664
665/**
666 * @brief Matrix determinant with LU decomposition
667 * @details Algorithm: numerical Recipes, 2nd ed. p. 47 ff
668 * Works for Real and Complex matrices
669 * Defined only for square matrices
670 *
671 * @return Complex<radix> Determinant
672 */
673
674#pragma hila novector
675template <int n, int m, typename T, typename Mtype>
677
678 static_assert(n == m, "Determinant is defined only for square matrix");
679
680 Mtype a(*this); // copy this matrix to a, modify a in place
682
683 hila::arithmetic_type<T> d = 1, big, tmp;
684 int imax = -1;
685 T ret = 0;
686
687 for (int i = 0; i < n; i++) {
688 big = 0;
689 for (int j = 0; j < n; j++) {
690 if ((tmp = ::squarenorm(a.e(i, j))) > big)
691 big = tmp;
692 }
693
694 // If full row is 0 det must be 0 too
695 if (big == 0)
696 return ret;
697
698 // scaling saved
699 vv[i] = 1.0 / sqrt(big);
700 }
701
702 // loop over rows
703 for (int j = 0; j < n; j++) {
704
705 // build the lower diagonal (L)
706 for (int i = 0; i < j; i++) {
707 for (int k = 0; k < i; k++) {
708 a.e(i, j) -= a.e(i, k) * a.e(k, j);
709 }
710 }
711
712 big = 0;
713 // search for the pivot, and start upper triangle
714 for (int i = j; i < n; i++) {
715 auto csum = a.e(i, j);
716 for (int k = 0; k < j; k++) {
717 csum -= a.e(i, k) * a.e(k, j);
718 }
719 a.e(i, j) = csum;
720 auto dum = vv[i] * ::abs(csum);
721 if (dum >= big) {
722 imax = i;
723 big = dum;
724 }
725 }
726 // and swap rows if needed
727 if (j != imax) {
728 for (int k = 0; k < n; k++) {
729 hila::swap(a.e(imax, k), a.e(j, k));
730 }
731 d = -d;
732 vv[imax] = vv[j];
733 }
734
735 // det must be zero if diag is zero now
736 if (::abs(a.e(j, j)) == 0.0)
737 return ret; // ret still 0
738
739 // and divide by the pivot
740 if (j != n - 1) {
741 auto dum = 1 / a.e(j, j);
742 for (int i = j + 1; i < n; i++) {
743 a.e(i, j) *= dum;
744 }
745 }
746 }
747
748 // form the det
749 ret = d;
750 for (int j = 0; j < n; j++) {
751 ret *= a.e(j, j);
752 }
753
754 return (ret);
755}
756
757
758/**
759 * @brief determinant function - if matrix size is < 5, use Laplace, otherwise LU
760 */
761
762#pragma hila novector
763template <int n, int m, typename T, typename Mtype>
765 static_assert(n == m, "Determinant only for square matrix");
766 if constexpr (n < 5)
767 return this->det_laplace();
768 else
769 return this->det_lu();
770}
771
772
773/**
774 * @brief function (as opposed to method) interfaces to det-functions
775 */
776
777template <int n, int m, typename T, typename Mtype>
778T det_laplace(const Matrix_t<n, m, T, Mtype> &mat) {
779 return mat.det_laplace();
780}
781
782template <int n, int m, typename T, typename Mtype>
783T det_lu(const Matrix_t<n, m, T, Mtype> &mat) {
784 return mat.det_lu();
785}
786
787template <int n, int m, typename T, typename Mtype>
788T det(const Matrix_t<n, m, T, Mtype> &mat) {
789 return mat.det();
790}
791
792namespace hila {
793/**
794 * @brief Inversed diagnal + const. matrix using Sherman-Morrison formula
795 */
796namespace linalg {
797
798/**
799 * @details Sherman-Morrison formula (generalized to complex) is
800 * \f[
801 * (A + u v^{\dagger})^{-1} = A^{-1} - \frac{A^{-1} u v^{\dagger} A^{-1}}{(1 + v^{\dagger} A^{-1} u)},
802 \f]
803 * where \f$A\f$ is invertible matrix and \f$u\f$,\f$v\f$ are vectors with outer product \f$u v^{\dagger}\f$.
804 * Let's specialize this here for the case where \f$A\f$ is diagonal and
805 * \f[
806 * u = v = \sqrt{c} [1, 1, 1, ...]^{T}
807 * \f]
808 * i.e. the inversed matrix \f$M^{-1} = (A + C)^{-1}\f$, where \f$C = c uv^{\dagger}\f$ is constant matrix. The inversed matrix \f$ M^{-1}\f$ exists iff \f$(1 + v^{\dagger} A^{-1} u) \neq 0\f$.
809 */
810template <int N, typename T, typename C,
811 std::enable_if_t<hila::is_complex_or_arithmetic<C>::value, int> = 0>
813
814 DiagonalMatrix<N, T> B = 1 / D;
815 auto tmul = c / (1 + c * trace(B));
816
817 return B - tmul * B.asVector() * B.asVector().transpose();
818}
819
820} // namespace linalg
821} // namespace hila
822
823#endif // MATRIX_LINALG_H
Array< n, m, T > conj(const Array< n, m, T > &arg)
Return conjugate Array.
Definition array.h:674
hila::arithmetic_type< T > squarenorm(const Array< n, m, T > &rhs)
Return square norm of Array.
Definition array.h:1018
Array< n, m, T > sqrt(Array< n, m, T > a)
Square root.
Definition array.h:1039
Array< n, m, hila::arithmetic_type< T > > real(const Array< n, m, T > &arg)
Return real part of Array.
Definition array.h:688
Complex definition.
Definition cmplx.h:56
Define type DiagonalMatrix<n,T>
T e(const int i) const
Element access - e(i) gives diagonal element i.
DiagonalMatrix< n, T > sort(Vector< N, int > &permutation, hila::sort order=hila::sort::ascending) const
implement sort as casting to matrix
The main matrix type template Matrix_t. This is a base class type for "useful" types which are deriv...
Definition matrix.h:106
void set_column(int c, const Vector< n, S > &v)
get column of a matrix
Definition matrix.h:395
Vector< n, T > column(int c) const
Returns column vector as value at index c.
Definition matrix.h:368
static constexpr int rows()
Define constant methods rows(), columns() - may be useful in template code.
Definition matrix.h:226
static constexpr int columns()
Returns column length.
Definition matrix.h:234
T det() const
determinant function - if matrix size is < 5, use Laplace, otherwise LU
Rtype dagger() const
Hermitian conjugate of matrix.
Definition matrix.h:1002
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.
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...
T e(const int i, const int j) const
Standard array indexing operation for matrices and vectors.
Definition matrix.h:286
T det_laplace() const
Determinant of matrix, using Laplace method.
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...
Mtype permute_columns(const Vector< m, int > &permutation) const
permute columns of Matrix
Definition matrix.h:1347
R dot(const Matrix< p, q, S > &rhs) const
Dot product.
Definition matrix.h:1220
T det_lu() const
following calculate the determinant of a square matrix det() is the generic interface,...
DiagonalMatrix< n, T > diagonal()
Return diagonal of square matrix.
Definition matrix.h:406
hila::arithmetic_type< T > squarenorm() const
Calculate square norm - sum of squared elements.
Definition matrix.h:1117
Matrix class which defines matrix operations.
Definition matrix.h:1620
Complex< T > dagger(const Complex< T > &val)
Return dagger of Complex number.
Definition cmplx.h:1223
T abs(const Complex< T > &a)
Return absolute value of Complex number.
Definition cmplx.h:1187
constexpr T sqr(const T &arg)
Define convenience function sqr(), returning square of argument.
Definition defs.h:154
Definition of Matrix types.
auto invert_diagonal_plus_constant_matrix(const DiagonalMatrix< N, T > &D, const C c)
Implement hila::swap for gauge fields.
Definition array.h:981
type to store the return value of eigen_hermitean(): {E, U} where E is nxn DiagonalMatrix containing ...
Definition matrix.h:73
type to store the return combo of svd: {U, D, V} where U and V are nxn unitary / orthogonal,...
Definition matrix.h:61