/************************************************************************************* Grid physics library, www.github.com/paboyle/Grid Source file: ./lib/qcd/utils/SUn.h Copyright (C) 2015 Author: Azusa Yamaguchi Author: Peter Boyle Author: neo Author: paboyle This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. See the full license in the file "LICENSE" in the top level distribution directory *************************************************************************************/ /* END LEGAL */ #ifndef QCD_UTIL_SUN_H #define QCD_UTIL_SUN_H namespace Grid { namespace QCD { template class SU { public: static const int Dimension = ncolour; static const int AdjointDimension = ncolour * ncolour - 1; static int su2subgroups(void) { return (ncolour * (ncolour - 1)) / 2; } template using iSUnMatrix = iScalar > >; template using iSU2Matrix = iScalar > >; template using iSUnAlgebraVector = iScalar > >; ////////////////////////////////////////////////////////////////////////////////////////////////// // Types can be accessed as SU<2>::Matrix , SU<2>::vSUnMatrix, // SU<2>::LatticeMatrix etc... ////////////////////////////////////////////////////////////////////////////////////////////////// typedef iSUnMatrix Matrix; typedef iSUnMatrix MatrixF; typedef iSUnMatrix MatrixD; typedef iSUnMatrix vMatrix; typedef iSUnMatrix vMatrixF; typedef iSUnMatrix vMatrixD; // For the projectors to the algebra // these should be real... // keeping complex for consistency with the SIMD vector types typedef iSUnAlgebraVector AlgebraVector; typedef iSUnAlgebraVector AlgebraVectorF; typedef iSUnAlgebraVector AlgebraVectorD; typedef iSUnAlgebraVector vAlgebraVector; typedef iSUnAlgebraVector vAlgebraVectorF; typedef iSUnAlgebraVector vAlgebraVectorD; typedef Lattice LatticeMatrix; typedef Lattice LatticeMatrixF; typedef Lattice LatticeMatrixD; typedef Lattice LatticeAlgebraVector; typedef Lattice LatticeAlgebraVectorF; typedef Lattice LatticeAlgebraVectorD; typedef iSU2Matrix SU2Matrix; typedef iSU2Matrix SU2MatrixF; typedef iSU2Matrix SU2MatrixD; typedef iSU2Matrix vSU2Matrix; typedef iSU2Matrix vSU2MatrixF; typedef iSU2Matrix vSU2MatrixD; typedef Lattice LatticeSU2Matrix; typedef Lattice LatticeSU2MatrixF; typedef Lattice LatticeSU2MatrixD; //////////////////////////////////////////////////////////////////////// // There are N^2-1 generators for SU(N). // // We take a traceless hermitian generator basis as follows // // * Normalisation: trace ta tb = 1/2 delta_ab = T_F delta_ab // T_F = 1/2 for SU(N) groups // // * Off diagonal // - pairs of rows i1,i2 behaving like pauli matrices signma_x, sigma_y // // - there are (Nc-1-i1) slots for i2 on each row [ x 0 x ] // direct count off each row // // - Sum of all pairs is Nc(Nc-1)/2: proof arithmetic series // // (Nc-1) + (Nc-2)+... 1 ==> Nc*(Nc-1)/2 // 1+ 2+ + + Nc-1 // // - There are 2 x Nc (Nc-1)/ 2 of these = Nc^2 - Nc // // - We enumerate the row-col pairs. // - for each row col pair there is a (sigma_x) and a (sigma_y) like // generator // // // t^a_ij = { in 0.. Nc(Nc-1)/2 -1} => 1/2(delta_{i,i1} delta_{j,i2} + // delta_{i,i1} delta_{j,i2}) // t^a_ij = { in Nc(Nc-1)/2 ... Nc(Nc-1) - 1} => i/2( delta_{i,i1} // delta_{j,i2} - i delta_{i,i1} delta_{j,i2}) // // * Diagonal; must be traceless and normalised // - Sequence is // N (1,-1,0,0...) // N (1, 1,-2,0...) // N (1, 1, 1,-3,0...) // N (1, 1, 1, 1,-4,0...) // // where 1/2 = N^2 (1+.. m^2)etc.... for the m-th diagonal generator // NB this gives the famous SU3 result for su2 index 8 // // N= sqrt(1/2 . 1/6 ) = 1/2 . 1/sqrt(3) // // ( 1 ) // ( 1 ) / sqrt(3) /2 = 1/2 lambda_8 // ( -2) // //////////////////////////////////////////////////////////////////////// template static void generator(int lieIndex, iSUnMatrix &ta) { // map lie index to which type of generator int diagIndex; int su2Index; int sigxy; int NNm1 = ncolour * (ncolour - 1); if (lieIndex >= NNm1) { diagIndex = lieIndex - NNm1; generatorDiagonal(diagIndex, ta); return; } sigxy = lieIndex & 0x1; // even or odd su2Index = lieIndex >> 1; if (sigxy) generatorSigmaY(su2Index, ta); else generatorSigmaX(su2Index, ta); } template static void generatorSigmaY(int su2Index, iSUnMatrix &ta) { ta = zero; int i1, i2; su2SubGroupIndex(i1, i2, su2Index); ta()()(i1, i2) = 1.0; ta()()(i2, i1) = 1.0; ta = ta * 0.5; } template static void generatorSigmaX(int su2Index, iSUnMatrix &ta) { ta = zero; cplx i(0.0, 1.0); int i1, i2; su2SubGroupIndex(i1, i2, su2Index); ta()()(i1, i2) = i; ta()()(i2, i1) = -i; ta = ta * 0.5; } template static void generatorDiagonal(int diagIndex, iSUnMatrix &ta) { // diag ({1, 1, ..., 1}(k-times), -k, 0, 0, ...) ta = zero; int k = diagIndex + 1; // diagIndex starts from 0 for (int i = 0; i <= diagIndex; i++) { // k iterations ta()()(i, i) = 1.0; } ta()()(k, k) = -k; // indexing starts from 0 RealD nrm = 1.0 / std::sqrt(2.0 * k * (k + 1)); ta = ta * nrm; } //////////////////////////////////////////////////////////////////////// // Map a su2 subgroup number to the pair of rows that are non zero //////////////////////////////////////////////////////////////////////// static void su2SubGroupIndex(int &i1, int &i2, int su2_index) { assert((su2_index >= 0) && (su2_index < (ncolour * (ncolour - 1)) / 2)); int spare = su2_index; for (i1 = 0; spare >= (ncolour - 1 - i1); i1++) { spare = spare - (ncolour - 1 - i1); // remove the Nc-1-i1 terms } i2 = i1 + 1 + spare; } ////////////////////////////////////////////////////////////////////////////////////////// // Pull out a subgroup and project on to real coeffs x pauli basis ////////////////////////////////////////////////////////////////////////////////////////// template static void su2Extract(Lattice > &Determinant, Lattice > &subgroup, const Lattice > &source, int su2_index) { GridBase *grid(source._grid); conformable(subgroup, source); conformable(subgroup, Determinant); int i0, i1; su2SubGroupIndex(i0, i1, su2_index); PARALLEL_FOR_LOOP for (int ss = 0; ss < grid->oSites(); ss++) { subgroup._odata[ss]()()(0, 0) = source._odata[ss]()()(i0, i0); subgroup._odata[ss]()()(0, 1) = source._odata[ss]()()(i0, i1); subgroup._odata[ss]()()(1, 0) = source._odata[ss]()()(i1, i0); subgroup._odata[ss]()()(1, 1) = source._odata[ss]()()(i1, i1); iSU2Matrix Sigma = subgroup._odata[ss]; Sigma = Sigma - adj(Sigma) + trace(adj(Sigma)); subgroup._odata[ss] = Sigma; // this should be purely real Determinant._odata[ss] = Sigma()()(0, 0) * Sigma()()(1, 1) - Sigma()()(0, 1) * Sigma()()(1, 0); } } ////////////////////////////////////////////////////////////////////////////////////////// // Set matrix to one and insert a pauli subgroup ////////////////////////////////////////////////////////////////////////////////////////// template static void su2Insert(const Lattice > &subgroup, Lattice > &dest, int su2_index) { GridBase *grid(dest._grid); conformable(subgroup, dest); int i0, i1; su2SubGroupIndex(i0, i1, su2_index); dest = 1.0; // start out with identity PARALLEL_FOR_LOOP for (int ss = 0; ss < grid->oSites(); ss++) { dest._odata[ss]()()(i0, i0) = subgroup._odata[ss]()()(0, 0); dest._odata[ss]()()(i0, i1) = subgroup._odata[ss]()()(0, 1); dest._odata[ss]()()(i1, i0) = subgroup._odata[ss]()()(1, 0); dest._odata[ss]()()(i1, i1) = subgroup._odata[ss]()()(1, 1); } } /////////////////////////////////////////////// // Generate e^{ Re Tr Staple Link} dlink // // *** Note Staple should be appropriate linear compbination between all // staples. // *** If already by beta pass coefficient 1.0. // *** This routine applies the additional 1/Nc factor that comes after trace // in action. // /////////////////////////////////////////////// static void SubGroupHeatBath( GridSerialRNG &sRNG, GridParallelRNG &pRNG, RealD beta, // coeff multiplying staple in action (with no 1/Nc) LatticeMatrix &link, const LatticeMatrix &barestaple, // multiplied by action coeffs so th int su2_subgroup, int nheatbath, LatticeInteger &wheremask) { GridBase *grid = link._grid; int ntrials = 0; int nfails = 0; const RealD twopi = 2.0 * M_PI; LatticeMatrix staple(grid); staple = barestaple * (beta / ncolour); LatticeMatrix V(grid); V = link * staple; // Subgroup manipulation in the lie algebra space LatticeSU2Matrix u( grid); // Kennedy pendleton "u" real projected normalised Sigma LatticeSU2Matrix uinv(grid); LatticeSU2Matrix ua(grid); // a in pauli form LatticeSU2Matrix b(grid); // rotated matrix after hb // Some handy constant fields LatticeComplex ones(grid); ones = 1.0; LatticeComplex zeros(grid); zeros = zero; LatticeReal rones(grid); rones = 1.0; LatticeReal rzeros(grid); rzeros = zero; LatticeComplex udet(grid); // determinant of real(staple) LatticeInteger mask_true(grid); mask_true = 1; LatticeInteger mask_false(grid); mask_false = 0; /* PLB 156 P393 (1985) (Kennedy and Pendleton) Note: absorb "beta" into the def of sigma compared to KP paper; staple passed to this routine has "beta" already multiplied in Action linear in links h and of form: beta S = beta Sum_p (1 - 1/Nc Re Tr Plaq ) Writing Sigma = 1/Nc (beta Sigma') where sum over staples is "Sigma' " beta S = const - beta/Nc Re Tr h Sigma' = const - Re Tr h Sigma Decompose h and Sigma into (1, sigma_j) ; h_i real, h^2=1, Sigma_i complex arbitrary. Tr h Sigma = h_i Sigma_j Tr (sigma_i sigma_j) = h_i Sigma_j 2 delta_ij Re Tr h Sigma = 2 h_j Re Sigma_j Normalised re Sigma_j = xi u_j With u_j a unit vector and U can be in SU(2); Re Tr h Sigma = 2 h_j Re Sigma_j = 2 xi (h.u) 4xi^2 = Det [ Sig - Sig^dag + 1 Tr Sigdag] u = 1/2xi [ Sig - Sig^dag + 1 Tr Sigdag] xi = sqrt(Det)/2; Write a= u h in SU(2); a has pauli decomp a_j; Note: Product b' xi is unvariant because scaling Sigma leaves normalised vector "u" fixed; Can rescale Sigma so b' = 1. */ //////////////////////////////////////////////////////// // Real part of Pauli decomposition // Note a subgroup can project to zero in cold start //////////////////////////////////////////////////////// su2Extract(udet, u, V, su2_subgroup); ////////////////////////////////////////////////////// // Normalising this vector if possible; else identity ////////////////////////////////////////////////////// LatticeComplex xi(grid); LatticeSU2Matrix lident(grid); SU2Matrix ident = Complex(1.0); SU2Matrix pauli1; SU<2>::generator(0, pauli1); SU2Matrix pauli2; SU<2>::generator(1, pauli2); SU2Matrix pauli3; SU<2>::generator(2, pauli3); pauli1 = timesI(pauli1) * 2.0; pauli2 = timesI(pauli2) * 2.0; pauli3 = timesI(pauli3) * 2.0; LatticeComplex cone(grid); LatticeReal adet(grid); adet = abs(toReal(udet)); lident = Complex(1.0); cone = Complex(1.0); Real machine_epsilon = 1.0e-7; u = where(adet > machine_epsilon, u, lident); udet = where(adet > machine_epsilon, udet, cone); xi = 0.5 * sqrt(udet); // 4xi^2 = Det [ Sig - Sig^dag + 1 Tr Sigdag] u = 0.5 * u * pow(xi, -1.0); // u = 1/2xi [ Sig - Sig^dag + 1 Tr Sigdag] // Debug test for sanity uinv = adj(u); b = u * uinv - 1.0; assert(norm2(b) < 1.0e-4); /* Measure: Haar measure dh has d^4a delta(1-|a^2|) In polars: da = da0 r^2 sin theta dr dtheta dphi delta( 1 - r^2 -a0^2) = da0 r^2 sin theta dr dtheta dphi delta( (sqrt(1-a0^) - r)(sqrt(1-a0^) + r) ) = da0 r/2 sin theta dr dtheta dphi delta( (sqrt(1-a0^) - r) ) Action factor Q(h) dh = e^-S[h] dh = e^{ xi Tr uh} dh // beta enters through xi = e^{2 xi (h.u)} dh = e^{2 xi h0u0}.e^{2 xi h1u1}.e^{2 xi h2u2}.e^{2 xi h3u3} dh Therefore for each site, take xi for that site i) generate |a0|<1 with dist (1-a0^2)^0.5 e^{2 xi a0 } da0 Take alpha = 2 xi = 2 xi [ recall 2 beta/Nc unmod staple norm]; hence 2.0/Nc factor in Chroma ] A. Generate two uniformly distributed pseudo-random numbers R and R', R'', R''' in the unit interval; B. Set X = -(ln R)/alpha, X' =-(ln R')/alpha; C. Set C = cos^2(2pi R"), with R" another uniform random number in [0,1] ; D. Set A = XC; E. Let d = X'+A; F. If R'''^2 :> 1 - 0.5 d, go back to A; G. Set a0 = 1 - d; Note that in step D setting B ~ X - A and using B in place of A in step E will generate a second independent a 0 value. */ ///////////////////////////////////////////////////////// // count the number of sites by picking "1"'s out of hat ///////////////////////////////////////////////////////// Integer hit = 0; LatticeReal rtmp(grid); rtmp = where(wheremask, rones, rzeros); RealD numSites = sum(rtmp); RealD numAccepted; LatticeInteger Accepted(grid); Accepted = zero; LatticeInteger newlyAccepted(grid); std::vector xr(4, grid); std::vector a(4, grid); LatticeReal d(grid); d = zero; LatticeReal alpha(grid); // std::cout< 1 - 0.5 d, go back to A; LatticeReal thresh(grid); thresh = 1.0 - d * 0.5; xrsq = xr[0] * xr[0]; LatticeInteger ione(grid); ione = 1; LatticeInteger izero(grid); izero = zero; newlyAccepted = where(xrsq < thresh, ione, izero); Accepted = where(newlyAccepted, newlyAccepted, Accepted); Accepted = where(wheremask, Accepted, izero); // FIXME need an iSum for integer to avoid overload on return type?? rtmp = where(Accepted, rones, rzeros); numAccepted = sum(rtmp); hit++; } while ((numAccepted < numSites) && (hit < nheatbath)); // G. Set a0 = 1 - d; a[0] = zero; a[0] = where(wheremask, 1.0 - d, a[0]); ////////////////////////////////////////// // ii) generate a_i uniform on two sphere radius (1-a0^2)^0.5 ////////////////////////////////////////// LatticeReal a123mag(grid); a123mag = sqrt(abs(1.0 - a[0] * a[0])); LatticeReal cos_theta(grid); LatticeReal sin_theta(grid); LatticeReal phi(grid); random(pRNG, phi); phi = phi * twopi; // uniform in [0,2pi] random(pRNG, cos_theta); cos_theta = (cos_theta * 2.0) - 1.0; // uniform in [-1,1] sin_theta = sqrt(abs(1.0 - cos_theta * cos_theta)); a[1] = a123mag * sin_theta * cos(phi); a[2] = a123mag * sin_theta * sin(phi); a[3] = a123mag * cos_theta; ua = toComplex(a[0]) * ident + toComplex(a[1]) * pauli1 + toComplex(a[2]) * pauli2 + toComplex(a[3]) * pauli3; b = 1.0; b = where(wheremask, uinv * ua, b); su2Insert(b, V, su2_subgroup); // mask the assignment back based on Accptance link = where(Accepted, V * link, link); ////////////////////////////// // Debug Checks // SU2 check LatticeSU2Matrix check(grid); // rotated matrix after hb u = zero; check = ua * adj(ua) - 1.0; check = where(Accepted, check, u); assert(norm2(check) < 1.0e-4); check = b * adj(b) - 1.0; check = where(Accepted, check, u); assert(norm2(check) < 1.0e-4); LatticeMatrix Vcheck(grid); Vcheck = zero; Vcheck = where(Accepted, V * adj(V) - 1.0, Vcheck); // std::cout< static void LieRandomize(GridParallelRNG &pRNG, LatticeMatrixType &out, double scale = 1.0) { GridBase *grid = out._grid; typedef typename LatticeMatrixType::vector_type vector_type; typedef typename LatticeMatrixType::scalar_type scalar_type; typedef iSinglet vTComplexType; typedef Lattice LatticeComplexType; typedef typename GridTypeMapper< typename LatticeMatrixType::vector_object>::scalar_object MatrixType; LatticeComplexType ca(grid); LatticeMatrixType lie(grid); LatticeMatrixType la(grid); ComplexD ci(0.0, scale); ComplexD cone(1.0, 0.0); MatrixType ta; lie = zero; for (int a = 0; a < AdjointDimension; a++) { random(pRNG, ca); ca = (ca + conjugate(ca)) * 0.5; ca = ca - 0.5; generator(a, ta); la = ci * ca * ta; lie = lie + la; // e^{i la ta} } taExp(lie, out); } static void GaussianFundamentalLieAlgebraMatrix(GridParallelRNG &pRNG, LatticeMatrix &out, Real scale = 1.0) { GridBase *grid = out._grid; LatticeReal ca(grid); LatticeMatrix la(grid); Complex ci(0.0, scale); Matrix ta; out = zero; for (int a = 0; a < AdjointDimension; a++) { gaussian(pRNG, ca); generator(a, ta); la = toComplex(ca) * ta; out += la; } out *= ci; } static void FundamentalLieAlgebraMatrix(const LatticeAlgebraVector &h, LatticeMatrix &out, Real scale = 1.0) { conformable(h, out); GridBase *grid = out._grid; LatticeMatrix la(grid); Matrix ta; out = zero; for (int a = 0; a < AdjointDimension; a++) { generator(a, ta); la = peekColour(h, a) * timesI(ta) * scale; out += la; } } // Projects the algebra components a lattice matrix (of dimension ncol*ncol -1 ) // inverse operation: FundamentalLieAlgebraMatrix static void projectOnAlgebra(LatticeAlgebraVector &h_out, const LatticeMatrix &in, Real scale = 1.0) { conformable(h_out, in); h_out = zero; Matrix Ta; for (int a = 0; a < AdjointDimension; a++) { generator(a, Ta); auto tmp = - 2.0 * (trace(timesI(Ta) * in)) * scale;// 2.0 for the normalization of the trace in the fundamental rep pokeColour(h_out, tmp, a); } } template static void HotConfiguration(GridParallelRNG &pRNG, GaugeField &out) { typedef typename GaugeField::vector_type vector_type; typedef iSUnMatrix vMatrixType; typedef Lattice LatticeMatrixType; LatticeMatrixType Umu(out._grid); for (int mu = 0; mu < Nd; mu++) { LieRandomize(pRNG, Umu, 1.0); PokeIndex(out, Umu, mu); } } static void TepidConfiguration(GridParallelRNG &pRNG, LatticeGaugeField &out) { LatticeMatrix Umu(out._grid); for (int mu = 0; mu < Nd; mu++) { LieRandomize(pRNG, Umu, 0.01); PokeIndex(out, Umu, mu); } } static void ColdConfiguration(GridParallelRNG &pRNG, LatticeGaugeField &out) { LatticeMatrix Umu(out._grid); Umu = 1.0; for (int mu = 0; mu < Nd; mu++) { PokeIndex(out, Umu, mu); } } static void taProj(const LatticeMatrix &in, LatticeMatrix &out) { out = Ta(in); } template static void taExp(const LatticeMatrixType &x, LatticeMatrixType &ex) { typedef typename LatticeMatrixType::scalar_type ComplexType; LatticeMatrixType xn(x._grid); RealD nfac = 1.0; xn = x; ex = xn + ComplexType(1.0); // 1+x // Do a 12th order exponentiation for (int i = 2; i <= 12; ++i) { nfac = nfac / RealD(i); // 1/2, 1/2.3 ... xn = xn * x; // x2, x3,x4.... ex = ex + xn * nfac; // x2/2!, x3/3!.... } } }; typedef SU<2> SU2; typedef SU<3> SU3; typedef SU<4> SU4; typedef SU<5> SU5; typedef SU FundamentalMatrices; } } #endif