Program Listing for File CPT.cpp¶
↰ Return to documentation for file (src_qcm/CPT.cpp)
#include "lattice_model_instance.hpp"
#include "matrix_continued_fraction.hpp"
#ifdef _OPENMP
#include <omp.h>
#endif
#include <fstream>
#define ETADELTA 0.25
//==============================================================================
void lattice_model_instance::set_V(Green_function_k &M, bool nohybrid){
if(M.ik >=0 && model->hybrid == nullptr) qcm_throw("Cannot execute this task with a preset lattice hybridization!");
size_t nv = model->neighbor.size();
M.phase.assign(nv, 1.0);
// computing the phases
for(int i=0; i<nv; ++i){
double z = M.k*model->neighbor[i]*2*M_PI;
M.phase[i] = Complex(cos(z), sin(z));
}
// computing tk
if(M.G.spin_down){
M.t = H_down;
for(auto& op : model->term){
if(auto it = params.find(op.second->name); it != params.end()){
double pv = it->second;
for(auto& e : op.second->IGF_elem_down){
M.t(e.r,e.c) += e.v*pv*M.phase[e.n];
}
}
}
}
else{
M.t = H; // adds the momentum-independent part of V
for(auto& op : model->term){
if(auto it = params.find(op.second->name); it != params.end()){
double pv = it->second;
for(auto& e : op.second->IGF_elem){
M.t(e.r,e.c) += e.v*pv*M.phase[e.n];
}
}
}
}
// computing V = tk - t' - Gamma_c + Gamma_k
M.V = M.t;
if(M.G.spin_down) Hc_down.add(M.V,-1.0);
else Hc.add(M.V,-1.0);
if(model->bath_exists and nohybrid==false) M.G.gamma.add(M.V,-1.0);
if(model->hybrid != nullptr) M.V.v += model->lattice_hybridization(M.G.iw, M.ik).v;
}
//==============================================================================
void lattice_model_instance::set_Gcpt(Green_function_k &M)
{
matrix<Complex> VG(Green_function_k::dim_GF);
set_V(M);
M.G.G.mult_right(M.V, VG); // VG = V*G
VG.v *= -1.0;
VG.add(1.0);
VG.inverse();
M.G.G.mult_left(VG, M.Gcpt); // Gcpt = G/(1-V*G)
}
//==============================================================================
void lattice_model_instance::inverse_Gcpt(const block_matrix<Complex> &Ginv, Green_function_k &M)
{
set_V(M);
Ginv.add(M.V, -1.0);
M.V.v *= -1.0;
}
//==============================================================================
matrix<Complex> lattice_model_instance::epsilon(Green_function_k &M)
{
char periodization = global_char("periodization");
set_V(M);
if(periodization == 'N') return M.t;
else return model->periodize(M.k,M.t);
}
//==============================================================================
vector<double> lattice_model_instance::dispersion(Green_function_k &M)
{
matrix<Complex> eps = epsilon(M);
vector<double> d(eps.r);
eps.eigenvalues(d);
return d;
}
//==============================================================================
matrix<Complex> lattice_model_instance::band_Green_function(Green_function_k &M)
{
// first compute the lattice Green function
periodized_Green_function(M);
// then compute the dispersion relation
auto eps = epsilon(M);
int nband = model->n_band;
matrix<Complex> U(nband);
vector<double> d(nband);
eps.eigensystem(d, U);
matrix<Complex> bandG(M.g);
bandG.simil(U, M.g);
return bandG;
}
//==============================================================================
void lattice_model_instance::periodized_Green_function(Green_function_k &M)
{
char periodization = global_char("periodization");
set_Gcpt(M);
if(periodization == 'G'){
M.g = model->periodize(M.k, M.Gcpt);
}
else if(periodization == 'M'){ // cumulant
double mu = 0.0;
if(params.find("mu") != params.end()) mu = params.at("mu");
matrix<Complex> gtmp(M.Gcpt);
matrix<Complex> ep(M.g.r);
gtmp.inverse();
for(int i=0; i<M.dim_GF; i++) M.t(i,i) += mu*(1-2*model->is_nambu_index[i]); // mu removed from t
gtmp.v += M.t.v; // the new t removed from gtmp, which is Gcpt^{-1}
gtmp.inverse();
M.g = model->periodize(M.k, gtmp); // gtmp is periodized and put in M.g
ep = model->periodize(M.k, M.t); // M.t (without mu) is periodized and put in ep
M.g.inverse();
M.g.v -= ep.v;
M.g.inverse();
}
else if(periodization == 'S'){ // sigma
to_zero(M.Gcpt.v);
cluster_self_energy(M.G);
M.Gcpt = M.G.sigma.build_matrix();
M.g = model->periodize(M.k, M.Gcpt);
matrix<Complex> eps(model->dim_reduced_GF);
eps = epsilon(M);
M.g.v += eps.v;
M.g.v -= M.G.w;
M.g.v *= -1.0;
M.g.inverse();
}
else if(periodization == 'C'){ // cluster
QCM_ASSERT(model->clusters.size()==1);
matrix<Complex> Gc = M.G.G.block[0];
M.g = model->periodize(M.k, Gc);
}
else if(periodization == 'N'){ // None
M.g = M.Gcpt;
}
else if(periodization == 'L'){ // Lanczos MCF periodization
QCM_ASSERT(model->clusters.size() == 1);
// Retrieve combined MCF in the full cluster site-orbital basis.
// Works with GF_method='M' or GF_method='L' + combine_mcf=True.
size_t sys_label = model->nsys * label + model->clusters[0].sys_start;
auto mcf_data = ED::get_combined_mcf(M.G.spin_down, sys_label);
// Incorporate the inter-cluster hopping into the first diagonal block.
// A[0] lives in the cluster orbital basis (dim_GF × dim_GF), as does M.V.
mcf_data.A[0].v += M.V.v;
// Apply compact_tiling to A[j>=1] and B[j>=1] before periodizing.
// A[0] is excluded because it already has M.V incorporated.
int nA = (int)mcf_data.A.size();
int nB = (int)mcf_data.B.size();
for(int j = 1; j < nA; ++j)
mcf_data.A[j] = model->compact_tiling(mcf_data.A[j], M.k);
for(int j = 1; j < nB; ++j)
mcf_data.B[j] = model->compact_tiling(mcf_data.B[j], M.k);
// Periodize all A[j], B[j] and W from the cluster basis (dim_GF × dim_GF)
// to the band basis (dim_reduced_GF × dim_reduced_GF).
vector<matrix<Complex>> A_per(nA), B_per(nB);
for(int j = 0; j < nA; ++j)
A_per[j] = model->periodize(M.k, mcf_data.A[j]);
for(int j = 0; j < nB; ++j)
B_per[j] = model->periodize(M.k, mcf_data.B[j]);
matrix<Complex> W_per = model->periodize(M.k, mcf_data.W);
// Build the periodized MCF and evaluate at the current frequency.
matrix_continued_fraction<Complex> mcf_per(A_per, B_per, W_per);
M.g = mcf_per.evaluate(M.G.w);
}
else{
qcm_throw("undefined periodization scheme");
}
}
//==============================================================================
void lattice_model_instance::self_energy(Green_function_k &M)
{
periodized_Green_function(M);
matrix<Complex> eps(model->dim_reduced_GF);
M.sigma = M.g;
M.sigma.inverse();
M.sigma.add(-M.G.w);
eps = epsilon(M);
M.sigma.v += eps.v;
M.sigma.v *= -1.0;
}
//==============================================================================
matrix<Complex> lattice_model_instance::projected_Green_function(Complex w, bool spin_down)
{
if(global_bool("verb_ED")) cout << "Computing projected GF at w = " << w << endl;
matrix<Complex> PGF(model->dim_GF);
Green_function G = cluster_Green_function(w, false, spin_down);
size_t kgrid_side = global_int("kgrid_side");
double step = 1.0/kgrid_side;
size_t nk = 0;
switch((int)model->superlattice.D){
case 0:
{
Green_function_k M(G,{0.0,0.0,0.0});
set_Gcpt(M);
PGF += M.Gcpt;
nk++;
}
break;
case 1:
// #pragma omp parallel for
for(int i=0; i<kgrid_side; i++){
Green_function_k M(G,{(i+0.5)*step,0.0,0.0});
set_Gcpt(M);
// #pragma omp critical
PGF += M.Gcpt;
nk++;
}
break;
case 2:
for(int i=0; i<kgrid_side; i++){
// #pragma omp parallel for
for(int j=0; j<kgrid_side; j++){
Green_function_k M(G,{(i+0.5)*step, (j+0.5)*step,0.0});
set_Gcpt(M);
// #pragma omp critical
PGF += M.Gcpt;
nk++;
}
}
break;
case 3:
for(int i=0; i<kgrid_side; i++){
for(int j=0; j<kgrid_side; j++){
// #pragma omp parallel for
for(int k=0; k<kgrid_side; k++){
Green_function_k M(G,{(i+0.5)*step, (j+0.5)*step, (k+0.5)*step});
set_Gcpt(M);
// #pragma omp critical
PGF += M.Gcpt;
nk++;
}
}
}
break;
default:
qcm_throw("Forbidden dimension in construction of wavevector grid!");
}
PGF.v *= 1.0/nk;
return PGF;
}
//==============================================================================
void lattice_model_instance::CDMFT_host(const vector<double>& freqs, const vector<double>& weights)
{
CDMFT_weights = weights;
CDMFT_freqs = freqs;
size_t n_clus = model->clusters.size();
if(G_host.size()==0){
G_host.assign(freqs.size(), vector<matrix<Complex>>(n_clus));
for(int i=0; i<freqs.size(); i++){
for(int c=0; c<n_clus; c++){
int d = model->GF_dims[c];
G_host[i][c].set_size(d,d);
}
}
}
else return;
if(model->hybrid != nullptr){
QCM_ASSERT(model->hybrid->nw == freqs.size());
CDMFT_host();
return;
}
// #pragma omp parallel for
if(global_bool("verb_ED")) cout << "Building host function" << endl;
for(int i=0; i<freqs.size(); i++){
Complex w(0.0, freqs[i]);
auto Gproj= projected_Green_function(w, false);
for(int c=0; c<n_clus; c++){
int d = model->GF_dims[c];
int o = model->GF_offset[c];
Gproj.move_sub_matrix(d, d, o, o, 0, 0, G_host[i][c]);
G_host[i][c].inverse();
auto X = cluster_self_energy(c, w, false);
auto Y = cluster_hopping_matrix(c, false);
G_host[i][c].v += X.v;
G_host[i][c].v += Y.v;
G_host[i][c].add(-w);
}
}
if(model->mixing & HS_mixing::up_down){
if (global_bool("verb_ED")) cout << "Building host function for spin down" << endl;
if(G_host_down.size()==0){
G_host_down.assign(freqs.size(), vector<matrix<Complex>>(n_clus));
for(int i=0; i<freqs.size(); i++){
for(int c=0; c<n_clus; c++){
int d = model->GF_dims[c];
G_host_down[i][c].set_size(d,d);
}
}
}
// #pragma omp parallel for
for(int i=0; i<freqs.size(); i++){
Complex w(0.0, freqs[i]);
auto Gproj= projected_Green_function(w, true);
for(int c=0; c<n_clus; c++){
int d = model->GF_dims[c];
int o = model->GF_offset[c];
Gproj.move_sub_matrix(d, d, o, o, 0, 0, G_host_down[i][c]);
G_host_down[i][c].inverse();
auto X = cluster_self_energy(c, w, true);
auto Y = cluster_hopping_matrix(c, true);
G_host_down[i][c].v += X.v;
G_host_down[i][c].v += Y.v;
G_host_down[i][c].add(-w);
}
}
}
}
//==============================================================================
void lattice_model_instance::CDMFT_host()
{
auto &H = *model->hybrid;
cout << "Building host function with an external hybridization" << endl;
for(int iw=0; iw<H.nw; iw++){
Complex w(0.0, H.w[iw]);
Green_function G = cluster_Green_function(w, false, false);
G.iw = iw;
matrix<Complex> Gproj(model->dim_GF);
for(int ik=0; ik<H.nk; ik++){
Green_function_k M(G,H.k[ik],ik);
set_Gcpt(M);
Gproj += M.Gcpt;
}
Gproj.v *= 1.0/H.nk;
for(int c=0; c<model->clusters.size(); c++){
int d = model->GF_dims[c];
int o = model->GF_offset[c];
Gproj.move_sub_matrix(d, d, o, o, 0, 0, G_host[iw][c]);
G_host[iw][c].inverse();
auto X = cluster_self_energy(c, w, false);
auto Y = cluster_hopping_matrix(c, false);
G_host[iw][c].v += X.v;
G_host[iw][c].v += Y.v;
G_host[iw][c].add(-w);
}
}
if(model->mixing & HS_mixing::up_down){
qcm_throw("mixing up_down not yet possible with external hybridization");
}
}
vector<vector<matrix<Complex>>> lattice_model_instance::get_CDMFT_host(bool spin_down){
if(spin_down){
if(G_host_down.size() == 0) qcm_throw("G_host_down has not been computed for this instance");
return G_host_down;
}
else {
if(G_host.size() == 0) qcm_throw("G_host has not been computed for this instance");
return G_host;
}
}
//==============================================================================
void lattice_model_instance::set_CDMFT_host(const vector<double>& freqs, const int clus, const vector<matrix<Complex>>& H, const bool spin_down)
{
CDMFT_weights.assign(freqs.size(), 1.0/freqs.size());
CDMFT_freqs = freqs;
int n_clus = model->clusters.size();
if(G_host.size()==0){
G_host.assign(freqs.size(), vector<matrix<Complex>>(n_clus));
for(int i=0; i<freqs.size(); i++){
for(int c=0; c<n_clus; c++){
int d = model->GF_dims[c];
G_host[i][c].set_size(d,d);
}
}
}
if(spin_down){
for(int i=0; i<freqs.size(); i++) G_host_down[i][clus] = H[i];
}
else{
for(int i=0; i<freqs.size(); i++) G_host[i][clus] = H[i];
}
}
//==============================================================================
double lattice_model_instance::CDMFT_distance(const vector<double>& p, int clus)
{
double dist = 0.0;
for(int i=0; i<model->param_set->CDMFT_variational[clus].size(); i++){
model->param_set->set_value(model->param_set->CDMFT_variational[clus][i], p[i]);
}
auto I = lattice_model_instance(model, label+999);
double dw = CDMFT_freqs[1]-CDMFT_freqs[0];
int dim = G_host[0][0].r;
#pragma omp parallel for reduction (+:dist)
for(int i=0; i<CDMFT_freqs.size(); i++){
Complex w(0.0, CDMFT_freqs[i]);
auto gamma = I.hybridization_function(clus, w, false);
gamma.v += G_host[i][clus].v;
dist += norm2(gamma.v)*CDMFT_weights[i];
}
if(model->mixing & HS_mixing::up_down){
#pragma omp parallel for reduction (+:dist)
for(int i=0; i<CDMFT_freqs.size(); i++){
Complex w(0.0, CDMFT_freqs[i]);
auto gamma = I.hybridization_function(clus, w, true);
gamma.v += G_host_down[i][clus].v;
dist += norm2(gamma.v)*CDMFT_weights[i];
}
}
if(model->mixing == 0) dist *= 2;
else if(model->mixing == 3) dist *= 0.5;
dist *= dw; // normalizes by the frequency step, to give something that does not depend on the step when step --> 0
dist /= (dim*dim); // divides by the number of elements in the matrices, to obtain an average distance per matrix element
return dist;
}
//==============================================================================
vector<double> lattice_model_instance::CDMFT_residuals(const vector<double>& p, int clus)
{
for(int i=0; i<model->param_set->CDMFT_variational[clus].size(); i++){
model->param_set->set_value(model->param_set->CDMFT_variational[clus][i], p[i]);
}
auto I = lattice_model_instance(model, label+999);
int dim = G_host[0][0].r;
int dim2 = dim*dim;
int Nfreq = (int)CDMFT_freqs.size();
bool has_down = (model->mixing & HS_mixing::up_down) != 0;
int n_spins = has_down ? 2 : 1;
vector<double> r(2 * n_spins * Nfreq * dim2, 0.0);
#pragma omp parallel for if(!omp_in_parallel())
for(int i=0; i<Nfreq; i++){
Complex w(0.0, CDMFT_freqs[i]);
double sw = sqrt(CDMFT_weights[i]);
auto gamma = I.hybridization_function(clus, w, false);
for(int k=0; k<dim2; k++){
Complex z = gamma.v[k] + G_host[i][clus].v[k];
r[2*dim2*i + k] = sw * z.real();
r[2*dim2*i + dim2 + k] = sw * z.imag();
}
}
if(has_down){
int off = 2 * Nfreq * dim2;
#pragma omp parallel for if(!omp_in_parallel())
for(int i=0; i<Nfreq; i++){
Complex w(0.0, CDMFT_freqs[i]);
double sw = sqrt(CDMFT_weights[i]);
auto gamma = I.hybridization_function(clus, w, true);
for(int k=0; k<dim2; k++){
Complex z = gamma.v[k] + G_host_down[i][clus].v[k];
r[off + 2*dim2*i + k] = sw * z.real();
r[off + 2*dim2*i + dim2 + k] = sw * z.imag();
}
}
}
return r;
}
//==============================================================================
vector<double> lattice_model_instance::CDMFT_gradient(const vector<double>& p, int clus)
{
int Nparams = (int)p.size();
int dim = G_host[0][0].r;
int dim2 = dim*dim;
int Nfreq = (int)CDMFT_freqs.size();
bool has_down = (model->mixing & HS_mixing::up_down) != 0;
int n_spins = has_down ? 2 : 1;
int Nrows = 2 * n_spins * Nfreq * dim2;
vector<double> J(Nrows * Nparams, 0.0);
const double delta = global_double("cdmft_jacobian_delta");
for(int j=0; j<Nparams; j++){
double scale = std::max(1.0, std::abs(p[j]));
double dj = delta * scale;
vector<double> pp = p;
pp[j] = p[j] + dj;
vector<double> rp = CDMFT_residuals(pp, clus);
vector<double> pm = p;
pm[j] = p[j] - dj;
vector<double> rm = CDMFT_residuals(pm, clus);
double inv = 1.0 / (2.0 * dj);
for(int row=0; row<Nrows; row++){
J[row * Nparams + j] = (rp[row] - rm[row]) * inv;
}
}
// Restore original parameter values so subsequent calls see consistent state
for(int i=0; i<model->param_set->CDMFT_variational[clus].size(); i++){
model->param_set->set_value(model->param_set->CDMFT_variational[clus][i], p[i]);
}
return J;
}