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 * @details Unpivoted rotation is generally faster than pivoted (esp. on gpu), but a bit less
439 * accurate
440 *
441 * Use:
442 * M.svd(U, S, V, [sorted]);
443 *
444 * Result satisfies M = U S V.dagger()
445 *
446 * The algorithm works by one-sided Jacobi rotations.
447 *
448 * - U = V = 1
449 * - Loop
450 * - for i=0..(n-2); j=(i+1)..(n-1)
451 * - calculate B_ii, B_ij, B_jj, (B_ij = (A^* A)_ij)
452 * - If B_ij > 0
453 * - diagonalize J^* [ B_ii B_ij ] J = diag(D_ii, D_jj)
454 * [ B_ji B_jj ]
455 * - A <- A J , Columns i and j change
456 * - V <- V J
457 * - endfor
458 * - if nothing changed, break Loop
459 * - Endloop
460 * Now A = U S, A^* A = S* S = S^2
461 * - S_ii = A_ii / |A_i| (norm of column i)
462 * - U = A / S
463 */
464
465
466#pragma hila novector
467template <int n, int m, typename T, typename Mtype>
468template <typename Et, typename Mt, typename MT>
470 out_only DiagonalMatrix<n, Et> &_S,
471 out_only Matrix_t<n, n, Mt, MT> &_V,
472 enum hila::sort sorted) const {
473
474 static_assert(!hila::contains_complex<T>::value || hila::contains_complex<Mt>::value,
475 "SVD: diagonalizing matrix must be complex with complex original matrix");
476
477 static_assert(n == m, "SVD can be solved only for square matrices");
478
479 using Dtype =
480 typename std::conditional<hila::contains_complex<T>::value, Complex<double>, double>::type;
481
482 // std::numeric_limits does not exist on cuda/gpu, so use explicit value
483 // constexpr double eps = std::numeric_limits<double>::epsilon();
484 constexpr double eps = 2.22e-16;
485
486 int rot;
489 Dtype Bpq;
490 double Bpp, Bqq;
491
492 // Do it in double prec; copy fields
493 V = 1;
494 M = (*this);
495
496 bool cont = true;
497 for (rot = 0; cont && rot < 100 * sqr(n);) {
498
499 cont = false;
500 for (int p = 0; p < n - 1; p++) {
501 bool need_pp = true;
502 Vector<n, Dtype> colp;
503 for (int q = p + 1; q < n; q++) {
504
505 double Bpp;
506 if (need_pp) {
507 colp = M.column(p);
508 Bpp = colp.squarenorm();
509 need_pp = false;
510 }
511
512 auto colq = M.column(q);
513 double Bqq = colq.squarenorm();
514
515 Dtype Bpq = colp.dot(colq);
516
517 rot++;
518
519 // check if need to rotate
520 if (::abs(Bpq) > eps * sqrt(Bpp * Bqq)) {
521 cont = true;
522 need_pp = true;
523
524 auto P = hila::linalg::diagonalize_2x2(Bpp, Bqq, Bpq);
525
526 // now do p,q rotation
527 P.mult_by_Givens_right(M, p, q); // only columns p,q change
528 P.mult_by_Givens_right(V, p, q);
529 }
530 }
531 }
532 }
533
534 // Now M = U S. Normalize columns
535
536 for (int i = 0; i < n; i++) {
537 auto col = M.column(i);
538 S.e(i) = col.norm();
539 M.set_column(i, col / S.e(i));
540 }
541
542 if (sorted == hila::sort::unsorted) {
543
544 // return values and vectors as is
545 _S = S;
546 _V = V;
547 _U = M;
548
549 } else {
550 // bubble sort eigenvalues to decreasing order
551 Vector<n, int> perm;
552 _S = S.sort(perm, sorted);
553 _V = V.permute_columns(perm);
554 _U = M.permute_columns(perm);
555 }
556 return (rot);
557}
558
559
560/**
561 * @brief svd and svd_pivot - alternative interface
562 *
563 * svd(hila::sort [optional]) returns
564 * struct svd_result<Mtype>, with fields
565 * U: nxn unitary matrix
566 * singularvalues: DiagonalMatrix of singular values (real, > 0)
567 * V: nxn unitary matrix
568 *
569 * auto res = M.svd();
570 * now res.U : nxn unitary
571 * res.singularvalues : DiagonalMatrix of singular values
572 * res.V : nxn unitary
573 *
574 * result satifies M = res.U res.singularvalues res.V.dagger()
575 */
576template <int n, int m, typename T, typename Mtype>
578
580 this->svd(res.U, res.singularvalues, res.V, sorted);
581 return res;
582}
583
584template <int n, int m, typename T, typename Mtype>
585svd_result<Mtype> Matrix_t<n, m, T, Mtype>::svd_pivot(enum hila::sort sorted) const {
586
588 this->svd_pivot(res.U, res.singularvalues, res.V, sorted);
589 return res;
590}
591
592
593namespace hila {
594namespace linalg {
595
596// templates needed for naive calculation of determinants
597template <
598 typename Mtype, std::enable_if_t<Mtype::is_matrix(), int> = 0,
599 typename Rtype = Matrix<Mtype::rows() - 1, Mtype::columns() - 1, hila::number_type<Mtype>>>
600Rtype Minor(const Mtype &bigger, int row, int col) {
601 constexpr int n = Mtype::rows();
602 constexpr int m = Mtype::columns();
603
604 Rtype result;
605 int ri = 0;
606 for (int i = 0; i < n; i++)
607 if (i != row) {
608 int ci = 0;
609 for (int j = 0; j < m; j++) {
610 if (j != col) {
611 result.e(ri, ci) = bigger.e(i, j);
612 ci++;
613 }
614 }
615 ri++;
616 }
617
618 return result;
619}
620
621} // namespace linalg
622} // namespace hila
623
624/**
625 * @brief Determinant of matrix, using Laplace method
626 * @details Defined only for square matrices
627 *
628 * For perfomance overloads exist for \f$ 1 \times 1 \f$, \f$ 2 \times 2 \f$ and \f$ 3 \times 3 \f$
629 * matrices.
630 *
631 * @param mat matrix to compute determinant for
632 * @return T result determinant
633 */
634
635#pragma hila novector
636template <int n, int m, typename T, typename Mtype>
638
639 static_assert(n == m, "determinants defined only for square matrices");
640
641 if constexpr (n == 1) {
642 return this->e(0.0);
643 }
644 if constexpr (n == 2) {
645 return this->e(0, 0) * this->e(1, 1) - this->e(1, 0) * this->e(0, 1);
646 }
647 if constexpr (n == 3) {
648 return this->e(0, 0) * (this->e(1, 1) * this->e(2, 2) - this->e(2, 1) * this->e(1, 2)) -
649 this->e(0, 1) * (this->e(1, 0) * this->e(2, 2) - this->e(1, 2) * this->e(2, 0)) +
650 this->e(0, 2) * (this->e(1, 0) * this->e(2, 1) - this->e(1, 1) * this->e(2, 0));
651 }
652
653 if constexpr (n > 3) {
654 T result(0);
655 hila::arithmetic_type<T> parity = 1, opposite = -1;
656 for (int i = 0; i < n; i++) {
657 Matrix<n - 1, m - 1, T> minor = hila::linalg::Minor(*this, 0, i);
658 result += parity * minor.det_laplace() * (*this).e(0, i);
659 parity *= opposite;
660 }
661 return result;
662 }
663}
664
665
666/**
667 * @brief Matrix determinant with LU decomposition
668 * @details Algorithm: numerical Recipes, 2nd ed. p. 47 ff
669 * Works for Real and Complex matrices
670 * Defined only for square matrices
671 *
672 * @return Complex<radix> Determinant
673 */
674
675#pragma hila novector
676template <int n, int m, typename T, typename Mtype>
678
679 static_assert(n == m, "Determinant is defined only for square matrix");
680
681 Mtype a(*this); // copy this matrix to a, modify a in place
683
684 hila::arithmetic_type<T> d = 1, big, tmp;
685 int imax = -1;
686 T ret = 0;
687
688 for (int i = 0; i < n; i++) {
689 big = 0;
690 for (int j = 0; j < n; j++) {
691 if ((tmp = ::squarenorm(a.e(i, j))) > big)
692 big = tmp;
693 }
694
695 // If full row is 0 det must be 0 too
696 if (big == 0)
697 return ret;
698
699 // scaling saved
700 vv[i] = 1.0 / sqrt(big);
701 }
702
703 // loop over rows
704 for (int j = 0; j < n; j++) {
705
706 // build the lower diagonal (L)
707 for (int i = 0; i < j; i++) {
708 for (int k = 0; k < i; k++) {
709 a.e(i, j) -= a.e(i, k) * a.e(k, j);
710 }
711 }
712
713 big = 0;
714 // search for the pivot, and start upper triangle
715 for (int i = j; i < n; i++) {
716 auto csum = a.e(i, j);
717 for (int k = 0; k < j; k++) {
718 csum -= a.e(i, k) * a.e(k, j);
719 }
720 a.e(i, j) = csum;
721 auto dum = vv[i] * ::abs(csum);
722 if (dum >= big) {
723 imax = i;
724 big = dum;
725 }
726 }
727 // and swap rows if needed
728 if (j != imax) {
729 for (int k = 0; k < n; k++) {
730 hila::swap(a.e(imax, k), a.e(j, k));
731 }
732 d = -d;
733 vv[imax] = vv[j];
734 }
735
736 // det must be zero if diag is zero now
737 if (::abs(a.e(j, j)) == 0.0)
738 return ret; // ret still 0
739
740 // and divide by the pivot
741 if (j != n - 1) {
742 auto dum = 1 / a.e(j, j);
743 for (int i = j + 1; i < n; i++) {
744 a.e(i, j) *= dum;
745 }
746 }
747 }
748
749 // form the det
750 ret = d;
751 for (int j = 0; j < n; j++) {
752 ret *= a.e(j, j);
753 }
754
755 return (ret);
756}
757
758
759/**
760 * @brief determinant function - if matrix size is < 5, use Laplace, otherwise LU
761 */
762
763#pragma hila novector
764template <int n, int m, typename T, typename Mtype>
766 static_assert(n == m, "Determinant only for square matrix");
767 if constexpr (n < 5)
768 return this->det_laplace();
769 else
770 return this->det_lu();
771}
772
773
774/**
775 * @brief function (as opposed to method) interfaces to det-functions
776 */
777
778template <int n, int m, typename T, typename Mtype>
779T det_laplace(const Matrix_t<n, m, T, Mtype> &mat) {
780 return mat.det_laplace();
781}
782
783template <int n, int m, typename T, typename Mtype>
784T det_lu(const Matrix_t<n, m, T, Mtype> &mat) {
785 return mat.det_lu();
786}
787
788template <int n, int m, typename T, typename Mtype>
789T det(const Matrix_t<n, m, T, Mtype> &mat) {
790 return mat.det();
791}
792
793
794/**
795 * @brief Invert diagonal + const. matrix using Sherman-Morrison formula
796 *
797 *
798 * Sherman-Morrison formula (generalized to complex) is
799 * (A + u v^*)^-1 = A^-1 - A^-1 u v^* A^-1/(1 + v^* A^-1 u)
800 * where A invertible matrix and u,v vectors.
801 *
802 * Specialize this here for the case where A is diagonal and
803 * u = v = sqrt(c) [1 1 1 ..]^T
804 * i.e. invert M = (A + C), where C is constant matrix. Let now B = A^-1, result is
805 * M^{-1}_ij = B_i delta_ij - c B_i B_j / (1 + c Tr B)
806 * or
807 * M^{-1} = B - c Bv Bv^T / (1 + c Tr B)
808 * where
809 * Bv = B [1 1 1 ..]^T, i.e. Bv_i = B_ii.
810 *
811 * Inverse exists if (1 + c Tr B) != 0.
812 */
813
814namespace hila {
815namespace linalg {
816
817template <int N, typename T, typename C,
818 std::enable_if_t<hila::is_complex_or_arithmetic<C>::value, int> = 0>
819auto invert_diagonal_plus_constant_matrix(const DiagonalMatrix<N, T> &D, const C c) {
820
821 DiagonalMatrix<N, T> B = 1 / D;
822 auto tmul = c / (1 + c * trace(B));
823
824 return B - tmul * B.asVector() * B.asVector().transpose();
825}
826
827} // namespace linalg
828} // namespace hila
829
830#endif // MATRIX_LINALG_H
Array< n, m, T > conj(const Array< n, m, T > &arg)
Return conjugate Array.
Definition array.h:648
hila::arithmetic_type< T > squarenorm(const Array< n, m, T > &rhs)
Return square norm of Array.
Definition array.h:957
Array< n, m, hila::arithmetic_type< T > > real(const Array< n, m, T > &arg)
Return real part of Array.
Definition array.h:662
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.
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:102
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
T det() const
determinant function - if matrix size is < 5, use Laplace, otherwise LU
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.
Definition matrix.h:272
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: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,...
hila::arithmetic_type< T > squarenorm() const
Calculate square norm - sum of squared elements.
Definition matrix.h:1167
Matrix class which defines matrix operations.
Definition matrix.h:1679
Complex< T > dagger(const Complex< T > &val)
Return dagger of Complex number.
Definition cmplx.h:1358
T abs(const Complex< T > &a)
Return absolute value of Complex number.
Definition cmplx.h:1322
constexpr T sqr(const T &arg)
Define convenience function sqr(), returning square of argument.
Definition defs.h:149
Definition of Matrix types.
Invert diagonal + const. matrix using Sherman-Morrison formula.
Definition array.h:920
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