Partial diagonalization of a one-dimensional Hubbard-Holstein model
The model describes behavior of strongly correlated electrons coupled to localized phonons [PBK74], [TC03]. The Hamiltonian of the model defined on a chain with \(L\) sites reads
where \(c^\dagger_{i,\sigma}\), \(c_{i,\sigma}\) and
\(n_{i,\sigma}\) are electron creation, annihilation and occupation number
operators respectively. Operators \(a^\dagger_i\), \(a_i\) create and
destroy phonons localized at the chain site \(i\). \(t\) is the hopping
constant of the electrons, \(\mu\) is the chemical potential, and \(U\)
is the on-site Coulomb repulsion constant. The localized phonons have frequency
\(\omega\) and are coupled to the electrons with strength \(g\).
Each localized phonon is allowed to occupy only two states, \(|0\rangle\)
and \(|1\rangle\). Diagonalization is performed within the sector with
\(N_{el} = 2\) electrons by means of Eigen’s SelfAdjointEigenSolver
.
1// 'state_vector_eigen3.hpp' must be included before 'libcommute.hpp'.
2// This way libcommute will know how to deal with Eigen's vector types.
3#include <libcommute/loperator/state_vector_eigen3.hpp>
4
5#include <libcommute/libcommute.hpp>
6
7#include <Eigen/Eigenvalues>
8
9using namespace libcommute;
10
11int main() {
12 int const L = 4; // Length of the chain
13 int const b = 1; // 2^b phonon states per chain site
14 double const t = 0.5; // Electron hopping constant
15 double const mu = 1.0; // Chemical potential
16 double const U = 2.0; // Coulomb repulsion
17 double const w = 2.0; // Phonon frequency
18 double const g = 0.1; // Electron-phonon coupling
19
20 // Expression of the Hubbard-Holstein Hamiltonian to be constructed.
21 // Every creation and annihilation operator found in the expression carries
22 // an integer index (chain site) and a string index (spin).
23 static_indices::expr_real<int, std::string> H;
24
25 // The following functions return operators with statically typed indices.
26 using static_indices::c_dag; // Create an electron
27 using static_indices::c; // Destroy an electron
28 using static_indices::n; // Number of electrons
29 using static_indices::a_dag; // Create a phonon
30 using static_indices::a; // Destroy a phonon
31
32 // Electron hopping terms of H.
33 for(auto spin : {"up", "down"}) {
34 for(int i = 0; i < L - 1; ++i) {
35 H += -t * (c_dag(i, spin) * c(i + 1, spin) + hc);
36 }
37 }
38 // Chemical potential terms.
39 for(int i = 0; i < L; ++i)
40 H += -mu * (n(i, "up") + n(i, "down"));
41 // Coulomb repulsion terms.
42 for(int i = 0; i < L; ++i)
43 H += U * n(i, "up") * n(i, "down");
44 // Energy of the localized phonons.
45 for(int i = 0; i < L; ++i)
46 H += w * a_dag(i, "") * a(i, "");
47 // Electron-phonon coupling.
48 for(int i = 0; i < L; ++i)
49 H += g * (n(i, "up") + n(i, "down")) * (a_dag(i, "") + a(i, ""));
50
51 // Automatically analyse structure of 'H' and construct a Hilbert space.
52 // Only the lowest 2^b states will be accounted for for each localized phonon.
53 auto hs = make_hilbert_space(H, boson_es_constructor(b));
54 std::cout << "Full Hilbert space dimension: " << hs.dim() << '\n';
55
56 // Construct an 'loperator' object that represents action of the expression
57 // 'H' on state vectors in the Hilbert space 'hs'.
58 auto Hop = make_loperator(H, hs);
59
60 // Diagonalize the 2-electron sector of the model.
61 // using Eigen 3 and the 'n_fermion_sector_view' class.
62 int const N_el = 2;
63
64 auto sec_size = n_fermion_sector_size(hs, N_el);
65 std::cout << "Size of the N_el = 2 sector: " << sec_size << '\n';
66
67 // Preallocate a Hamiltonian matrix to be filled and diagonalized. Note that
68 // we have to store matrix elements only within the small sector.
69 Eigen::MatrixXd Hmat(sec_size, sec_size);
70
71 // Preallocate an input state vector.
72 Eigen::VectorXd psi(sec_size);
73
74 // Create a constant 2-fermion sector view of the source vector 'psi'.
75 // The view makes the small vector 'psi' compatible with 'Hop', which wants
76 // to act in the full Hilbert space.
77 auto psi_view = make_const_nfs_view(psi, hs, N_el);
78
79 // Fill 'Hmat'.
80 for(int k = 0; k < (int)sec_size; ++k) {
81 // Reset vector |\psi> to zero.
82 psi = Eigen::VectorXd::Zero((int)sec_size);
83 psi[k] = 1; // 'psi' is the k-th basis vector now.
84
85 // Create a 2-fermion sector view of the k-th column of 'Hmat'.
86 auto HMat_col_view = make_nfs_view(Hmat.col(k), hs, N_el);
87
88 // Act with 'Hop' on the k-th sector basis state and write the result into
89 // the k-th column of 'Hmat'.
90 Hop(psi_view, HMat_col_view);
91 }
92
93 // Diagonalize 'Hmat'.
94 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> diag(Hmat,
95 Eigen::EigenvaluesOnly);
96
97 std::cout << "10 lowest eigenvalues of the N_el = " << N_el
98 << " sector: " << diag.eigenvalues().head(10).transpose() << '\n';
99
100 return 0;
101}
Note
A Krylov subspace iteration eigensolver used instead of Eigen’s
SelfAdjointEigenSolver
would require only repeated evaluation of
\(\hat H|\psi\rangle\). Therefore, it would be unnecessary to store and
fill the whole matrix Hmat
at once.
“Low-temperature properties of the one-dimensional polaron band. I. Extreme-band-narrowing regime”, G. Beni, P. Pincus and J. Kanamori, Phys. Rev. B 10, pp. 1896-1901 (1974), https://doi.org/10.1103/PhysRevB.10.1896
“Possibility of a metallic phase in the charge-density-wave–spin-density-wave crossover region in the one-dimensional Hubbard-Holstein model at half filling”, Y. Takada and A. Chatterjee, Phys. Rev. B 8, p. 081102 (2003), https://doi.org/10.1103/PhysRevB.67.081102