//////////////////////////////////////////////////////////////////////// // // * Two index representation generators // // * Normalisation for the fundamental generators: // trace ta tb = 1/2 delta_ab = T_F delta_ab // T_F = 1/2 for SU(N) groups // // // base for NxN two index (anti-symmetric) matrices // normalized to 1 (d_ij is the kroenecker delta) // // (e^(ij)_{kl} = 1 / sqrt(2) (d_ik d_jl +/- d_jk d_il) // // Then the generators are written as // // (iT_a)^(ij)(lk) = i * ( tr[e^(ij)^dag e^(lk) T^trasp_a] + // tr[e^(lk)e^(ij)^dag T_a] ) // // // //////////////////////////////////////////////////////////////////////// // Authors: David Preti, Guido Cossu #ifndef QCD_UTIL_GAUGEGROUPTWOINDEX_H #define QCD_UTIL_GAUGEGROUPTWOINDEX_H NAMESPACE_BEGIN(Grid); enum TwoIndexSymmetry { Symmetric = 1, AntiSymmetric = -1 }; constexpr inline Real delta(int a, int b) { return (a == b) ? 1.0 : 0.0; } namespace detail { template struct baseOffDiagonalSpHelper; template struct baseOffDiagonalSpHelper { static const int ngroup = nc / 2; static void baseOffDiagonalSp(int i, int j, iScalar > > &eij) { eij = Zero(); RealD tmp; if ((i == ngroup + j) && (1 <= j) && (j < ngroup)) { for (int k = 0; k < j+1; k++) { if (k < j) { tmp = 1 / sqrt(j * (j + 1)); eij()()(k, k + ngroup) = tmp; eij()()(k + ngroup, k) = -tmp; } if (k == j) { tmp = -j / sqrt(j * (j + 1)); eij()()(k, k + ngroup) = tmp; eij()()(k + ngroup, k) = -tmp; } } } else if (i != ngroup + j) { for (int k = 0; k < nc; k++) for (int l = 0; l < nc; l++) { eij()()(l, k) = delta(i, k) * delta(j, l) - delta(j, k) * delta(i, l); } } RealD nrm = 1. / std::sqrt(2.0); eij = eij * nrm; } }; template struct baseOffDiagonalSpHelper { static void baseOffDiagonalSp(int i, int j, iScalar > > &eij) { eij = Zero(); for (int k = 0; k < nc; k++) for (int l = 0; l < nc; l++) eij()()(l, k) = delta(i, k) * delta(j, l) + delta(j, k) * delta(i, l); RealD nrm = 1. / std::sqrt(2.0); eij = eij * nrm; } }; } // closing detail namespace template class GaugeGroupTwoIndex : public GaugeGroup { public: // The chosen convention is that we are taking ncolour to be N in SU but 2N // in Sp(2N). ngroup is equal to N for SU but 2N/2 = N for Sp(2N). static_assert(std::is_same::value or std::is_same::value, "ngroup is only implemented for SU and Sp currently."); static const int ngroup = std::is_same::value ? ncolour : ncolour / 2; static const int Dimension = (ncolour * (ncolour + S) / 2) + (std::is_same::value ? (S - 1) / 2 : 0); static const int DimensionAS = (ncolour * (ncolour - 1) / 2) + (std::is_same::value ? (- 1) : 0); static const int DimensionS = ncolour * (ncolour + 1) / 2; static const int NumGenerators = GaugeGroup::AlgebraDimension; template using iGroupTwoIndexMatrix = iScalar > >; typedef iGroupTwoIndexMatrix TIMatrix; typedef iGroupTwoIndexMatrix TIMatrixF; typedef iGroupTwoIndexMatrix TIMatrixD; typedef iGroupTwoIndexMatrix vTIMatrix; typedef iGroupTwoIndexMatrix vTIMatrixF; typedef iGroupTwoIndexMatrix vTIMatrixD; typedef Lattice LatticeTwoIndexMatrix; typedef Lattice LatticeTwoIndexMatrixF; typedef Lattice LatticeTwoIndexMatrixD; typedef Lattice >, Nd> > LatticeTwoIndexField; typedef Lattice >, Nd> > LatticeTwoIndexFieldF; typedef Lattice >, Nd> > LatticeTwoIndexFieldD; template using iGroupMatrix = iScalar > >; typedef iGroupMatrix Matrix; typedef iGroupMatrix MatrixF; typedef iGroupMatrix MatrixD; private: template static void baseDiagonal(int Index, iGroupMatrix &eij) { eij = Zero(); eij()()(Index - ncolour * (ncolour - 1) / 2, Index - ncolour * (ncolour - 1) / 2) = 1.0; } template static void baseOffDiagonal(int i, int j, iGroupMatrix &eij, GroupName::SU) { eij = Zero(); for (int k = 0; k < ncolour; k++) for (int l = 0; l < ncolour; l++) eij()()(l, k) = delta(i, k) * delta(j, l) + S * delta(j, k) * delta(i, l); RealD nrm = 1. / std::sqrt(2.0); eij = eij * nrm; } template static void baseOffDiagonal(int i, int j, iGroupMatrix &eij, GroupName::Sp) { detail::baseOffDiagonalSpHelper::baseOffDiagonalSp(i, j, eij); } public: template static void base(int Index, iGroupMatrix &eij) { // returns (e)^(ij)_{kl} necessary for change of base U_F -> U_R assert(Index < Dimension); eij = Zero(); // for the linearisation of the 2 indexes static int a[ncolour * (ncolour - 1) / 2][2]; // store the a <-> i,j static bool filled = false; if (!filled) { int counter = 0; for (int i = 1; i < ncolour; i++) { for (int j = 0; j < i; j++) { if (std::is_same::value) { if (j==0 && i==ngroup+j && S==-1) { //std::cout << "skipping" << std::endl; // for Sp2n this vanishes identically. j = j+1; } } a[counter][0] = i; a[counter][1] = j; counter++; } } filled = true; } if (Index < ncolour*ncolour - DimensionS) { baseOffDiagonal(a[Index][0], a[Index][1], eij, group_name()); } else { baseDiagonal(Index, eij); } } static void printBase(void) { for (int gen = 0; gen < Dimension; gen++) { Matrix tmp; base(gen, tmp); std::cout << GridLogMessage << "Nc = " << ncolour << " t_" << gen << std::endl; std::cout << GridLogMessage << tmp << std::endl; } } template static void generator(int Index, iGroupTwoIndexMatrix &i2indTa) { Vector > ta(NumGenerators); Vector > eij(Dimension); iGroupMatrix tmp; for (int a = 0; a < NumGenerators; a++) GaugeGroup::generator(a, ta[a]); for (int a = 0; a < Dimension; a++) base(a, eij[a]); for (int a = 0; a < Dimension; a++) { tmp = transpose(eij[a]*ta[Index]) + transpose(eij[a]) * ta[Index]; for (int b = 0; b < Dimension; b++) { Complex iTr = TensorRemove(timesI(trace(tmp * eij[b]))); i2indTa()()(a, b) = iTr; } } } static void printGenerators(void) { for (int gen = 0; gen < NumGenerators; gen++) { TIMatrix i2indTa; generator(gen, i2indTa); std::cout << GridLogMessage << "Nc = " << ncolour << " t_" << gen << std::endl; std::cout << GridLogMessage << i2indTa << std::endl; } } static void testGenerators(void) { TIMatrix i2indTa, i2indTb; std::cout << GridLogMessage << "2IndexRep - Checking if traceless" << std::endl; for (int a = 0; a < NumGenerators; a++) { generator(a, i2indTa); std::cout << GridLogMessage << a << std::endl; assert(norm2(trace(i2indTa)) < 1.0e-6); } std::cout << GridLogMessage << std::endl; std::cout << GridLogMessage << "2IndexRep - Checking if antihermitean" << std::endl; for (int a = 0; a < NumGenerators; a++) { generator(a, i2indTa); std::cout << GridLogMessage << a << std::endl; assert(norm2(adj(i2indTa) + i2indTa) < 1.0e-6); } std::cout << GridLogMessage << std::endl; std::cout << GridLogMessage << "2IndexRep - Checking Tr[Ta*Tb]=delta(a,b)*(N +- 2)/2" << std::endl; for (int a = 0; a < NumGenerators; a++) { for (int b = 0; b < NumGenerators; b++) { generator(a, i2indTa); generator(b, i2indTb); // generator returns iTa, so we need a minus sign here Complex Tr = -TensorRemove(trace(i2indTa * i2indTb)); std::cout << GridLogMessage << "a=" << a << "b=" << b << "Tr=" << Tr << std::endl; if (a == b) { assert(real(Tr) - ((ncolour + S * 2) * 0.5) < 1e-8); } else { assert(real(Tr) < 1e-8); } assert(imag(Tr) < 1e-8); } } std::cout << GridLogMessage << std::endl; } static void TwoIndexLieAlgebraMatrix( const typename GaugeGroup::LatticeAlgebraVector &h, LatticeTwoIndexMatrix &out, Real scale = 1.0) { conformable(h, out); GridBase *grid = out.Grid(); LatticeTwoIndexMatrix la(grid); TIMatrix i2indTa; out = Zero(); for (int a = 0; a < NumGenerators; a++) { generator(a, i2indTa); la = peekColour(h, a) * i2indTa; out += la; } out *= scale; } // Projects the algebra components // of a lattice matrix ( of dimension ncol*ncol -1 ) static void projectOnAlgebra( typename GaugeGroup::LatticeAlgebraVector &h_out, const LatticeTwoIndexMatrix &in, Real scale = 1.0) { conformable(h_out, in); h_out = Zero(); TIMatrix i2indTa; Real coefficient = -2.0 / (ncolour + 2 * S) * scale; // 2/(Nc +/- 2) for the normalization of the trace in the two index rep for (int a = 0; a < NumGenerators; a++) { generator(a, i2indTa); pokeColour(h_out, real(trace(i2indTa * in)) * coefficient, a); } } // a projector that keeps the generators stored to avoid the overhead of // recomputing them static void projector( typename GaugeGroup::LatticeAlgebraVector &h_out, const LatticeTwoIndexMatrix &in, Real scale = 1.0) { conformable(h_out, in); // to store the generators static std::vector i2indTa(NumGenerators); h_out = Zero(); static bool precalculated = false; if (!precalculated) { precalculated = true; for (int a = 0; a < NumGenerators; a++) generator(a, i2indTa[a]); } Real coefficient = -2.0 / (ncolour + 2 * S) * scale; // 2/(Nc +/- 2) for the normalization // of the trace in the two index rep for (int a = 0; a < NumGenerators; a++) { auto tmp = real(trace(i2indTa[a] * in)) * coefficient; pokeColour(h_out, tmp, a); } } }; template using SU_TwoIndex = GaugeGroupTwoIndex; // Some useful type names typedef SU_TwoIndex TwoIndexSymmMatrices; typedef SU_TwoIndex TwoIndexAntiSymmMatrices; typedef SU_TwoIndex<2, Symmetric> SU2TwoIndexSymm; typedef SU_TwoIndex<3, Symmetric> SU3TwoIndexSymm; typedef SU_TwoIndex<4, Symmetric> SU4TwoIndexSymm; typedef SU_TwoIndex<5, Symmetric> SU5TwoIndexSymm; typedef SU_TwoIndex<2, AntiSymmetric> SU2TwoIndexAntiSymm; typedef SU_TwoIndex<3, AntiSymmetric> SU3TwoIndexAntiSymm; typedef SU_TwoIndex<4, AntiSymmetric> SU4TwoIndexAntiSymm; typedef SU_TwoIndex<5, AntiSymmetric> SU5TwoIndexAntiSymm; template using Sp_TwoIndex = GaugeGroupTwoIndex; typedef Sp_TwoIndex SpTwoIndexSymmMatrices; typedef Sp_TwoIndex SpTwoIndexAntiSymmMatrices; typedef Sp_TwoIndex<2, Symmetric> Sp2TwoIndexSymm; typedef Sp_TwoIndex<4, Symmetric> Sp4TwoIndexSymm; typedef Sp_TwoIndex<4, AntiSymmetric> Sp4TwoIndexAntiSymm; NAMESPACE_END(Grid); #endif