Program Listing for File symmetry_group.cpp¶
↰ Return to documentation for file (src_ed/symmetry_group.cpp)
//
// symmetry_group.cpp
// exact_diagonalization
//
// Created by David Sénéchal on 16-11-08.
// Copyright © 2016 David Sénéchal. All rights reserved.
//
#include "symmetry_group.hpp"
symmetry_group::symmetry_group(int _N, int _n_sites, const vector<vector<int>> &gen, bool _bath_irrep) : N(_N), n_sites(_n_sites), generator(gen), bath_irrep(_bath_irrep)
{
bath_irrep = _bath_irrep;
if(_bath_irrep) L = _n_sites;
else L = N;
if(global_bool("nosym") or generator.size() == 0){
bath_irrep = false;
generator.resize(1);
generator[0].resize(N);
for(int i=0; i<N; i++) generator[0][i] = i;
build();
}
else build();
}
bool symmetry_group::is_valid_element(const vector<int>& v, bool full){
for(int i=0; i<L; ++i){
if(v[i] >= L or v[i] < 0) return false;
for(int j=0; j<i; ++j) if(v[i] == v[j]) return false;
}
if(full){
for(int i=L; i<N; ++i){
if(v[i] >= g) return false;
}
}
return true;
}
bool symmetry_group::diff(const vector<int>& x, const vector<int>& y, bool full){
for(int i=0; i<L; ++i) if(x[i] != y[i]) return true;
if(full) for(int i=L; i<N; ++i) if(x[i] != y[i]) return true;
return false;
}
bool symmetry_group::is_identity(const vector<int>& v, bool full){
for(int i=0; i<L; ++i) if(v[i] != i) return false;
if(full){
for(int i=L; i<N; ++i) if(v[i] != 0) return false;
}
return true;
}
vector<int> symmetry_group::identity(){
vector<int> v(N);
for(int i=0; i<L; ++i) v[i] = i;
for(int i=L; i<N; ++i) v[i] = 0;
return v;
}
vector<int> symmetry_group::product(const vector<int>& x, const vector<int>& y)
{
vector<int> v(N);
for(int i=0; i<L; ++i) v[i] = x[y[i]];
if(g) for(int i=L; i<N; ++i) v[i]= (x[i] + y[i])%g;
return v;
}
vector<int> symmetry_group::inverse(const vector<int>& x){
vector<int> v(N);
for(int i=0; i<L; ++i) v[x[i]] = i;
for(int i=L; i<N; ++i) v[i]= (-x[i] + g)%g;
return v;
}
// returns the signature of the permutation
int symmetry_group::sign(const vector<int>& x){
int n1 = L-1;
int s=0;
for(int i=0; i<n1; ++i){
for(int j=i+1; j<L; ++j) if (x[i] > x[j]) s++;
}
if(s & 1) return(-1);
else return(1);
}
// applies the group element to the bits of b
uint32_t symmetry_group::apply_single(const vector<int>& x, const uint32_t b){
int bp = 0;
for(int i=0; i<L; ++i) if(b&(1<<i)) bp += (1<<x[i]);
return bp;
}
pair<uint32_t, int> symmetry_group::apply(const vector<int>& v, const uint32_t b){
uint32_t bp = 0;
uint32_t mask = 1;
int q[32];
int j=0;
int phase = 0;
memset(q,0,N*sizeof(*q));
for(int i=0; i<L; i++){
if(b & mask){
bp += (1 << v[i]);
q[j++] = v[i];
}
mask <<= 1;
}
for(int i=L; i<N; i++){
if(b & mask){
phase += 2*v[i];
bp += mask;
}
mask <<= 1;
}
// computing the signature
int n1 = j-1;
int m=0;
for(int i=0; i<n1; ++i){
for(int k=i+1; k<j; ++k) if (q[i] > q[k]) m++;
}
phase += (m%2)*g; // adds g (i.e. x -1) if the permutation is odd
return {bp, phase};
}
// applies the group element to a vector
template<typename T>
vector<T> symmetry_group::apply_to_vector(const vector<int>& v, const vector<T>& x)
{
QCM_ASSERT(x.size()==L);
vector<T> xp(L);
for(int i=0; i<L; i++) xp[i] = x[v[i]];
return xp;
}
pair<int, int> symmetry_group::map_orbital(const vector<int>& v, int a){
if(a<L) return {v[a], 0};
else if(a<N) return {a, 2*v[a]};
else if(a<N+L) return {v[a-N]+N, 0};
else return {a, 2*v[a%N]};
}
void symmetry_group::build()
{
//..............................................................................
// Building and checking the generators
if(generator.size() > MAX_GENERATORS){
qcm_ED_throw("Number of group generators exceeds the limit of " + to_string(MAX_GENERATORS));
}
for(int i=0; i<(int)generator.size(); ++i){
if(!is_valid_element(generator[i])){
cout << "generator " << generator[i] << " problematic" << endl;
qcm_ED_throw("generator " + to_string(i+1) + " is not valid!");
}
}
//..............................................................................
// Checking that the generators commute with each other
for(int i=0; i<generator.size(); ++i){
for(int j=0; j<i; ++j){
auto p1 = product(generator[i], generator[j]);
auto p2 = product(generator[j], generator[i]);
if(diff(p1, p2)) {
qcm_ED_throw("generators " + to_string(i+1) + " and " + to_string(j+1) + " do not commute!");
}
}
}
//..............................................................................
// Lengths of the generators
g=1;
has_complex_irrep = false;
vector<int> dim;
dim.reserve(MAX_GENERATORS);
for(int i=0; i<generator.size(); ++i){
auto p = identity();
int length=1;
while(true){
p = product(p, generator[i]);
if(is_identity(p)) break;
++length;
}
dim.push_back(length);
g *= length;
if(length>2) has_complex_irrep = true;
}
Index index(dim);
//..............................................................................
// Checking (again) that the generators commute with each other
for(int i=0; i<generator.size(); ++i){
for(int j=0; j<i; ++j){
auto p1 = product(generator[i], generator[j]);
auto p2 = product(generator[j], generator[i]);
if(diff(p1, p2, true)) {
qcm_ED_throw("generators " + to_string(i+1) + " and " + to_string(j+1) + " do not commute!");
}
}
}
//..............................................................................
// Constructing the elements
e.reserve(g);
for(int i=0; i<g; ++i){
auto p = identity();
index(i);
for(int j=0; j<generator.size(); ++j){
for(int l=0; l<index.ind[j]; ++l) p = product(p, generator[j]);
}
// Problem if this is the identity. Complain and abort
if(is_identity(p, true) and i != 0){
qcm_ED_throw("The identity is generated as the non trivial element " + index.str());
}
if(!is_valid_element(p,true)){
cout << "element " << generator[i] << " problematic" << endl;
qcm_ED_throw("element " + to_string(i+1) + " is not valid!");
}
e.push_back(p);
}
//..............................................................................
// Constructing the phases of the characters and the character table
phi.set_size(g, g);
Index irrep(index);
for(int i=0; i<g; ++i){
irrep(i);
for(int j=0; j<g; ++j){
index(j);
phi(i,j) = irrep*index;
}
}
complex_irrep.assign(g, false);
chi.set_size(g, g);
for(int i=0; i<g; ++i){
for(int j=0; j<g; ++j){
double phase = phi(i,j)*2*M_PI/g;
Complex z(cos(phase),sin(phase));
chi(i,j) = chop(z,1e-12);
if(abs(sin(phase)) > 1e-12) complex_irrep[i] = true;
}
}
//..............................................................................
// Constructing the transformation matrix for symmetric orbitals
site_irrep_dim.resize(g);
site_irrep_dim_cumul.resize(g);
site_irrep_dim_cumul[0] = 0;
S.set_size(n_sites);
int n_sym_orb = 0;
vector<Complex> orb(n_sites);
for(int irrep = 0; irrep < g; irrep++){
int n_sym_orb_in_irrep = 0;
vector<bool> skip_site(n_sites);
for(int site=0; site < n_sites; site++){ // loop over orbits (i.e. representatives)
if(skip_site[site]) continue; // orbit already covered for that irrep
skip_site[site] = true;
to_zero(orb);
for(int i=0; i < g; i++){
skip_site[e[i][site]] = true;
orb[(int)e[i][site]] += chi(irrep,i);
}
if(normalize(orb)){
for(int i=0 ; i<n_sites; i++) S(n_sym_orb,i) = orb[i];
n_sym_orb ++;
n_sym_orb_in_irrep++;
}
}
site_irrep_dim[irrep] = n_sym_orb_in_irrep;
if(irrep>0) site_irrep_dim_cumul[irrep] = site_irrep_dim_cumul[irrep-1] + n_sym_orb_in_irrep;
}
if(n_sym_orb != n_sites){
qcm_ED_throw("Problem with construction of symmetric orbitals: "
+ to_string(n_sym_orb) + " found instead of " + to_string(n_sites));
}
if(has_complex_irrep == false){
S_real.set_size(n_sites);
S_real.real_part(S);
}
//..............................................................................
// construction of the tensor products
tensor.set_size(g);
for (int r1=0; r1<g; r1++) { // loop over irreps
for (int r2=0; r2<g; r2++){ // loop over irreps
tensor(r1,r2) = index(r1,r2); // group or representation product
}
}
//..............................................................................
// identifying the conjugate irreps
conjugate.resize(g);
// cout << "conjugate representations:\n";
for(int r=0; r<g; r++){
vector<int> chi_tmp(g);
for(int i=0; i<g; i++) chi_tmp[i] = 2*g-phi(r,i); // adding 2*g to make result positive
for(int rp=0; rp<g; rp++){
int i;
for(i=0; i<g; i++) if((chi_tmp[i]-phi(rp,i)+2*g)%g) break;
if(i==g){
conjugate[r] = rp;
break;
}
}
}
//..............................................................................
// precomputing the phases
phaseR.resize(2*g);
phaseC.resize(2*g);
for(int i=0; i<2*g; i++) phaseR[i] =-1; phaseR[0] = 1;
for(int i=0; i<2*g; i++){
double phi = (M_PI*i)/g;
Complex z(cos(phi),sin(phi));
phaseC[i] = z;
}
}
pair<binary_state, int> symmetry_group::apply(const int &elem, const binary_state &b)
{
// first the right part of b
auto right = apply(e[elem], b.right());
// second the left part of b
auto left = apply(e[elem], b.left());
return {binary_state(left.first, right.first), left.second+right.second};
}
pair<binary_state, int> symmetry_group::apply(const int &elem, const int &irrep, const binary_state &b)
{
auto X = apply(elem, b);
X.second += 2*phi(elem, irrep);
X.second = X.second%(2*g);
return X;
}
rep_map symmetry_group::Representative(const binary_state &b, int irrep)
{
int h=1; // size of the subgroup that leaves the state invariant
rep_map M = {b, 0, 0};
for(int elem=1; elem<g; ++elem){ // loop over group elements to find the representative
auto X = apply(elem, b);
X.second += 2*phi(elem,irrep);
X.second = X.second%(2*g);
if(X.first == b){
h++;
if(X.second != 0 and (2*g)%X.second == 0){
M.length = 0;
return M;
}
}
if (X.first.b < M.b.b) {
M.b = X.first;
M.phase = X.second;
}
}
M.length = g/h;
return M;
}
void symmetry_group::to_site_basis(int r, vector<Complex> &x, vector<Complex> &y, int n_mixed)
{
to_zero(y);
QCM_ASSERT(y.size() == n_mixed*n_sites);
QCM_ASSERT(x.size() == n_mixed*site_irrep_dim[r]);
int offset = site_irrep_dim_cumul[r];
for(int mr=0; mr<n_mixed; mr++){
int y_offset = mr*n_sites;
for(int a=0; a<n_sites; a++){
int dr = site_irrep_dim[r];
Complex z(0.0);
int x_offset = dr*mr;
for(int alpha=0; alpha<dr; alpha++){
z += x[alpha+x_offset]*S(alpha+offset,a);
}
y[a+y_offset] += z;
}
}
}
void symmetry_group::to_site_basis(int r, vector<double> &x, vector<double> &y, int n_mixed)
{
to_zero(y);
QCM_ASSERT(y.size() == n_mixed*n_sites);
QCM_ASSERT(x.size() == n_mixed*site_irrep_dim[r]);
int offset = site_irrep_dim_cumul[r];
for(int mr=0; mr<n_mixed; mr++){
int y_offset = mr*n_sites;
for(int a=0; a<n_sites; a++){
int dr = site_irrep_dim[r];
double z(0.0);
int x_offset = dr*mr;
for(int alpha=0; alpha<dr; alpha++){
z += x[alpha+x_offset]*S_real(alpha+offset,a);
}
y[a+y_offset] += z;
}
}
}
bool symmetry_group::sector_is_valid(const sector& sec){
if(sec.N < sector::odd){
if(sec.N < 0) return false;
if(sec.N > 2*N) return false;
if(sec.S < sector::odd){
if(sec.S > sec.N) return false;
if(sec.S < -sec.N) return false;
if((sec.N+sec.S)%2) return false;
}
}
if(sec.S < sector::odd){
int norb((int)N);
if(sec.S > norb) return false;
if(sec.S < -norb) return false;
}
if(sec.irrep >= g) return false;
else return true;
}
// Shifts a sector by adding (pm=1) or removing (pm=-1) a particle of spin "spin" (=0 or 1) in irrep _irrep
sector symmetry_group::shift_sector(const sector& _sec, int pm, int spin, int _irrep){
sector sec = _sec;
if(sec.N == sector::odd) sec.N = sector::even;
else if(sec.N == sector::even) sec.N = sector::odd;
else sec.N += pm;
if(sec.S == sector::odd) sec.S = sector::even;
else if(sec.S == sector::even) sec.S = sector::odd;
else{
if(spin) sec.S -= pm;
else sec.S += pm;
}
if(pm==1) sec.irrep = tensor(sec.irrep,_irrep);
else sec.irrep = tensor(sec.irrep, conjugate[_irrep]);
return sec;
}
bool symmetry_group::operator!=(const symmetry_group &y){
if(N != y.N) return true;
if(n_sites != y.n_sites) return true;
if(generator.size() != y.generator.size()) return true;
for(int i=0; i<generator.size(); ++i) if(generator[i] != y.generator[i]) return true;
return false;
}
void symmetry_group::to_site_basis(block_matrix<Complex> &B, matrix<Complex> &G, int n_mixed)
{
for(int mr=0; mr<n_mixed; mr++){
int G_offset_r = mr*n_sites;
for(int mc=0; mc<n_mixed; mc++){
int G_offset_c = mc*n_sites;
for(int a=0; a<n_sites; a++){
for(int b=0; b<n_sites; b++){
int offset = 0;
for(int r=0; r<g; r++){
int dr = site_irrep_dim[r];
Complex z(0.0);
matrix<Complex>& sym_g = B.block[r];
int sym_g_offset_r = dr*mr;
int sym_g_offset_c = dr*mc;
for(int alpha=0; alpha<dr; alpha++){
for(int beta=0; beta<dr; beta++){
z += sym_g(alpha+sym_g_offset_r,beta+sym_g_offset_c)*S(beta+offset,b)*conj(S(alpha+offset,a));
}
}
offset += dr;
G(a+G_offset_r,b+G_offset_c) += z;
}
}
}
}
}
}
vector<vector<symmetric_orbital>> symmetry_group::build_symmetric_orbitals(int mixing)
{
int control = 0;
int nambu_max = (mixing&HS_mixing::anomalous)? 2:1;
int spin_max = (mixing&HS_mixing::spin_flip)? 2:1;
vector<vector<symmetric_orbital>> sym_orb(g);
for(int r=0; r<g; r++){
vector<symmetric_orbital> tmp;
tmp.reserve(nambu_max*spin_max*site_irrep_dim[r]);
for(int i_nambu = 0; i_nambu < nambu_max; i_nambu++){
for(int i_spin = 0; i_spin < spin_max; i_spin++){
for(int i=0; i< site_irrep_dim[r]; i++){
if(i_nambu){
symmetric_orbital tmp_orb;
if(spin_max==2) tmp_orb = symmetric_orbital(conjugate[r], i, i_spin, i_nambu, site_irrep_dim);
else tmp_orb = symmetric_orbital(conjugate[r], i, (i_spin+1)%2, i_nambu, site_irrep_dim);
tmp.push_back(tmp_orb);
}
else{
symmetric_orbital tmp_orb(r, i, i_spin, i_nambu, site_irrep_dim);
tmp.push_back(tmp_orb);
}
control++;
}
}
}
sym_orb[r] = tmp;
}
return sym_orb;
}
std::ostream & operator<<(std::ostream &flux, const symmetry_group &x)
{
flux << "generators\n";
for(int i=0; i<x.generator.size(); i++) flux << i+1 << " : " << x.generator[i] << '\n';
flux << "\ngroup elements\n";
for(int i=0; i<x.g; i++) flux << i+1 << " : " << x.e[i] << '\n';
flux << "\nSymmetric orbitals:\n" << x.S << '\n';
flux << "\nphases according to the different irreps:\n" << x.phi << endl;
flux << "\nphaseR:\n" << x.phaseR << endl;
flux << "\nphaseC:\n" << x.phaseC << endl;
return flux;
}