TRIQS/TRIQS 4.0.0
Researching Interacting Quantum Systems
Loading...
Searching...
No Matches
functions.cpp
1// Copyright (c) 2017-2018 Commissariat à l'énergie atomique et aux énergies alternatives (CEA)
2// Copyright (c) 2017-2018 Centre national de la recherche scientifique (CNRS)
3// Copyright (c) 2018-2023 Simons Foundation
4// Copyright (c) 2017 Igor Krivenko
5//
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10//
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You may obtain a copy of the License at
17// https://www.gnu.org/licenses/gpl-3.0.txt
18//
19// Authors: Michel Ferrero, Alexander Hampel, Igor Krivenko, Olivier Parcollet, Nils Wentzell
20
21#include "../atom_diag.hpp"
22#include "../functions.hpp"
23#include "../../arrays.hpp"
27
28#include <algorithm>
29#include <cmath>
30#include <complex>
31#include <vector>
32
33namespace triqs::atom_diag {
34
35 using namespace triqs::arrays;
36
37#define ATOM_DIAG atom_diag<Complex>
38#define ATOM_DIAG_R atom_diag<false>
39#define ATOM_DIAG_C atom_diag<true>
40#define ATOM_DIAG_T typename atom_diag<Complex>
41
42 template <bool Complex> double partition_function(ATOM_DIAG const &atom, double beta) {
43 double z = 0;
44 for (auto const &es : atom.get_eigensystems()) z += sum(exp(-beta * es.eigenvalues));
45 return z;
46 }
47 template double partition_function(ATOM_DIAG_R const &, double);
48 template double partition_function(ATOM_DIAG_C const &, double);
49
50 // -----------------------------------------------------------------
51 template <bool Complex> ATOM_DIAG_T::block_matrix_t atomic_density_matrix(ATOM_DIAG const &atom, double beta) {
52 double z = partition_function(atom, beta);
53 int n_blocks = atom.n_subspaces();
54 ATOM_DIAG_T::block_matrix_t dm(n_blocks);
55 for (int bl = 0; bl < n_blocks; ++bl) {
56 int bl_size = atom.get_subspace_dim(bl);
57 dm[bl] = ATOM_DIAG_T::matrix_t(bl_size, bl_size);
58 for (int u = 0; u < bl_size; ++u) {
59 for (int v = 0; v < bl_size; ++v) { dm[bl](u, v) = (u == v) ? std::exp(-beta * atom.get_eigenvalue(bl, u)) / z : 0; }
60 }
61 }
62 return dm;
63 }
64 template typename ATOM_DIAG_R::block_matrix_t atomic_density_matrix(ATOM_DIAG_R const &, double);
65 template typename ATOM_DIAG_C::block_matrix_t atomic_density_matrix(ATOM_DIAG_C const &, double);
66
67 // -----------------------------------------------------------------
68
69 template <bool Complex>
70 ATOM_DIAG_T::scalar_t trace_rho_op(ATOM_DIAG_T::block_matrix_t const &density_matrix, ATOM_DIAG_T::many_body_op_t const &op,
71 ATOM_DIAG const &atom) {
72 ATOM_DIAG_T::scalar_t result = 0;
73 if (atom.n_subspaces() != density_matrix.size()) TRIQS_RUNTIME_ERROR << "trace_rho_op : size mismatch : number of blocks differ";
74 for (int sp = 0; sp < atom.n_subspaces(); ++sp) {
75 if (atom.get_subspace_dim(sp) != first_dim(density_matrix[sp]))
76 TRIQS_RUNTIME_ERROR << "trace_rho_op : size mismatch : size of block " << sp << " differ";
77 for (auto const &x : op) {
78 auto b_m = atom.get_matrix_element_of_monomial(x.monomial, sp);
79 if (b_m.first == sp) result += x.coef * trace(b_m.second * density_matrix[sp]);
80 }
81 }
82 // little helper function to check block matrix hermiticity
83 auto is_block_matrix_hermitian = [](ATOM_DIAG_T::block_matrix_t const &bm) {
84 return std::all_of(bm.begin(), bm.end(), [](ATOM_DIAG_T::matrix_t const &mat) { return max_element(abs(mat - dagger(mat))) == 0.0; });
85 };
86 // check if op && density matrix are hermitian, if so return real(result)
87 // The product of two hermitian matrices is hermitian, hence the Tr(rho * Op)
88 // needs to be real in this case. Here, each monomial b_m is represented in the
89 // eigenbasis of atom diag. This unitary rotation should not change any of the
90 // above statements (Tr is invariant under trafo) However, the eigenbasis is obtained
91 // via LAPACK up to machine prec, introducing tiny imaginary elements. Here, we filter those.
92 // Note: Here it assumed that op & den mat are truly hermitian, 0 tolerance
93 if (is_op_hermitian(op) && is_block_matrix_hermitian(density_matrix)) {
94 return std::real(result);
95 } else
96 return result;
97 }
98 template ATOM_DIAG_R::scalar_t trace_rho_op(ATOM_DIAG_R::block_matrix_t const &, ATOM_DIAG_R::many_body_op_t const &, ATOM_DIAG_R const &);
99 template ATOM_DIAG_C::scalar_t trace_rho_op(ATOM_DIAG_C::block_matrix_t const &, ATOM_DIAG_C::many_body_op_t const &, ATOM_DIAG_C const &);
100
101 // -----------------------------------------------------------------
102 template <bool Complex>
103 auto act(ATOM_DIAG_T::many_body_op_t const &op, ATOM_DIAG_T::full_hilbert_space_state_t const &st, ATOM_DIAG const &atom)
104 -> ATOM_DIAG_T::full_hilbert_space_state_t {
105 ATOM_DIAG_T::full_hilbert_space_state_t result(st.size());
106 result() = 0;
107 for (auto const &x : op) {
108 for (int bl = 0; bl < atom.n_subspaces(); ++bl) {
109 auto b_m = atom.get_matrix_element_of_monomial(x.monomial, bl);
110 if (b_m.first == -1) continue;
111 result(atom.index_range_of_subspace(b_m.first)) += x.coef * b_m.second * st(atom.index_range_of_subspace(bl));
112 }
113 }
114 return result;
115 }
116 template ATOM_DIAG_R::full_hilbert_space_state_t act(ATOM_DIAG_R::many_body_op_t const &, ATOM_DIAG_R::full_hilbert_space_state_t const &,
117 ATOM_DIAG_R const &);
118 template ATOM_DIAG_C::full_hilbert_space_state_t act(ATOM_DIAG_C::many_body_op_t const &, ATOM_DIAG_C::full_hilbert_space_state_t const &,
119 ATOM_DIAG_C const &);
120
121 // -----------------------------------------------------------------
122 template <bool Complex>
123 auto quantum_number_eigenvalues(ATOM_DIAG_T::many_body_op_t const &op, ATOM_DIAG const &atom) -> std::vector<std::vector<quantum_number_t>> {
124
125 auto commutator = op * atom.get_h_atomic() - atom.get_h_atomic() * op;
126 if (!commutator.is_almost_zero()) TRIQS_RUNTIME_ERROR << "The operator is not a quantum number";
127
128 std::vector<std::vector<quantum_number_t>> result;
129
130 for (int sp = 0; sp < atom.n_subspaces(); ++sp) {
131 auto dim = atom.get_subspace_dim(sp);
132 result.push_back(std::vector<quantum_number_t>(dim, 0));
133 for (auto const &x : op) {
134 auto b_m = atom.get_matrix_element_of_monomial(x.monomial, sp);
135 if (b_m.first != sp) continue;
136 for (int i = 0; i < dim; ++i) result.back()[i] += std::real(x.coef * b_m.second(i, i));
137 }
138 }
139 return result;
140 }
141 template std::vector<std::vector<quantum_number_t>> quantum_number_eigenvalues(ATOM_DIAG_R::many_body_op_t const &, ATOM_DIAG_R const &);
142 template std::vector<std::vector<quantum_number_t>> quantum_number_eigenvalues(ATOM_DIAG_C::many_body_op_t const &, ATOM_DIAG_C const &);
143
144 // -----------------------------------------------------------------
145 template <typename M>
146 // require (ImmutableMatrix<M>)
147 inline bool is_diagonal(M const &m) {
148 double static const threshold = 1.e-11;
149 return triqs::utility::is_zero(sum(abs(m)) - trace(abs(m)), threshold);
150 }
151
152 template <bool Complex>
153 auto quantum_number_eigenvalues_checked(ATOM_DIAG_T::many_body_op_t const &op, ATOM_DIAG const &atom)
154 -> std::vector<std::vector<quantum_number_t>> {
155
156 auto commutator = op * atom.get_h_atomic() - atom.get_h_atomic() * op;
157 if (!commutator.is_almost_zero()) TRIQS_RUNTIME_ERROR << "The operator is not a quantum number";
158
159 auto d = atom.get_full_hilbert_space_dim();
160 matrix<quantum_number_t> M(d, d);
161 M() = 0;
162 std::vector<std::vector<quantum_number_t>> result;
163
164 for (int sp = 0; sp < atom.n_subspaces(); ++sp) {
165 for (auto const &x : op) {
166 auto b_m = atom.get_matrix_element_of_monomial(x.monomial, sp);
167 if (b_m.first == -1) continue;
168 M(atom.index_range_of_subspace(b_m.first), atom.index_range_of_subspace(sp)) += real(x.coef * b_m.second);
169 }
170 }
171 if (!is_diagonal(M)) TRIQS_RUNTIME_ERROR << "The matrix of the operator is not diagonal !!!";
172
173 for (int sp = 0; sp < atom.n_subspaces(); ++sp) {
174 auto dim = atom.get_subspace_dim(sp);
175 result.push_back(std::vector<quantum_number_t>(dim, 0));
176 for (int i = 0; i < dim; ++i) result.back()[i] = M(atom.flatten_subspace_index(sp, i), atom.flatten_subspace_index(sp, i));
177 }
178
179 return result;
180 }
181 template std::vector<std::vector<quantum_number_t>> quantum_number_eigenvalues_checked(ATOM_DIAG_R::many_body_op_t const &, ATOM_DIAG_R const &);
182 template std::vector<std::vector<quantum_number_t>> quantum_number_eigenvalues_checked(ATOM_DIAG_C::many_body_op_t const &, ATOM_DIAG_C const &);
183
184} // namespace triqs::atom_diag
Backward-compatibility umbrella header pulling in the nda array library.
Provides a lightweight exact diagonalization solver for fermionic Hamiltonians.
Free functions that compute thermodynamic averages and act with operators on a solved diagonalization...
TRIQS exception hierarchy and related macros.
double partition_function(atom_diag< Complex > const &atom, double beta)
Compute the atomic partition function at inverse temperature .
Definition functions.cpp:42
atom_diag< Complex >::block_matrix_t atomic_density_matrix(atom_diag< Complex > const &atom, double beta)
Compute the atomic density matrix at inverse temperature .
Definition functions.cpp:51
atom_diag< Complex >::scalar_t trace_rho_op(typename atom_diag< Complex >::block_matrix_t const &density_matrix, typename atom_diag< Complex >::many_body_op_t const &op, atom_diag< Complex > const &atom)
Compute the trace of a many-body operator weighted by a block-diagonal density matrix.
Definition functions.cpp:70
std::vector< std::vector< quantum_number_t > > quantum_number_eigenvalues_checked(typename atom_diag< Complex >::many_body_op_t const &op, atom_diag< Complex > const &atom)
Tabulate the eigenvalues of a quantum-number operator , also checking that the operator is diagonal ...
std::vector< std::vector< quantum_number_t > > quantum_number_eigenvalues(typename atom_diag< Complex >::many_body_op_t const &op, atom_diag< Complex > const &atom)
Tabulate the eigenvalues of a quantum-number operator over all eigenstates of the Hamiltonian.
atom_diag< Complex >::full_hilbert_space_state_t act(typename atom_diag< Complex >::many_body_op_t const &op, typename atom_diag< Complex >::full_hilbert_space_state_t const &st, atom_diag< Complex > const &atom)
Act with a many-body operator on a state vector, .
G::regular_type::real_t real(G const &g)
Take the real part of a Green's function (no check), returning a new Green's function with a real tar...
#define TRIQS_RUNTIME_ERROR
Throw a triqs::runtime_error with the current source location.
bool is_zero(I const &x)
Exact zero check for integral values.
Numeric helpers overloaded for various types.
Provides generic many-body operators.