Program Listing for File nrgcore.hpp#
↰ Return to documentation for file (nrgcore/nrgcore.hpp
)
#pragma once
#include "qOperator.hpp"
#include "utils/qmatrix.hpp"
#include "utils/timer.hpp"
#include <algorithm>
#include <iostream>
#include <map>
#include <numeric>
#include <optional>
#include <stdexcept>
#include <tuple>
#include <vector>
// Take two hamiltonian as a template parameter
// Create a new hamiltonian
template <typename im_type, // Impurity type
typename bath_type> // bath type
class nrgcore {
im_type *impurityModel;
bath_type *bath_model;
//
std::vector<qOperator> pre_fdag_oparator;
public:
nrgcore(im_type &im_hamilt, bath_type &bt_hamilt)
: impurityModel(&im_hamilt), bath_model(&bt_hamilt),
chi_bath(bath_model->get_chi_Q()),
bath_eigenvaluesQ(bath_model->get_eigenvaluesQ()),
nq_bath(bath_model->get_basis()) {
// create the staring basis states ....
// TODO(sp): bath_eigenvaluesQ may be different on each wilson site
set_parameters(); // set the default parameters
test();
}
void add_bath_site(const std::vector<double> &thopping, double rescale) {
timer t1("add_bath_site " + std::to_string(nrg_iterations_cnt));
// copy the bath model stuff. This important for superconductor bath
// this was not if we had used pointer.
bath_eigenvaluesQ = bath_model->get_eigenvaluesQ();
nq_bath = bath_model->get_basis();
chi_bath = bath_model->get_chi_Q();
//
if (nrg_iterations_cnt == 0) {
// This is only for the first wilson site
eigenvaluesQ = impurityModel->get_eigenvaluesQ();
pre_sysmQ = impurityModel->get_basis();
pre_fdag_oparator = impurityModel->f_dag_operator;
discard_higher_energies();
}
//
create_next_basis();
create_next_hamiltonians(thopping, rescale);
std::cout << "Done create_next_hamiltonians at " << t1.getDuration()
<< std::endl;
// solve all the hamiltonians
eigenvaluesQ.clear(); // TODO(sp): save values See if one needs
eigenvaluesQ.resize(current_hamiltonQ.size(), {});
for (size_t i = 0; i < current_hamiltonQ.size(); i++) {
// std::cout << current_hamiltonQ[i].getcolumn() << " "
// << current_hamiltonQ[i].getrow() << std::endl
// if (current_sysmQ[i] == std::vector{1, 1})
// std::cout << "Nqi" << current_sysmQ[i] << "current_hamiltonQ"
// << current_hamiltonQ[i];
eigenvaluesQ[i] = current_hamiltonQ[i].diag();
// std::cout << "eigenvaluesQ" << eigenvaluesQ[i];
}
std::cout << "Done Diag " << t1.getDuration() << std::endl;
set_current_fdag_operator();
std::cout << "Done set_current_fdag_operator: " << t1.getDuration()
<< std::endl;
// discard higher energy states.
// This should be done after set_current_fdag_operator
// as previous eigenvaluesQ_kept_indices is needed
// for set_current_fdag_operator
// update_internal_state();
}
void update_internal_state() {
nrg_iterations_cnt++;
discard_higher_energies();
pre_sysmQ = std::move(current_sysmQ);
}
void test() {
// Number of quantum number
if (impurityModel->n_Q[0].size() != bath_model->n_Q[0].size()) {
throw std::runtime_error(
"Number of quantum number is different on model and impurity!");
}
// foperator size
if (impurityModel->f_dag_operator.size() !=
bath_model->f_dag_operator.size()) {
std::string thString =
"f_dag_operator.size is different on model and impurity!\n"
"impurityModel->f_dag_operator.size() = " +
std::to_string(impurityModel->f_dag_operator.size()) +
"bath_model->f_dag_operator.size() = " +
std::to_string(bath_model->f_dag_operator.size());
throw std::runtime_error(thString);
}
}
void create_next_hamiltonians(const std::vector<double> &t_hopping, // NOLINT
double rescale) {
if (t_hopping.size() != pre_fdag_oparator.size()) {
throw std::runtime_error(
std::string("t_hopping ") + std::to_string(t_hopping.size()) +
" =! pre_fdag_oparator " + std::to_string(pre_fdag_oparator.size()));
}
std::cout << "-------------------------------------------------------------"
<< std::endl;
std::cout << "Adding Wilson site " << nrg_iterations_cnt
<< " thopping:" << t_hopping << " rescale:" << rescale
<< std::endl;
current_hamiltonQ.clear();
for (size_t licq = 0; licq < coupled_nQ_index.size(); licq++) {
current_hamiltonQ.emplace_back();
}
#pragma omp parallel for // NOLINT
for (size_t licq = 0; licq < coupled_nQ_index.size(); licq++) {
auto lindex = coupled_nQ_index[licq];
size_t ldim = 0;
for (auto kindex : lindex) {
auto ii = kindex / nq_bath.size(); // impurity nqi index
auto bb = kindex % nq_bath.size(); // bath nqi index
ldim = ldim + eigenvaluesQ_kept_indices[ii].size() *
bath_eigenvaluesQ[bb].size();
}
// TODO(sp): Not all the eigenvalues are always available
// create the local nqi hamiltonian
qmatrix<double> h_nqi(ldim, ldim, 0);
// std::cout << "-------------------------------------";
// std::cout << "ldim " << ldim << " lindex " << lindex << std::endl;
size_t kdim = 0;
for (auto kindex : lindex) {
auto ii = kindex / nq_bath.size(); // impurity nqi index
auto bb = kindex % nq_bath.size(); // bath nqi index
// set diagonal matrix elements
for (size_t it : eigenvaluesQ_kept_indices[ii]) {
for (size_t il = 0; il < bath_eigenvaluesQ[bb].size(); il++) {
// this assumes that the diagonal and off-diagonal elements of the
// impurity and bath site hamiltonians are diagonal;
h_nqi(kdim + it + il * eigenvaluesQ_kept_indices[ii].size(),
kdim + it + il * eigenvaluesQ_kept_indices[ii].size()) =
eigenvaluesQ[ii][it] * rescale + bath_eigenvaluesQ[bb][il];
}
}
size_t kpdim = 0;
for (auto kp_index : lindex) {
auto ii_p = kp_index / nq_bath.size(); // impurity nqi index
auto bb_p = kp_index % nq_bath.size(); // bath nqi index
// get f_operators of the bath and
// f_dag_opr_list of the previous
// site.
// This is optional, mean sometime the operator
// does not exist i.e.,{0}
for (size_t ip = 0; ip < pre_fdag_oparator.size(); ip++) {
auto fdag_opt =
pre_fdag_oparator[ip].get(ii, ii_p); // previous f f_operators
auto f_bath_opt = bath_model->f_dag_operator[ip].get(
bb_p, bb); // get the f_dag operator
// auto f_bath_opt = bath_model->f_operator[ip].get(bb, bb_p);
if (fdag_opt && f_bath_opt) {
auto *f_dag_prev =
fdag_opt.value(); // This is a pointer to qmatrix
auto f_bath = f_bath_opt.value();
for (size_t it : eigenvaluesQ_kept_indices[ii]) {
for (size_t il = 0; il < bath_eigenvaluesQ[bb].size(); il++) {
for (size_t it_p : eigenvaluesQ_kept_indices[ii_p]) {
for (size_t il_p = 0; il_p < bath_eigenvaluesQ[bb_p].size();
il_p++) {
// sum over all the f operator
double aa = 0;
// std::cout << it << it_p << il << il_p << std::endl;
aa += (f_dag_prev->at(it, it_p)) //
* (f_bath->at(il_p, il)) //
* (chi_bath[bb_p]);
h_nqi(
kdim + it + il * eigenvaluesQ_kept_indices[ii].size(),
kpdim + it_p +
il_p * eigenvaluesQ_kept_indices[ii_p].size()) +=
aa * t_hopping[ip];
h_nqi(kpdim + it_p +
il_p * eigenvaluesQ_kept_indices[ii_p].size(),
kdim + it +
il * eigenvaluesQ_kept_indices[ii].size()) +=
aa * t_hopping[ip];
}
}
// end of iner loop
}
}
}
// end off local nqi index
}
kpdim = kpdim + eigenvaluesQ_kept_indices[ii_p].size() *
bath_eigenvaluesQ[bb_p].size();
}
kdim = kdim + eigenvaluesQ_kept_indices[ii].size() *
bath_eigenvaluesQ[bb].size();
}
current_hamiltonQ[licq] = h_nqi;
}
}
void create_next_basis() {
coupled_nQ_index.clear();
current_sysmQ.clear();
for (size_t i = 0; i < pre_sysmQ.size(); i++) {
for (size_t j = 0; j < nq_bath.size(); j++) {
auto tm = pre_sysmQ[i];
for (size_t ix = 0; ix < nq_bath[j].size(); ix++) {
tm[ix] += nq_bath[j][ix];
}
// check if tm is already exist or not
bool ex = false;
for (size_t ix = 0; ix < current_sysmQ.size(); ix++) {
if (tm == current_sysmQ[ix]) {
ex = true;
coupled_nQ_index[ix].push_back({i * nq_bath.size() + j});
}
}
if (!ex) {
current_sysmQ.push_back(tm);
coupled_nQ_index.push_back({i * nq_bath.size() + j});
}
}
}
};
// function to create in the nrgcore
void discard_higher_energies() {
all_eigenvalue.clear();
for (auto aa : eigenvaluesQ) {
all_eigenvalue.insert(all_eigenvalue.end(), aa.begin(), aa.end());
}
std::sort(all_eigenvalue.begin(), all_eigenvalue.end());
eigenvaluesQ_kept_indices.clear();
double En_max = all_eigenvalue[all_eigenvalue.size() - 1];
double En_min = all_eigenvalue[0];
if (all_eigenvalue.size() <= max_kept_states) {
for (auto &aa : eigenvaluesQ) {
std::vector<size_t> tm(aa.size());
std::iota(tm.begin(), tm.end(), 0);
eigenvaluesQ_kept_indices.push_back(tm);
}
} else {
En_max = all_eigenvalue[max_kept_states];
// size_t tdim{max_kept_states};
for (size_t i = max_kept_states + 1; i < all_eigenvalue.size(); i++) {
if (std::fabs(all_eigenvalue[i] - all_eigenvalue[max_kept_states]) <
errorbarInEnergy) {
// std::cout << "En_max" << En_max << " " << all_eigenvalue[i]
// << std::endl;
// tdim++;
}
}
// Set kept indices
// check if higher energy discardtion is needed
for (auto &aa : eigenvaluesQ) {
std::vector<size_t> tm;
size_t id = 0;
for (auto &bb : aa) {
if (bb < En_max + errorbarInEnergy) {
tm.push_back(id);
}
id++;
}
eigenvaluesQ_kept_indices.push_back(tm);
}
}
// Save the energy eigenvalues
size_t tmp_kept{0};
for (auto &aa : eigenvaluesQ_kept_indices) {
tmp_kept += aa.size();
}
no_of_kept_states = tmp_kept;
std::cout << "NRG: Iteration " << nrg_iterations_cnt << " N:K:kp "
<< all_eigenvalue.size() << ":" << tmp_kept << ":"
<< no_of_kept_states << std::endl;
std::cout << "Ground state energy: " << all_eigenvalue[0]
<< " MaxEn(kept): " << En_max << std::endl;
// std::cout << "eigenvaluesQ_kept_indices" << eigenvaluesQ_kept_indices;
if (no_of_kept_states <= max_kept_states) {
nrg_iterations_min = nrg_iterations_cnt;
}
// Set energy values relative to the ground state.
else { // Shift energy of the ground state energy only if the higher
// energy state energy discarded
relativeGroundStateEnergy.push_back(En_min);
for (auto &aa : eigenvaluesQ) {
std::transform(aa.begin(), aa.end(), aa.begin(),
[En_min](double a) { return a - En_min; });
// for (auto &bb : aa) {
// bb = bb - En_min;
// }
}
}
}
void set_parameters(size_t n = 1024) {
no_of_kept_states = n;
errorbarInEnergy = 1e-8;
nrg_iterations_cnt = 0;
nrg_iterations_min = 0;
max_kept_states = no_of_kept_states;
}
// variables
std::vector<std::vector<int>> get_basis_nQ() {
if (current_sysmQ.empty()) {
throw std::runtime_error("current_sysmQ.empty!");
}
return current_sysmQ; // because current stuffs are move
}
std::vector<std::vector<double>> get_eigenvaluesQ() {
if (eigenvaluesQ.empty()) {
throw std::runtime_error("eigenvaluesQ.empty!");
}
return eigenvaluesQ;
}
auto get_f_dag_operator() { return pre_fdag_oparator; }
bool checkHigherEnergyDiscarded() {
return no_of_kept_states >= max_kept_states;
}
private:
void set_current_fdag_operator() { // NOLINT
for (auto &aa : pre_fdag_oparator) {
aa.clear();
}
//
std::map<std::array<size_t, 3>, std::vector<std::array<size_t, 6>>> foprIdx;
//
timer t1("set_current_fdag_operator");
for (size_t ip = 0; ip < pre_fdag_oparator.size(); ip++) {
// find the i,j index of the basis
for (size_t i = 0; i < current_sysmQ.size(); i++) {
for (size_t j = 0; j < current_sysmQ.size(); j++) {
// TODO(sp): Not all the eigenvalues are always available
auto idx = coupled_nQ_index[i];
auto idx_p = coupled_nQ_index[j];
// std::cout << "--------------------------";
// bool checkValue = false;
size_t kidx = 0;
for (auto kindex : idx) {
auto ii = kindex / nq_bath.size(); // impurity nqi index
auto bb = kindex % nq_bath.size(); // bath nqi index
size_t kidx_p = 0;
for (auto kindex_p : idx_p) {
auto ii_p = kindex_p / nq_bath.size(); // impurity nqi index
auto bb_p = kindex_p % nq_bath.size(); // bath nqi index
auto fdag_bath_opt = bath_model->f_dag_operator[ip].get(bb, bb_p);
if (ii == ii_p && fdag_bath_opt) {
// checkValue = true;
foprIdx[{ip, i, j}].push_back(
{ii, bb, ii_p, bb_p, kidx, kidx_p});
}
kidx_p += eigenvaluesQ_kept_indices[ii_p].size() *
bath_eigenvaluesQ[bb_p].size();
}
kidx += eigenvaluesQ_kept_indices[ii].size() *
bath_eigenvaluesQ[bb].size();
}
}
}
}
// start the foprIdx Iteration
std::cout << "--------------------------" << t1.getDuration() << std::endl;
std::vector<qmatrix<double>> qfr(foprIdx.size());
// std::cout << "idx" << idx << " idx_p" << idx_p << std::endl;
// #pragma omp parallel for
for (auto itf = foprIdx.begin(); itf != foprIdx.end(); itf++) {
//
//
auto ip = itf->first[0];
auto i = itf->first[1];
auto j = itf->first[2];
size_t kpdim = eigenvaluesQ[i].size();
size_t kpdim_p = eigenvaluesQ[j].size();
qmatrix<double> tmat(kpdim, kpdim_p, 0);
// value
//
for (auto const &itd : itf->second) {
auto ii = itd[0];
auto bb = itd[1];
// auto ii_p = itd[2];
auto bb_p = itd[3];
//
size_t kidx = itd[4];
size_t kidx_p = itd[5];
//
// auto fdag_bath = bath_model->get_fdag_operator(bb, bb_p);
// create previous bath id matrix
// std::cout << "bb:bb_p" << bb << ":" << bb_p << std::endl;
auto fdag_bath_opt = bath_model->f_dag_operator[ip].get(bb, bb_p);
auto fdag_bath = fdag_bath_opt.value();
// TODO(sp): check the pragma openmp effect for the multi orbital
//
// #pragma omp parallel for collapse(2)
for (size_t il = 0; il < bath_eigenvaluesQ[bb].size(); il++) {
for (size_t il_p = 0; il_p < bath_eigenvaluesQ[bb_p].size(); il_p++) {
double rvalue = fdag_bath->at(il, il_p);
for (size_t it : eigenvaluesQ_kept_indices[ii]) {
tmat(kidx + it + il * eigenvaluesQ_kept_indices[ii].size(),
kidx_p + it + il_p * eigenvaluesQ_kept_indices[ii].size()) +=
rvalue;
// fdag_bath->at(il, il_p);
}
}
}
} // set the operator
// pre_fdag_oparator[ip].set(current_hamiltonQ[i].cTranspose().dot(tmat.dot(
// current_hamiltonQ[j])), //
// UnitaryTransform
// i, j);
qfr[std::distance(foprIdx.begin(), itf)] =
current_hamiltonQ[i].cTranspose().dot(tmat.dot(current_hamiltonQ[j]));
// end of ip loop
}
std::cout << "--------------------------" << t1.getDuration() << std::endl;
// set the foparator
for (auto itf = foprIdx.begin(); itf != foprIdx.end(); itf++) {
//
//
auto ip = itf->first[0];
auto i = itf->first[1];
auto j = itf->first[2];
pre_fdag_oparator[ip].set(qfr[std::distance(foprIdx.begin(), itf)], i, j);
}
}
void enforceDegeneracy() {
// enforce the degenarecy of the energy levels
for (auto &aa : eigenvaluesQ) {
for (auto &bb : aa) {
for (auto &aa_p : eigenvaluesQ) {
for (auto &bb_p : aa_p) {
if (std::fabs(bb - bb_p) < errorbarInEnergy) {
double tmp = std::min(bb, bb_p);
bb_p = tmp;
bb = tmp;
}
}
}
if (std::abs(bb) < 1e-10) {
std::cout << "degeneracy found" << std::endl;
}
}
}
}
void set_current_fdag_operator_old() { // NOLINT
for (auto &aa : pre_fdag_oparator) {
aa.clear();
}
//
for (size_t i = 0; i < current_sysmQ.size(); i++) {
for (size_t j = 0; j < current_sysmQ.size(); j++) {
// TODO(sp): Not all the eigenvalues are always available
size_t kpdim = eigenvaluesQ[i].size();
size_t kpdim_p = eigenvaluesQ[j].size();
auto idx = coupled_nQ_index[i];
auto idx_p = coupled_nQ_index[j];
// std::cout << "--------------------------";
// std::cout << "idx" << idx << " idx_p" << idx_p << std::endl;
std::vector<qmatrix<double>> c_fopr(pre_fdag_oparator.size(),
qmatrix<double>(kpdim, kpdim_p, 0));
std::vector<bool> hasValue(pre_fdag_oparator.size(), false);
size_t kidx = 0;
for (auto kindex : idx) {
auto ii = kindex / nq_bath.size(); // impurity nqi index
auto bb = kindex % nq_bath.size(); // bath nqi index
size_t kidx_p = 0;
for (auto kindex_p : idx_p) {
auto ii_p = kindex_p / nq_bath.size(); // impurity nqi index
auto bb_p = kindex_p % nq_bath.size(); // bath nqi index
if (ii == ii_p) {
// auto fdag_bath = bath_model->get_fdag_operator(bb, bb_p);
// create previous bath id matrix
for (size_t ip = 0; ip < pre_fdag_oparator.size(); ip++) {
auto fdag_bath_opt =
bath_model->f_dag_operator[ip].get(bb, bb_p);
if (fdag_bath_opt) {
// std::cout << "bb:bb_p" << bb << ":" << bb_p << std::endl;
hasValue[ip] = true;
auto fdag_bath = fdag_bath_opt.value();
for (size_t il = 0; il < bath_eigenvaluesQ[bb].size(); il++) {
for (size_t il_p = 0; il_p < bath_eigenvaluesQ[bb_p].size();
il_p++) {
for (size_t it : eigenvaluesQ_kept_indices[ii]) {
c_fopr[ip](
kidx + it +
il * eigenvaluesQ_kept_indices[ii].size(),
kidx_p + it +
il_p * eigenvaluesQ_kept_indices[ii].size()) +=
fdag_bath->at(il, il_p);
}
}
}
}
}
}
kidx_p += eigenvaluesQ_kept_indices[ii_p].size() *
bath_eigenvaluesQ[bb_p].size();
}
kidx += eigenvaluesQ_kept_indices[ii].size() *
bath_eigenvaluesQ[bb].size();
}
// set the matrix elements
// end of lm loop
// End of matrix generation.
// Rotate the c operator in the eigen basis
for (size_t iv = 0; iv < hasValue.size(); iv++) {
if (hasValue[iv]) {
pre_fdag_oparator[iv].set(
current_hamiltonQ[i].cTranspose().dot(
c_fopr[iv].dot(current_hamiltonQ[j])), // UnitaryTransform
i, j);
}
} // End of loop save
// std::cout << "c_fopr" << c_fopr;
}
}
}
// Store and set previous hamiltonian
// indices of of coupled system and bath n_Q i.e.,
// which subspaces are connected
// get the bath stuff so that we dont have to create them
// in every site
std::vector<double> chi_bath;
// Construct the nrgcore
// from two hamiltonian type
size_t no_of_kept_states{0};
size_t max_kept_states{0};
double errorbarInEnergy{0};
public:
std::vector<double> all_eigenvalue;
std::vector<double> relativeGroundStateEnergy;
// No need save this for backward iteration.
std::vector<std::vector<size_t>> eigenvaluesQ_kept_indices;
std::vector<qmatrix<double>> current_hamiltonQ; // next hamiltonians
std::vector<std::vector<int>> current_sysmQ; // next symmetries
std::vector<std::vector<int>> pre_sysmQ; // previous symmetries
std::vector<std::vector<double>> eigenvaluesQ; // Eigenvalues
std::vector<std::vector<size_t>> coupled_nQ_index;
// These are not update on each wilson site
int nrg_iterations_cnt{};
int nrg_iterations_min{};
std::vector<std::vector<double>> bath_eigenvaluesQ;
std::vector<std::vector<int>> nq_bath;
std::vector<qOperator> *getPreWilsonSiteOperators() {
return &pre_fdag_oparator;
}
};