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"
13
14/// Define type SU<n,type>
15/// Derives from square Matrix<Complex> type
16
17template <typename G>
18class Algebra;
19
20/**
21 * @brief Class for SU(N) matrix
22 * @details Class for Special unitary group SU(N).
23 *
24 * SU class is a special case inherited from Matrix_t class. SU specific or overloaded methods are:
25 *
26 * - SU::make_unitary
27 * - SU::fix_det
28 * - SU::reunitarize
29 * - SU::random
30 * - SU::project_to_algebra
31 *
32 *
33 * @tparam N Dimensionality of SU(N) matrix
34 * @tparam T Arithmetic type of Complex<T> number which SU(N) matrix elements consist of
35 */
36template <int N, typename T>
37class SU : public Matrix_t<N, N, Complex<T>, SU<N, T>> {
38
39 public:
40 // std incantation for field types
41 using base_type = T;
42 using argument_type = Complex<T>; // constructed from complex
43
44 // get all constructors from base
46 using Matrix_t<N, N, Complex<T>, SU<N, T>>::operator=;
47 using Matrix_t<N, N, Complex<T>, SU<N, T>>::operator+=;
48 using Matrix_t<N, N, Complex<T>, SU<N, T>>::operator-=;
49 using Matrix_t<N, N, Complex<T>, SU<N, T>>::operator*=;
50 using Matrix_t<N, N, Complex<T>, SU<N, T>>::operator/=;
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 c = 0; c < N; c++)
73 n2 += ::squarenorm(this->e(r, c));
74 n2 = 1.0 / sqrt(n2);
75 for (int c = 0; c < N; c++)
76 this->e(r, c) *= 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, factor;
105 T t;
106
107 d = det(*(this));
108 t = d.arg() / N;
109 factor = Complex<T>(cos(-t), sin(-t));
110 this->asArray() *= factor;
111 return *this;
112 }
113
114 /**
115 * @brief Reunitarize SU(N) matrix
116 * @details Steps to reunitarize are:
117 * 1. Make SU(N) matrix unitary
118 * 2. Fix determinant
119 *
120 * @return const SU&
121 */
122 const SU &reunitarize() {
123 make_unitary();
124 fix_det();
125 return *this;
126 }
127
128 /**
129 * @brief Generate random SU(N) matrix
130 * @details If N=2 random SU(N) matrix is generated by using Pauli matrix representation.
131 *
132 * If N > 2 then first we generate a random SU(2) matrix, and multiply the Identity matrix I(N)
133 * by this matrix using SU::mult_by_2x2_left. After this we SU::reunitarize the matrix due to
134 * numerical errors in the multiplication.
135 *
136 * @param nhits Number of times I(N) matrix is multiplied to generate random SU(N) matrix, only
137 * relevant for N > 2s
138 * @return const SU&
139 */
140 const SU &random(int nhits = 16) out_only {
141
142 // use Pauli matrix representation to generate SU(2) random matrix
143 if constexpr (N == 2) {
144 Vector<4, T> v;
145 v.gaussian_random();
146 v /= v.norm();
147 this->e(0, 0) = Complex<T>(v[0], v[3]);
148 this->e(1, 1) = Complex<T>(v[0], -v[3]);
149 this->e(0, 1) = Complex<T>(v[2], v[1]);
150 this->e(1, 0) = Complex<T>(-v[2], v[1]);
151
152 } else {
153
154 *this = 1;
155 SU<2, T> m2;
156
157 for (int h = 1; h <= nhits; h++) {
158 for (int r = 0; r < N - 1; r++)
159 for (int q = r + 1; q < N; q++) {
160 m2.random();
161 this->mult_by_2x2_left(r, q, m2);
162 }
163
164 // keep it SU(N)
165 if (h % 16 == 0) {
166 this->reunitarize();
167 }
168 }
169 if (nhits % 16 != 0)
170 this->reunitarize();
171 }
172 return *this;
173 }
174
175 ///
176 /// Project matrix to antihermitean and traceless algebra
177 /// of the group.
178 /// suN generators, normalized as
179 /// Tr(\lambda_i\lambda_j) = 1/2 \delta_ij
180 /// off-diagonal are just that:
181 /// \lambda^od_ij,r = 1/2 for elements ij and ji
182 /// \lambda^od_ij,i = i/2 for ij; -i for ji
183 ///
184 /// diagonals: su(N) has N-1 diag generators
185 /// parametrize these recursively:
186 /// \lambda_1 = diag(1,-1,0,0,..)/sqrt(1)/2
187 /// \lambda_2 = diag(1,1,-2,0,..)/sqrt(3)/2
188 /// \lambda_3 = diag(1,1,1,-3,..)/sqrt(6)/2
189 /// \lambda_i = diag(1,.. ,-i,..)/sqrt(i(i+1)/2)/2
190 /// ..
191 /// \lambda_N-1 = diag(1,.. ,1,-(N-1))/sqrt(N(N-1)/2)/2
192 ///
193 /// Define \lambda's so that diagonals come first
194 ///
195 /// Dividing U = U_ah + U_h, U_ah = 1/2 (U - U^+) = i a_i \lambda_i + tr.im I/N
196 /// => Tr \lambda_i (U_ah) = 1/2 i a_i = 1/2 (\lambda_i)_jk (u_kj - u_jk^*)
197 /// => a_i = -i (\lambda_i)_jk (u_kj - u_jk^*)
198 ///
199 /// Thus, for diags,
200 /// a_i = (u_00 + ..u_(i-1)(i-1) - i*u_ii).im 2/(sqrt(2i(i+1)))
201 ///
202 /// and off-diags:
203 /// symm: a_i = -i (u_kj - u_kj^* + u_jk - u_jk^*)/2 = -i i(u_kj.im + u_jk.im)
204 /// = (u_kj.im + u_jk.im)
205 /// antisymm: a_i = -i (i u_kj - i u_jk^* - i u_jk + i u_kj^*)/2
206 /// = (u_kj.re - u_jk.re)
207
210
211 // diagonal generators
212 T sum = this->e(0, 0).im;
213 for (int i = 1; i < N; i++) {
214 a.e(i - 1) = (sum - i * this->e(i, i).im) / sqrt(0.5 * i * (i + 1));
215 sum += this->e(i, i).im;
216 }
217
218 // Then off-diag bits
219 int k = a.n_diag;
220 for (int i = 0; i < N - 1; i++) {
221 for (int j = i + 1; j < N; j++) {
222 auto od = this->e(i, j) - this->e(j, i).conj();
223 a.e(k) = od.re;
224 a.e(k + 1) = od.im;
225 k += 2;
226 }
227 }
228
229 return a;
230 }
231};
232
233///////////////////////////////////////////////////////////
234/// Specialize Algebra type to SU(N)
235/// Derive from (real) Vector of N*N-1 elements
236
237template <int N, typename T>
238class Algebra<SU<N, T>> : public Matrix_t<N * N - 1, 1, T, Algebra<SU<N, T>>> {
239 public:
240 // std incantation for field types
241 using base_type = hila::arithmetic_type<T>;
242 using argument_type = T;
243
244 // storage for the diagonal and off-diag
245 // components of the antihermitean traceless matrix
246 static constexpr int n_offdiag = N * (N - 1);
247 static constexpr int n_diag = N - 1;
248 static constexpr int N_a = N * N - 1;
249
250 /// std constructors and operators derived from vector
251 using Matrix_t<N * N - 1, 1, T, Algebra<SU<N, T>>>::Matrix_t;
252 using Matrix_t<N * N - 1, 1, T, Algebra<SU<N, T>>>::operator=;
253 using Matrix_t<N * N - 1, 1, T, Algebra<SU<N, T>>>::operator+=;
254 using Matrix_t<N * N - 1, 1, T, Algebra<SU<N, T>>>::operator-=;
255 using Matrix_t<N * N - 1, 1, T, Algebra<SU<N, T>>>::operator*=;
256 using Matrix_t<N * N - 1, 1, T, Algebra<SU<N, T>>>::operator/=;
257
258 // suN generators, normalized as
259 // Tr(\lambda_i\lambda_j) = 1/2 \delta_ij
260 // off-diagonal are just that:
261 // \lambda^od_ij,r = 1/2 for elements ij and ji
262 // \lambda^od_ij,i = i/2 for ij; -i for ji
263 //
264 // diagonals: su(N) has N-1 diag generators
265 // parametrize these recursively:
266 // \lambda_1 = diag(1,-1,0,0,..)/sqrt(1)/2
267 // \lambda_2 = diag(1,1,-2,0,..)/sqrt(3)/2
268 // \lambda_3 = diag(1,1,1,-3,..)/sqrt(6)/2
269 // \lambda_i = diag(1,.. ,-i,..)/sqrt(i(i+1)/2)/2
270 // ..
271 // \lambda_N-1 = diag(1,.. ,1,-(N-1))/sqrt(N(N-1)/2)/2
272 //
273 // Define \lambda's so that diagonals come first
274
275 /// expand algebra to matrix rep - antihermitean
276 SU<N, T> expand() const {
277 SU<N, T> m;
278
279 Vector<N, T> d;
280
281 d.e(0) = (T)0;
282 for (int i = 1; i < N; i++) {
283 T r = this->e(i - 1) * sqrt(0.5 / (i * (i + 1)));
284 // the contributions from 1's above
285 for (int j = 0; j < i; j++)
286 d.e(j) += r;
287 // and set the negative element - no sum here, first contrib
288 d.e(i) = -i * r;
289 }
290
291 for (int i = 0; i < N; i++)
292 m.e(i, i) = Complex<T>(0, d.e(i));
293
294 int k = n_diag;
295 T inv2 = 1.0 / 2;
296 for (int i = 0; i < N - 1; i++)
297 for (int j = i + 1; j < N; j++) {
298 Complex<T> v(this->c[k] * inv2, this->c[k + 1] * inv2);
299 m.e(i, j) = v;
300 m.e(j, i) = -v.conj();
301 k += 2;
302 }
303
304 return m;
305 }
306
307
308 /// Produce gaussian random distributed algebra
309 /// element.
310 /// Set default normalisation so that the algebra matrix
311 /// ah = i h = i xi_i \lambda_i
312 /// is from distribution
313 /// exp(-Tr h^2 ) = exp(- xi_i^2 / 2 )
314 /// I.E. the coefficients of the generators have
315 /// < xi_i^2 > = 1
316 ///
317 /// Now the off-diag elements have
318 /// <|od|^2> = 1/2 = <od.re^2> + <od.im^2> = 1/4 + 1/4
319 /// 1/4 (<xi_a^2> + <xi_b^2>)
320 ///
321 /// With this convention the inherited method from Vector
322 /// is fine and the code below is not needed
323
324 // Algebra &gaussian_random() out_only {
325 //
326 // for (int i=0; i<N_a; i++) {
327 // a.e(i) = hila::gaussrand();
328 // }
329 //
330 // return *this;
331 // }
332};
333
334template <int N, typename T>
335SU<N, T> exp(const Algebra<SU<N, T>> &a) {
336 SU<N, T> m = a.expand();
337 return exp(m);
338
339 // SU<N,T> m = a.expand() * (-I); // make hermitean
340 // SquareMatrix<N,Complex<T>> D;
341 // Vector<N,T> ev;
342 // m.eigen_jacobi(ev,D);
343 // Vector<N,Complex<T>> expv;
344
345 // for (int i=0; i<N; i++) expv[i] = exp(I*ev[i]);
346 // for (int i=0; i<N; i++) for (int j=0; j<N; j++) {
347 // m.e(i,j) = D.e(i,0) * expv[0] * D.e(j,0).conj();
348 // for (int k=1; k<N; k++)
349 // m.e(i,j) += D.e(i,k) * expv[k] * D.e(j,k).conj();
350 // }
351 // return m;
352}
353
354namespace hila {
355
356///
357/// Function hila::random(SU<N,T> & m), equivalent to m.random()
358// template <int N, typename T>
359// void random(out_only SU<N, T> &m) {
360// m.random();
361// }
362
363
364} // namespace hila
365#endif
SU< N, T > expand() const
expand algebra to matrix rep - antihermitean
Definition sun_matrix.h:276
Definition su2.h:7
Complex definition.
Definition cmplx.h:50
Complex< T > conj() const
Compute conjugate of Complex number.
Definition cmplx.h:315
T arg() const
Compute argument of Complex number.
Definition cmplx.h:303
The main matrix type template Matrix_t. This is a base class type for "useful" types which are deriv...
Definition matrix.h:102
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
hila::arithmetic_type< T > norm() const
Calculate vector norm - sqrt of squarenorm.
Definition matrix.h:1183
Mtype & gaussian_random(double width=1.0)
Fills Matrix with gaussian random elements from gaussian distribution.
Definition matrix.h:1352
Matrix_t()=default
Define default constructors to ensure std::is_trivial.
SU< N, T > conj() const
Returns complex conjugate of Matrix.
Definition matrix.h:1036
Complex< T > det() const
determinant function - if matrix size is < 5, use Laplace, otherwise LU
Complex< T > c[n *m]
The data as a one dimensional array.
Definition matrix.h:106
Complex< T > e(const int i, const int j) const
Standard array indexing operation for matrices.
Definition matrix.h:272
const Array< n, m, Complex< T > > & asArray() const
Cast Matrix to Array.
Definition matrix.h:467
hila::arithmetic_type< Complex< T > > squarenorm() const
Calculate square norm - sum of squared elements.
Definition matrix.h:1167
Matrix class which defines matrix operations.
Definition matrix.h:1679
Class for SU(N) matrix.
Definition sun_matrix.h:37
const SU & random(int nhits=16)
Generate random SU(N) matrix.
Definition sun_matrix.h:140
const SU & reunitarize()
Reunitarize SU(N) matrix.
Definition sun_matrix.h:122
const SU & fix_det()
Fix determinant.
Definition sun_matrix.h:102
Algebra< SU< N, T > > project_to_algebra() const
Definition sun_matrix.h:208
const SU & make_unitary()
Makes the matrix unitary by orthogonalizing the rows.
Definition sun_matrix.h:65
Definition of Matrix types.
Invert diagonal + const. matrix using Sherman-Morrison formula.
Definition array.h:920