Program Listing for File fermionBasis.hpp#
↰ Return to documentation for file (models/fermionBasis.hpp
)
#pragma once
#include "nrgcore/qOperator.hpp"
#include "utils/qmatrix.hpp"
#include <cmath>
#include <cstddef>
#include <iostream>
#include <map>
#include <nrgcore/qsymmetry.hpp>
#include <ostream>
#include <set>
class fermionBasis {
public:
enum modelSymmetry {
chargeOnly, // Only charge of the system is conserved.
spinOnly,
chargeAndSpin
}; // Add a key for the exact diagonisation
std::vector<qOperator> f_dag_operator; // f_dag operator
std::vector<std::vector<double>> fnParticle;
fermionBasis(size_t dof, modelSymmetry l) {
// create the basis in nstates x nstates
createFermionBasis(dof); // create the basis in nstates x nstates
// create the switch statement for l
switch (l) {
case chargeOnly:
std::cout << "chargeOnly" << std::endl;
create_QuantumNChargeonly(); // create the quantum numbers
break;
case spinOnly:
std::cout << "spinOnly" << std::endl;
{
std::vector<size_t> tm1;
std::vector<size_t> tm2;
for (size_t j = 0; j < dof; j++) {
if (j % 2 == 0) { // Spin up
tm1.push_back(j);
} else { // Spin down
tm2.push_back(j);
}
}
std::vector<std::vector<size_t>> SpinSz = {tm1, tm2};
create_QuantumSpinOnly(SpinSz); // create the quantum numbers
} //
break;
case chargeAndSpin:
std::cout << "chargeAndSpin" << std::endl;
create_QuantumNspinCharge();
break;
}
create_Block_structure(); // create the block structure
set_f_dag_operators(); // set the fermion operators
DebugView = false; //
}
[[nodiscard]] auto get_raw_f_dag_operator() const { return fermionOprMat; }
[[nodiscard]] auto get_f_dag_operator() const { return f_dag_operator; }
void create_QuantumNChargeonly() {
// Here We assume that
// charge of the individual spin is conserved
nQuantum.clear();
for (size_t i = 0; i < fnParticle[0].size(); i++) {
int tm1{0};
for (auto &j : fnParticle) {
tm1 += static_cast<int>(j[i]);
// std::cout << fnpr[j][i] << " ";
}
nQuantum.push_back({tm1});
// std::cout << ":Q:" << std::vector({tm1, tm2});
// std::cout << std::endl;
}
}
void create_QuantumNspinCharge() {
// Here We assume that
// charge of the individual spin is conserved
nQuantum.clear();
for (size_t i = 0; i < fnParticle[0].size(); i++) {
int tm1{0};
int tm2{0};
for (size_t j = 0; j < fnParticle.size(); j++) {
if (j % 2 == 0) { // Spin up
tm1 += static_cast<int>(fnParticle[j][i]);
} else { // Spin down
tm2 += static_cast<int>(fnParticle[j][i]);
}
// std::cout << fnpr[j][i] << " ";
}
nQuantum.push_back({tm1, tm2});
// std::cout << ":Q:" << std::vector({tm1, tm2});
// std::cout << std::endl;
}
}
void create_QuantumSpinOnly(std::vector<std::vector<size_t>> &qsymmetry) {
// qsymmetry defines the symmetries of propblem
// qsymmetry.size = 2 . If there is four fermion flavours the qsymmetry is
// (Charge of individual spin is conserved)
// i.e qsymmetry = {{0,2},{1,3}}
// check for empty
if (qsymmetry.empty()) {
std::cout << "Warning: qsymmetry is empty" << std::endl;
// create odd even pair for spin up and down.
std::vector<size_t> tm1;
std::vector<size_t> tm2;
for (size_t j = 0; j < fnParticle.size(); j++) {
if (j % 2 == 0) { // Spin up
tm1.push_back(j);
} else { // Spin down
tm2.push_back(j);
}
}
qsymmetry = std::vector<std::vector<size_t>>({tm1, tm2});
}
if (qsymmetry.size() != 2) {
throw std::runtime_error("qsymmetry.size() != 2");
}
nQuantum.clear();
for (size_t i = 0; i < fnParticle[0].size(); i++) {
std::vector<int> tmVec;
{
size_t iq = 0;
int tm1{0}; // up spin
int tm2{0}; // down spin
for (auto jq : qsymmetry[iq]) {
tm1 += static_cast<int>(fnParticle[jq][i]);
}
iq = 1;
for (auto jq : qsymmetry[iq]) {
tm2 += static_cast<int>(fnParticle[jq][i]);
}
tmVec.push_back(tm1 - tm2);
}
nQuantum.push_back(tmVec);
// std::cout << ":Q:" << std::vector({tm1, tm2});
// std::cout << std::endl;
}
}
void createQNumbers(const std::vector<std::vector<size_t>> &qsymmetry) {
// qsymmetry defines the symmetries of propblem
// qsymmetry.size = 2 for a system with charge and conserved
// (Charge of individual spin is conserved)
// i.e qsymmetry = {{0,2},{1,3}}
nQuantum.clear();
for (size_t i = 0; i < fnParticle[0].size(); i++) {
std::vector<int> tmVec;
for (const auto &iq : qsymmetry) {
int tm1{0};
for (auto jq : iq) {
tm1 += static_cast<int>(fnParticle[jq][i]);
}
tmVec.push_back(tm1);
}
nQuantum.push_back(tmVec);
// std::cout << ":Q:" << std::vector({tm1, tm2});
// std::cout << std::endl;
}
}
void create_Block_structure() {
nQBlocks.clear();
unique_Qnumbers.clear();
// unique_Indices of the quantum numbers
std::vector<size_t> unique_Indices;
{
std::set<std::vector<int>> tm;
for (size_t i = 0; i < fnParticle[0].size(); i++) {
if (tm.insert(nQuantum[i]).second) {
unique_Indices.push_back(i);
}
}
}
// sub blocks indices where Quantum numbers are same.
for (auto i : unique_Indices) {
unique_Qnumbers.push_back(nQuantum[i]);
std::vector<size_t> tm1{i};
for (size_t j = i; j < fnParticle[0].size(); j++) {
if (nQuantum[i] == nQuantum[j]) {
if (i != j) {
tm1.push_back(j);
}
}
}
if (DebugView) {
std::cout << "Coupled : " << nQuantum[i] << " : ";
for (auto j : tm1) {
for (auto &jx : fnParticle) {
std::cout << jx[j] << " ";
}
std::cout << std::endl;
}
}
// << tm1 << std::endl;
nQBlocks.push_back(tm1);
}
// std::cout << "nQBlocks : " << nQBlocks.size() << std::endl;
//
}
auto get_unique_Qnumbers() {
if (unique_Qnumbers.empty()) {
std::cout << "Warning: unique_Qnumbers is empty !" << std::endl;
}
return unique_Qnumbers;
}
std::vector<qOperator>
get_block_operators(const std::vector<qmatrix<>> &sys_operators) {
// This function can be used to calculate any other
// operators which are a combinations of the f operators
std::vector<qOperator> block_operators(sys_operators.size(), qOperator());
for (size_t i = 0; i < nQBlocks.size(); i++) {
for (size_t j = 0; j < nQBlocks.size(); j++) {
size_t tdim = nQBlocks[i].size();
size_t tdim_p = nQBlocks[j].size();
for (size_t ipr = 0; ipr < sys_operators.size(); ipr++) {
qmatrix<> fmatrix(tdim, tdim_p, 0);
double tvalue{0};
for (size_t ik = 0; ik < nQBlocks[i].size(); ik++) {
for (size_t ik_p = 0; ik_p < nQBlocks[j].size(); ik_p++) {
fmatrix(ik, ik_p) =
sys_operators[ipr](nQBlocks[i][ik], nQBlocks[j][ik_p]);
tvalue += std::fabs(
sys_operators[ipr](nQBlocks[i][ik], nQBlocks[j][ik_p]));
}
}
if (tvalue > 0) {
block_operators[ipr].set(fmatrix, i, j);
}
}
}
} // End of operator
return block_operators;
}
qOperator get_block_Hamiltonian(const qmatrix<double> &sys_operators) {
// This function can be used to calculate any other
// check the Hamiltonian is Harmitian i.e H == H.T
{
double tvalue = 0;
for (size_t i = 0; i < sys_operators.getcolumn(); i++) {
for (size_t j = i + 1; j < sys_operators.getrow(); j++) {
tvalue += std::fabs(sys_operators(i, j) - sys_operators(j, i));
}
}
if (tvalue > 1e-10) {
throw std::runtime_error("Hamiltonian is not Hermitian\n ");
}
}
// operators which are a combinations of the f operators
// std::cout << "&sys_operators" << sys_operators;
qOperator block_operators;
for (size_t i = 0; i < nQBlocks.size(); i++) {
size_t tdim = nQBlocks[i].size();
qmatrix<> fmatrix(tdim, tdim, 0);
for (size_t ik = 0; ik < nQBlocks[i].size(); ik++) {
for (size_t ik_p = 0; ik_p < nQBlocks[i].size(); ik_p++) {
fmatrix(ik, ik_p) = sys_operators(nQBlocks[i][ik], nQBlocks[i][ik_p]);
}
}
block_operators.set(fmatrix, i, i);
} //
// test all the offdiagonal elements are zero
double tvalue = 0;
for (size_t i = 0; i < nQBlocks.size(); i++) {
for (size_t j = i + 1; j < nQBlocks.size(); j++) {
for (size_t ik = 0; ik < nQBlocks[i].size(); ik++) {
for (size_t ik_p = 0; ik_p < nQBlocks[j].size(); ik_p++) {
tvalue += sys_operators(nQBlocks[i][ik], nQBlocks[j][ik_p]);
}
}
}
}
if (std::abs(tvalue) > 1e-10) {
throw std::runtime_error("Warning: Hamiltonian is not block diagonal \n");
}
//
// / ///////////////////////////////////////
return block_operators;
}
// End of operator
[[nodiscard]] auto get_basis() const { return fnParticle; }
void setDebugMode(bool debug = true) { DebugView = debug; }
void createFermionBasis(size_t ldof) {
qmatrix<double> fdag({0, 0, 1, 0});
qmatrix<double> sigz({1, 0, 0, -1});
qmatrix<double> id2({1, 0, 0, 1});
fermionOprMat.clear();
for (size_t i = 0; i < ldof; i++) {
qmatrix<> prev(1, 1, 1);
for (size_t j = 0; j < ldof - i - 1; j++) {
prev = id2.krDot(prev);
}
qmatrix<> nxt(1, 1, 1);
for (size_t j = 0; j < i; j++) {
nxt = sigz.krDot(nxt);
}
fermionOprMat.push_back(prev.krDot(fdag.krDot(nxt)));
}
fnParticle.clear();
// std::cout << fup << fdw;
for (size_t i = 0; i < ldof; i++) {
fnParticle.push_back(
(fermionOprMat[i].dot(fermionOprMat[i].cTranspose())).getdiagonal());
}
//
}
std::vector<qmatrix<>> fermionOprMat;
void set_f_dag_operators() {
f_dag_operator = get_block_operators(fermionOprMat);
}
private:
bool DebugView{false};
// foperator in the full basis
// no of particles operator
// basis states -- and quantum numbers
// n_up n_down as a quntum number
std::vector<std::vector<int>> nQuantum;
std::vector<std::vector<int>> unique_Qnumbers;
// Blocked no of particles i.e.,
std::vector<std::vector<size_t>> nQBlocks;
};