HILA
Loading...
Searching...
No Matches
sun_matrix.h
Go to the documentation of this file.
1/**
2 * @file sun_matrix.h
3 * @brief SU(N) Matrix definitions
4 * @details This file contains definitions for SU(N) matrix class and specialized Algebra
5 * class for SU(N) matrix.
6 */
7
8#ifndef SUN_MATRIX_H_
9#define SUN_MATRIX_H_
10
11#include "matrix.h"
12#include "su2.h"
14
15/// Define type SU<n,type>
16/// Derives from square Matrix<Complex> type
17
18template <typename G>
19class Algebra;
20
21/**
22 * @brief Class for SU(N) matrix
23 * @details Class for Special unitary group SU(N).
24 *
25 * SU class is a special case inherited from Matrix_t class. SU specific or overloaded methods are:
26 *
27 * - SU::make_unitary
28 * - SU::fix_det
29 * - SU::reunitarize
30 * - SU::random
31 * - SU::project_to_algebra
32 *
33 *
34 * @tparam N Dimensionality of SU(N) matrix
35 * @tparam T Arithmetic type of Complex<T> number which SU(N) matrix elements consist of
36 */
37template <int N, typename T>
38class SU : public Matrix_t<N, N, Complex<T>, SU<N, T>> {
39
40 public:
41 // std incantation for field types
42 using base_type = T;
43 using argument_type = Complex<T>; // constructed from complex
44
45 // get all constructors from base
47 // same with assignent, but delete rvalue assign
48 using Matrix_t<N, N, Complex<T>, SU<N, T>>::operator=;
49 SU &operator=(const SU &su) && = delete;
50
51
52 /**
53 * @brief Makes the matrix unitary by orthogonalizing the rows
54 * @details This is not the most optimal method, but it is simple:
55 *
56 * Let `SU<N> H ` be a special unitary matrix and define the indicies \f$ i \in
57 * \{0,...,N-1\} \f$. The method is as follows:
58 * 1. Normalize ` H.row(i) `
59 * 2. Make rows `H.row(i+1) ` ...` H.row(n-1)` orthogonal with respect
60 * to row `H.row(i) `
61 *
62 *
63 * @return const SU&
64 */
65 const SU &make_unitary() {
66
67 for (int r = 0; r < N; r++) {
68
69 // normalize row r
70 T n2 = 0;
71 // use here function instead of method, works for double/float too
72 for (int i = 0; i < N; i++)
73 n2 += ::squarenorm(this->e(r, i));
74 n2 = 1.0 / sqrt(n2);
75 for (int i = 0; i < N; i++)
76 this->e(r, i) *= n2;
77
78 // Now make rows r+1 .. n-1 orthogonal to row r,
79 // by doing j = j - (r^* j) r
80
81 Complex<T> d;
82 for (int j = r + 1; j < N; j++) {
83 // dot productof r^* j
84 d = 0;
85 for (int i = 0; i < N; i++) {
86 d += ::conj(this->e(r, i)) * this->e(j, i);
87 }
88 // and j -= d * r
89 for (int i = 0; i < N; i++) {
90 this->e(j, i) -= d * this->e(r, i);
91 }
92 }
93 }
94 return *this;
95 }
96
97 /**
98 * @brief Fix determinant
99 * @details Set the determinant of the SU(N) matrix to 1
100 * @return const SU&
101 */
102 const SU &fix_det() {
103
104 Complex<T> d = det(*(this));
105 T t = d.arg() / N;
106 d = Complex<T>(cos(-t), sin(-t));
107 for (int i = 0; i < N * N; i++) {
108 this->c[i] *= d;
109 }
110 return *this;
111 }
112
113 /**
114 * @brief Reunitarize SU(N) matrix
115 * @details Steps to reunitarize are:
116 * 1. Make SU(N) matrix unitary
117 * 2. Fix determinant
118 *
119 * @return const SU&
120 */
121 const SU &reunitarize() {
122 make_unitary();
123 fix_det();
124 return *this;
125 }
126
127 /**
128 * @brief Generate random SU(N) matrix
129 * @details If N=2 random SU(N) matrix is generated by using Pauli matrix representation.
130 *
131 * If N > 2 then first we generate a random SU(2) matrix, and multiply the Identity matrix I(N)
132 * by this matrix using SU::mult_by_2x2_left. After this we SU::reunitarize the matrix due to
133 * numerical errors in the multiplication.
134 *
135 * @param nhits Number of times I(N) matrix is multiplied to generate random SU(N) matrix, only
136 * relevant for N > 2s
137 * @return const SU&
138 */
139 const SU &random(int nhits = 16) out_only {
140
141 // use Pauli matrix representation to generate SU(2) random matrix
142 if constexpr (N == 2) {
143 Vector<4, T> v;
144 v.gaussian_random();
145 v /= v.norm();
146 this->e(0, 0) = Complex<T>(v[0], v[3]);
147 this->e(1, 1) = Complex<T>(v[0], -v[3]);
148 this->e(0, 1) = Complex<T>(v[2], v[1]);
149 this->e(1, 0) = Complex<T>(-v[2], v[1]);
150
151 } else {
152
153 *this = 1;
154 SU<2, T> m2;
155
156 for (int h = 1; h <= nhits; h++) {
157 for (int r = 0; r < N - 1; r++)
158 for (int q = r + 1; q < N; q++) {
159 m2.random();
160 this->mult_by_2x2_left(r, q, m2);
161 }
162
163 // keep it SU(N)
164 if (h % 16 == 0) {
165 this->reunitarize();
166 }
167 }
168 if (nhits % 16 != 0)
169 this->reunitarize();
170 }
171 return *this;
172 }
173
174 ///
175 /// Project matrix to antihermitean and traceless algebra
176 /// of the group.
177 /// suN generators, normalized as
178 /// Tr(\lambda_n \lambda_m) = -1/2 \delta_nm ,
179 /// or equivalently: Tr(\lambda^{\dagger}_n \lambda_m) = 1/2 \delta_nm
180 ///
181 /// off-diagonal are just that:
182 /// \lambda^od_nm = i/2 for elements nm and mn
183 /// \lambda^od_nm = 1/2 for nm; -1/2 for mn
184 ///
185 /// diagonals: su(N) has N-1 diag generators
186 /// parametrize these recursively:
187 /// \lambda_1 = diag(i,-i,0,0,..)/sqrt(1)/2
188 /// \lambda_2 = diag(i,i,-2i,0,..)/sqrt(3)/2
189 /// \lambda_3 = diag(i,i,i,-3i,0,..)/sqrt(6)/2
190 /// \lambda_k = diag(i,.. i,-ki,0,..)/sqrt(k(k+1)/2)/2
191 /// ..
192 /// \lambda_N-1 = diag(i,.. i,-(N-1)i)/sqrt(N(N-1)/2)/2
193 ///
194 /// Define \lambda's so that diagonals come first
195 ///
196 /// Dividing U = U_ah + U_h, U_ah = 1/2 (U - U^+) = a_k \lambda_k + tr.im I/N
197 /// => Tr(\lambda_k U_ah) = -1/2 a_k = 1/2 (\lambda_k)_lm (u_ml - u_lm^*)
198 /// => a_k = - (\lambda_k)_lm (u_ml - u_lm^*)
199 ///
200 /// Thus, for diags,
201 /// a_k = (u_00 + ..u_(k-1)(k-1) - k*u_kk).im 2/(sqrt(2k(k+1)))
202 ///
203 /// and off-diags:
204 /// symm: a_i = -i (u_kj - u_kj^* + u_jk - u_jk^*)/2 = -i i(u_kj.im + u_jk.im)
205 /// = (u_kj.im + u_jk.im)
206 /// antisymm: a_i = -i (i u_kj - i u_jk^* - i u_jk + i u_kj^*)/2
207 /// = (u_kj.re - u_jk.re)
208
210 // computes real vector a[] of Lie-algebra decomposition coefficients
211 // of A[][]=(*this) , s.t. a[i] = 2 ReTr( \lambda^{\dagger}_i A )
212
214
215 // diagonal generators
216 T sum = this->e(0, 0).im;
217 for (int i = 1; i < N; i++) {
218 a.e(i - 1) = (sum - i * this->e(i, i).im) / sqrt(0.5 * i * (i + 1));
219 sum += this->e(i, i).im;
220 }
221
222 // Then off-diag bits
223 int k = a.n_diag;
224 for (int i = 0; i < N - 1; i++) {
225 for (int j = i + 1; j < N; j++) {
226 auto od = this->e(i, j) - this->e(j, i).conj();
227 a.e(k) = od.re;
228 a.e(k + 1) = od.im;
229 k += 2;
230 }
231 }
232
233 return a;
234 }
235
236 Algebra<SU<N, T>> project_to_algebra_scaled(T scf) const {
237 // as above, but rescales the output vector by the factor scf
239
240 // diagonal generators
241 T sum = this->e(0, 0).im;
242 for (int i = 1; i < N; i++) {
243 a.e(i - 1) = scf * (sum - i * this->e(i, i).im) / sqrt(0.5 * i * (i + 1));
244 sum += this->e(i, i).im;
245 }
246
247 // Then off-diag bits
248 int k = a.n_diag;
249 for (int i = 0; i < N - 1; i++) {
250 for (int j = i + 1; j < N; j++) {
251 auto od = this->e(i, j) - this->e(j, i).conj();
252 a.e(k) = scf * od.re;
253 a.e(k + 1) = scf * od.im;
254 k += 2;
255 }
256 }
257
258 return a;
259 }
260
261 Algebra<SU<N, T>> project_to_algebra(out_only T &onenorm) const {
262 // computes and returns vector a[] of real-valued Lie-algebra
263 // projection coefficients and sets in addition onenorm
264 // to be the 1-norm of a[]
266
267 onenorm = 0;
268 // diagonal generators
269 T sum = this->e(0, 0).im;
270 for (int i = 1; i < N; i++) {
271 a.e(i - 1) = (sum - i * this->e(i, i).im) / sqrt(0.5 * i * (i + 1));
272 sum += this->e(i, i).im;
273 onenorm += abs(a.e(i - 1));
274 }
275
276 // Then off-diag bits
277 int k = a.n_diag;
278 for (int i = 0; i < N - 1; i++) {
279 for (int j = i + 1; j < N; j++) {
280 auto od = this->e(i, j) - this->e(j, i).conj();
281 a.e(k) = od.re;
282 a.e(k + 1) = od.im;
283 onenorm += abs(a.e(k)) + abs(a.e(k + 1));
284 k += 2;
285 }
286 }
287
288 return a;
289 }
290};
291
292///////////////////////////////////////////////////////////
293/// Specialize Algebra type to SU(N)
294/// Derive from (real) Vector of N*N-1 elements
295
296template <int N, typename T>
297class Algebra<SU<N, T>> : public Matrix_t<N * N - 1, 1, T, Algebra<SU<N, T>>> {
298 public:
299 // std incantation for field types
300 using base_type = hila::arithmetic_type<T>;
301 using argument_type = T;
302
303 // storage for the diagonal and off-diag
304 // components of the antihermitean traceless matrix
305 static constexpr int n_offdiag = N * (N - 1);
306 static constexpr int n_diag = N - 1;
307 static constexpr int N_a = N * N - 1;
308
309 /// std constructors and operators derived from vector
310 using Matrix_t<N * N - 1, 1, T, Algebra<SU<N, T>>>::Matrix_t;
311 using Matrix_t<N * N - 1, 1, T, Algebra<SU<N, T>>>::operator=;
312 Algebra &operator=(const Algebra &a) && = delete;
313
314 // suN generators, normalized as
315 // Tr(\lambda_i \lambda_j) = -1/2 \delta_ij ,
316 // or equivalently: Tr(\lambda_i \lambda_j) = 1/2 \delta_ij
317 // off-diagonal are just that:
318 // \lambda^od_ij,r = 1/2 for elements ij and ji
319 // \lambda^od_ij,i = i/2 for ij; -i for ji
320 //
321 // diagonals: su(N) has N-1 diag generators
322 // parametrize these recursively:
323 // \lambda_1 = diag(1,-1,0,0,..)/sqrt(1)/2
324 // \lambda_2 = diag(1,1,-2,0,..)/sqrt(3)/2
325 // \lambda_3 = diag(1,1,1,-3,..)/sqrt(6)/2
326 // \lambda_i = diag(1,.. ,-i,..)/sqrt(i(i+1)/2)/2
327 // ..
328 // \lambda_N-1 = diag(1,.. ,1,-(N-1))/sqrt(N(N-1)/2)/2
329 //
330 // Define \lambda's so that diagonals come first
331
332 /// expand algebra to matrix rep - antihermitean
333 SU<N, T> expand() const {
334 SU<N, T> m;
335
336 Vector<N, T> d;
337
338 d.e(0) = (T)0;
339 for (int i = 1; i < N; i++) {
340 T r = this->c[i - 1] * sqrt(0.5 / (i * (i + 1)));
341 // the contributions from 1's above
342 for (int j = 0; j < i; j++)
343 d.e(j) += r;
344 // and set the negative element - no sum here, first contrib
345 d.e(i) = -i * r;
346 }
347
348 for (int i = 0; i < N; i++)
349 m.e(i, i) = Complex<T>(0, d.e(i));
350
351 int k = n_diag;
352 T inv2 = 0.5;
353 for (int i = 0; i < N - 1; i++)
354 for (int j = i + 1; j < N; j++) {
355 Complex<T> v(this->c[k] * inv2, this->c[k + 1] * inv2);
356 m.e(i, j) = v;
357 m.e(j, i) = -v.conj();
358 k += 2;
359 }
360
361 return m;
362 }
363
364 /// expand algebra to scaled matrix rep - antihermitian
365 SU<N, T> expand_scaled(T scf) const {
366 SU<N, T> m;
367
368 Vector<N, T> d;
369
370 d.e(0) = (T)0;
371 for (int i = 1; i < N; i++) {
372 T r = this->c[i - 1] * sqrt(0.5 / (i * (i + 1)));
373 // the contributions from 1's above
374 for (int j = 0; j < i; j++)
375 d.e(j) += r;
376 // and set the negative element - no sum here, first contrib
377 d.e(i) = -i * r;
378 }
379
380 for (int i = 0; i < N; i++)
381 m.e(i, i) = Complex<T>(0, scf * d.e(i));
382
383 int k = n_diag;
384 T inv2 = 0.5 * scf;
385 for (int i = 0; i < N - 1; i++)
386 for (int j = i + 1; j < N; j++) {
387 Complex<T> v(this->c[k] * inv2, this->c[k + 1] * inv2);
388 m.e(i, j) = v;
389 m.e(j, i) = -v.conj();
390 k += 2;
391 }
392
393 return m;
394 }
395
396
397 /// Produce gaussian random distributed algebra
398 /// element.
399 /// Set default normalisation so that the algebra matrix
400 /// ah = i h = i xi_i \lambda_i
401 /// is from distribution
402 /// exp(-Tr h^2 ) = exp(- xi_i^2 / 2 )
403 /// I.E. the coefficients of the generators have
404 /// < xi_i^2 > = 1
405 ///
406 /// Now the off-diag elements have
407 /// <|od|^2> = 1/2 = <od.re^2> + <od.im^2> = 1/4 + 1/4
408 /// 1/4 (<xi_a^2> + <xi_b^2>)
409 ///
410 /// With this convention the inherited method from Vector
411 /// is fine and the code below is not needed
412
413 // Algebra &gaussian_random() out_only {
414 //
415 // for (int i=0; i<N_a; i++) {
416 // a.e(i) = hila::gaussrand();
417 // }
418 //
419 // return *this;
420 // }
421 // Wrapper for base class' gaussian_random with appropriate
422 // default gaussian width for chosen algebra normalization
423 Algebra &gaussian_random(T width = sqrt(2.0)) out_only {
424 Matrix_t<N * N - 1, 1, T, Algebra<SU<N, T>>>::gaussian_random(width);
425 return *this;
426 }
427
428 // dot product of two Algebra vectors
429 T dot(const Algebra &rhs) const {
430 T r = 0.0;
431 for (int i = 0; i < N_a; ++i) {
432 r += this->e(i) * rhs.e(i);
433 }
434 return -r * 0.5;
435 }
436};
437
438template <int N, typename T>
439SU<N, T> exp(const Algebra<SU<N, T>> &a) {
440 SU<N, T> m = a.expand();
441 return exp(m);
442
443 // SU<N,T> m = a.expand() * (-I); // make hermitean
444 // SquareMatrix<N,Complex<T>> D;
445 // Vector<N,T> ev;
446 // m.eigen_jacobi(ev,D);
447 // Vector<N,Complex<T>> expv;
448
449 // for (int i=0; i<N; i++) expv[i] = exp(I*ev[i]);
450 // for (int i=0; i<N; i++) for (int j=0; j<N; j++) {
451 // m.e(i,j) = D.e(i,0) * expv[0] * D.e(j,0).conj();
452 // for (int k=1; k<N; k++)
453 // m.e(i,j) += D.e(i,k) * expv[k] * D.e(j,k).conj();
454 // }
455 // return m;
456}
457
458
459// overload of matrix exponential with iterative Cayley-Hamilton (ch) defined in matrix.h.
460template <int N, typename T>
461SU<N, T> chexp(const Algebra<SU<N, T>> &a) {
462 SU<N, T> m = a.expand();
463 return chexp(m);
464}
465
466
467// overload of matrix exponential with iterative Cayley-Hamilton using
468// "chs" implementation (defined in matrix.h) which needs less temporary
469// memory, but is a bit slower.
470template <int N, typename T>
471SU<N, T> chsexp(const Algebra<SU<N, T>> &a) {
472 SU<N, T> m = a.expand();
473 return chsexp(m);
474}
475
476
477// logarithm of SU(N) matrix with iterative Cayley-Hamilton
478template <int N, typename T>
479Algebra<SU<N, T>> log(const SU<N, T> &a) {
480 int maxit = 5 * N;
481 T fprec = fp<T>::epsilon * 10.0 * Algebra<SU<N, T>>::N_a;
483
484 SU<N, T> tmat = a, tmat2;
485 Algebra<SU<N, T>> res = 0, tres;
486 T trn, rn;
487 int it, i;
488 for (it = 0; it < maxit; ++it) {
489 tres = tmat.project_to_algebra(trn);
490 rn = 0;
491 for (i = 0; i < Algebra<SU<N, T>>::N_a; ++i) {
492 res.e(i) += tres.e(i);
493 rn += abs(res.e(i));
494 }
495 if (trn < fprec * (rn + 1.0)) {
496 break;
497 }
498 tmat = res.expand_scaled(-1.0);
499 chexp(tmat, tmat2, pl);
500 mult(a, tmat2, tmat);
501 }
502
503 return res;
504}
505
506
507namespace hila {
508
509///
510/// Function hila::random(SU<N,T> & m), equivalent to m.random()
511// template <int N, typename T>
512// void random(out_only SU<N, T> &m) {
513// m.random();
514// }
515
516
517} // namespace hila
518#endif
Array< n, m, T > exp(Array< n, m, T > a)
Exponential.
Definition array.h:1059
Array< n, m, T > cos(Array< n, m, T > a)
Cosine.
Definition array.h:1089
Array< n, m, T > sqrt(Array< n, m, T > a)
Square root.
Definition array.h:1039
Array< n, m, T > sin(Array< n, m, T > a)
Sine.
Definition array.h:1079
SU< N, T > expand_scaled(T scf) const
expand algebra to scaled matrix rep - antihermitian
Definition sun_matrix.h:365
SU< N, T > expand() const
expand algebra to matrix rep - antihermitean
Definition sun_matrix.h:333
Algebra & gaussian_random(T width=sqrt(2.0))
Definition sun_matrix.h:423
Definition su2.h:7
Complex definition.
Definition cmplx.h:56
Complex< T > conj() const
Compute conjugate of Complex number.
Definition cmplx.h:282
T arg() const
Compute argument of Complex number.
Definition cmplx.h:270
The main matrix type template Matrix_t. This is a base class type for "useful" types which are deriv...
Definition matrix.h:106
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:1473
hila::arithmetic_type< T > norm() const
Calculate vector norm - sqrt of squarenorm.
Definition matrix.h:1133
Matrix_t()=default
Define default constructors to ensure std::is_trivial.
SU< N, T > conj() const
Returns complex conjugate of Matrix.
Definition matrix.h:986
Complex< 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:1031
Complex< T > c[n *m]
The data as a one dimensional array.
Definition matrix.h:110
Mtype & gaussian_random(base_type width=1.0)
Fills Matrix with gaussian random elements.
Definition matrix.h:1314
Complex< T > e(const int i, const int j) const
Standard array indexing operation for matrices and vectors.
Definition matrix.h:286
hila::arithmetic_type< Complex< T > > squarenorm() const
Calculate square norm - sum of squared elements.
Definition matrix.h:1117
Matrix class which defines matrix operations.
Definition matrix.h:1620
Class for SU(N) matrix.
Definition sun_matrix.h:38
const SU & random(int nhits=16)
Generate random SU(N) matrix.
Definition sun_matrix.h:139
const SU & reunitarize()
Reunitarize SU(N) matrix.
Definition sun_matrix.h:121
const SU & fix_det()
Fix determinant.
Definition sun_matrix.h:102
Algebra< SU< N, T > > project_to_algebra() const
Definition sun_matrix.h:209
const SU & make_unitary()
Makes the matrix unitary by orthogonalizing the rows.
Definition sun_matrix.h:65
T abs(const Complex< T > &a)
Return absolute value of Complex number.
Definition cmplx.h:1187
Definition of Matrix types.
Matrix_t< n, m, T, MT > chsexp(const Matrix_t< n, m, T, MT > &mat)
Calculate exp of a square matrix.
Definition matrix.h:2723
void chexp(const Matrix_t< n, m, T, MT > &mat, Matrix_t< n, m, T, MT > &omat, Matrix_t< n, m, T, MT >(&pl)[n+1])
Calculate exp of a square matrix.
Definition matrix.h:2579
void mult(const Mt &a, const Mt &b, Mt &res)
compute product of two square matrices and write result to existing matrix
Definition matrix.h:2327
Implement hila::swap for gauge fields.
Definition array.h:981
logger_class log
Now declare the logger.