Hi,
I am a beginner to DMRG, and for the learning purpose, I have started with the paper(Phase diagram of the extended Bose–Hubbard model; DOI 10.1088/1367-2630/14/6/065012), which I intend to reproduce using ITensor. The model extended Bose-Hubbard Hamiltonian, which has the form
H=-J\sum_ib_i^{\dagger}b_i+h.c+\frac{U}{2}\sum_in_i(n_i-1)+V\sum_in_in_{i+1}.
The calculation has been done with J=1, the maximum number of particles per site n_{max}=3 and the average filling \bar{n}=1. The neutral gap and charge gap are the easiest to calculate here. For this purpose, I have used the following code
// test.cpp
#include <iostream>
#include <cmath>
#include <iomanip>
#include <cmath>
#include "itensor/all.h"
#include "meshgrid.hpp"
using namespace itensor;
using namespace std;
using namespace meshgen;
const int precision = 10;
const complex<double> z(0, 1);
const double hop = -1;
const int N = 50; // number of particle
const int L = 50; // number of lattice sites
const int max_occ = 3; // maximum occupancy per site
const int max_value = 10;
const double h = 0.01;
const int max_size = (max_value / h) + 1;
class hamiltonian
{
private:
int L, N, max_occ;
double U_ONSITE, U_NN;
MPS psi;
MPO H;
Sweeps sweeps;
public:
Real en0, en1;
MPS psi0, psi1;
// number of lattice sites
// number of particles
// maximum occupancy
// onsite interaction
// nearest neighour interaction
hamiltonian(int m, int n, int o, double j, double k) : U_ONSITE(j), U_NN(k), L(m), N(n), max_occ(o)
{
auto sites = Boson(N, {"MaxOcc", max_occ});
// Create the Hamiltonian
auto ampo = AutoMPO(sites);
for (int i = 1; i <= N; ++i)
{
// hopping term
if (i < N)
{
ampo += hop, "Adag", i, "A", i + 1;
ampo += hop, "A", i, "Adag", i + 1;
}
// on-site interaction term
ampo += (1.0 / 2.0) * U_ONSITE, "Adag", i, "Adag", i, "A", i, "A", i;
// nearest-neighbor interaction term
if (i < N)
{
ampo += U_NN, "N", i, "N", i + 1;
}
}
H = toMPO(ampo);
vector<int> box(L, 0);
for (int i = 0; i < N; i++)
{
box[i % L] += 1;
}
// Create the initial MPS
auto state = InitState(sites);
for (int i = 1; i <= L; i++)
{
state.set(i, to_string(box[i - 1]));
}
psi = MPS(state);
}
void ground_state()
{
sweeps = Sweeps(20);
sweeps.maxdim() = 10, 20, 30, 40, 50, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300, 330, 360, 400;
sweeps.niter() = 6;
sweeps.noise() = 0.0;
sweeps.cutoff() = 1E-8;
// Run the DMRG calculation
auto [en_g, psi_g] = dmrg(H, psi, sweeps, {"Quiet=", true});
en0 = en_g;
psi0 = psi_g;
}
void excited_state()
{
sweeps = Sweeps(50);
sweeps.maxdim() = 10, 10, 10, 10, 10, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 160, 160, 160, 160, 160, 300, 300, 600, 600, 1200, 1200, 2400, 2400, 4800, 4800;
sweeps.niter() = 6;
sweeps.noise() = 0.0;
sweeps.cutoff() = 1E-8;
auto wfs = vector<MPS>(1);
wfs.at(0) = psi0;
auto [en_e, psi_e] = dmrg(H, wfs, psi, sweeps, {"Quiet=", true, "Weight=", 50.0});
en1 = en_e;
psi1 = psi_e;
}
};
// number of lattice sites
// number of particles
// maximum occupancy
// onsite interaction
// nearest neighour interaction
class nutral_gap : public hamiltonian
{
public:
double ng;
nutral_gap(int m, int n, int o, double j, double k) : hamiltonian(m, n, o, j, k)
{
ground_state();
excited_state();
ng = en1 - en0;
}
};
class charge_gap
{
public:
double cg;
charge_gap(int m, int n, int o, double j, double k)
{
hamiltonian Hl(m, n, o, j, k);
hamiltonian Hll(m - 1, n - 1, o, j, k);
hamiltonian HlL(m + 1, n + 1, o, j, k);
Hl.ground_state();
Hll.ground_state();
HlL.ground_state();
cg = HlL.en0 + Hll.en0 - (2 * Hl.en0);
}
};
int main()
{
vector<double> u_nn(max_size, 0.0);
linspace(u_nn.begin(), u_nn.end(), 0.0, h);
// input value
int c_point;
cin >> c_point;
double U_ONSITE = 5.0;
double U_NN = u_nn[c_point];
nutral_gap NG(L, N, max_occ, U_ONSITE, U_NN);
charge_gap CG(L, N, max_occ, U_ONSITE, U_NN);
double ng = NG.ng;
double cg = CG.cg;
cout << setprecision(precision) << "ng: " << ng << endl;
cout << setprecision(precision) << "cg: " << cg << endl;
cout << "U_ONSITE: " << U_ONSITE << endl;
cout << "U_NN: " << U_NN << endl;
// const int column_width = 20;
// ofstream file;
// file.open("nutral_gap/" + to_string(c_point) + ".txt");
// if (file.is_open())
// {
// file << left << setw(column_width) << "V" << left << setw(column_width) << "U_ONSITE" << left << setw(column_width) << "U_NN";
// file << left << setw(column_width) << "nutral_gap";
// file << left << setw(column_width) << "C_POINT" << endl;
// file << left << setw(column_width) << V << left << setw(column_width) << U_ONSITE << left << setw(column_width) << U_NN;
// file << left << setw(column_width) << ng;
// file << left << setw(column_width) << c_point << endl;
// }
// file.close();
// file.open("charge_gap/" + to_string(c_point) + ".txt");
// if (file.is_open())
// {
// file << left << setw(column_width) << "V" << left << setw(column_width) << "U_ONSITE" << left << setw(column_width) << "U_NN";
// file << left << setw(column_width) << "charge_gap";
// file << left << setw(column_width) << "C_POINT" << endl;
// file << left << setw(column_width) << V << left << setw(column_width) << U_ONSITE << left << setw(column_width) << U_NN;
// file << left << setw(column_width) << cg;
// file << left << setw(column_width) << c_point << endl;
// }
// file.close();
return 0;
}
For each different value of c_point, one can obtain each point of the graph. The U in the Hamiltonian is same U_ONSITE in code, and similarly, V in the Hamiltonian is same as U_NN in code. What I obtained is the following(left-hand image).
I have no idea why the results do not resemble Figure 6 of that paper(right-hand image)!
The charge gap is also not matching, and it got negative values. I have done all the calculations for 50 particles in 50 lattice sites. A comment or a suggestion would be a great help to me.