TRIQS/triqs_cthyb 4.0.0
A TRIQS application
Loading...
Searching...
No Matches
impurity_trace.hpp
1/*******************************************************************************
2 *
3 * TRIQS: a Toolbox for Research in Interacting Quantum Systems
4 *
5 * Copyright (C) 2014, P. Seth, I. Krivenko, M. Ferrero and O. Parcollet
6 *
7 * TRIQS is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later
10 * version.
11 *
12 * TRIQS is distributed in the hope that it will be useful, but WITHOUT ANY
13 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15 * details.
16 *
17 * You should have received a copy of the GNU General Public License along with
18 * TRIQS. If not, see <http://www.gnu.org/licenses/>.
19 *
20 ******************************************************************************/
21#pragma once
22#include "./configuration.hpp"
23#include "./parameters.hpp"
24#include "triqs/utility/rbt.hpp"
25#include <triqs/stat/histograms.hpp>
26#include <triqs/atom_diag/atom_diag.hpp>
27
28//#define PRINT_CONF_DEBUG
29
30using namespace triqs;
31using histo_map_t = std::map<std::string, triqs::stat::histogram>;
32using triqs::stat::histogram;
33using triqs::utility::rb_tree;
34using triqs::utility::rbt_insert_error;
35
36namespace triqs_cthyb {
37
38 /********************************************
39 Calculate the trace of the impurity problem.
40 ********************************************/
41 class impurity_trace {
42
43 double beta;
44 bool use_norm_as_weight;
45 bool measure_density_matrix;
46
47 public:
48 // construct from the config, the diagonalization of h_loc, and parameters
49 impurity_trace(double beta, atom_diag const &h_diag, histo_map_t *hist_map,
50 bool use_norm_as_weight=false, bool measure_density_matrix=false, bool performance_analysis=false);
51
52 ~impurity_trace() {
53 cancel_insert_impl(); // in case of an exception, we need to remove any trial nodes before cleaning the tree!
54 }
55
56 std::pair<h_scalar_t, h_scalar_t> compute(double p_yee = -1, double u_yee = 0);
57
58 // ------- Configuration and h_loc data ----------------
59
60 const configuration *config; // config object does exist longer (temporally) than this object.
61 const atom_diag *h_diag; // access to the diagonalization of h_loc
62 const int n_orbitals = h_diag->get_fops().size(); // total number of orbital flavours
63 const int n_blocks = h_diag->n_subspaces(); //
64 const int n_eigstates = h_diag->get_full_hilbert_space_dim(); // size of the hilbert space
65
66 // ------- Trace data ----------------
67
68 private:
69 struct bool_and_matrix {
70 bool is_valid;
71 matrix<h_scalar_t> mat;
72 };
73 std::vector<bool_and_matrix> density_matrix; // density_matrix, by block, with a bool to say if it has been recomputed
74 std::vector<bool_and_matrix> atomic_rho; // atomic density matrix (non-normalized)
75 double atomic_z; // atomic partition function
76 double atomic_norm; // Frobenius norm of atomic_rho
77
78 public:
79 std::vector<bool_and_matrix> const &get_density_matrix() const { return density_matrix; }
80
81 // ------------------ Cache data ----------------
82
83 private:
84 // The data stored for each node in tree
85 struct cache_t {
86 double dtau_l = 0, dtau_r = 0; // difference in tau of this node and left and right sub-trees
87 std::vector<int> block_table; // number of blocks limited to 2^15
88 std::vector<arrays::matrix<h_scalar_t>> matrices; // partial product of operator/time evolution matrices
89 std::vector<double> matrix_lnorms; // -ln(norm(matrix))
90 std::vector<bool> matrix_norm_valid; // is the norm of the matrix still valid?
91 cache_t(int n_blocks) : block_table(n_blocks), matrices(n_blocks), matrix_lnorms(n_blocks), matrix_norm_valid(n_blocks) {}
92 };
93
94 struct node_data_t {
95 op_desc op;
96 cache_t cache;
97 node_data_t(op_desc op, int n_blocks) : op(op), cache(n_blocks) {}
98 void reset(op_desc op_new) { op = op_new; }
99 };
100
101 using rb_tree_t = rb_tree<time_pt, node_data_t, std::greater<time_pt>>;
102 using node = rb_tree_t::node;
103
104#ifdef EXT_DEBUG
105 public:
106#endif
107 rb_tree_t tree; // the red black tree and its nodes
108
109 std::vector<atom_diag::op_block_mat_t> aux_operators;
110
111 // ---------------- Cache machinery ----------------
112 void update_cache();
113
114 private:
115 // The dimension of block b
116 int get_block_dim(int b) const { return h_diag->get_subspace_dim(b); }
117
118 // the i-th eigenvalue of the block b
119 double get_block_eigenval(int b, int i) const { return h_diag->get_eigenvalue(b, i); }
120
121 // the minimal eigenvalue of the block b
122 double get_block_emin(int b) const { return get_block_eigenval(b, 0); }
123
124 // node, block -> image of the block by n->op (the operator)
125 int get_op_block_map(node n, int b) const {
126 if( n->op.linear_index >= 0 )
127 return (n->op.dagger ? h_diag->cdag_connection(n->op.linear_index, b) : h_diag->c_connection(n->op.linear_index, b));
128 else {
129 int aux_idx = -n->op.linear_index - 1;
130 return aux_operators[aux_idx].connection(b);
131 }
132 }
133
134 // the matrix of n->op, from block b to its image
135 matrix<h_scalar_t> const &get_op_block_matrix(node n, int b) const {
136 if( n->op.linear_index >= 0 )
137 return (n->op.dagger ? h_diag->cdag_matrix(n->op.linear_index, b) : h_diag->c_matrix(n->op.linear_index, b));
138 else {
139 int aux_idx = -n->op.linear_index - 1;
140 return aux_operators[aux_idx].block_mat[b];
141 }
142 }
143
144 // recursive function for tree traversal
145 int compute_block_table(node n, int b);
146 std::pair<int, double> compute_block_table_and_bound(node n, int b, double bound_threshold, bool use_threshold = true);
147 std::pair<int, matrix_t> compute_matrix(node n, int b);
148
149 void update_cache_impl(node n);
150 void update_dtau(node n);
151
152 bool use_norm_of_matrices_in_cache = true; // When a matrix is computed in cache, its spectral radius replaces the norm estimate
153
154 // integrity check
155 void check_cache_integrity(bool print = false);
156 void check_cache_integrity_one_node(node n, bool print);
157 int check_one_block_table_linear(node n, int b, bool print); // compare block table to that of a linear method (ie. no tree)
158 matrix_t check_one_block_matrix_linear(node n, int b); // compare matrix to that of a linear method (ie. no tree)
159
160 // Pool of detached nodes
161 class nodes_storage {
162
163 const int n_blocks;
164 std::vector<node> nodes;
165 int i;
166
167 // make a new detached black node
168 node make_new_node() { return new rb_tree_t::node_t(time_pt{}, node_data_t{{}, n_blocks}, false, 1); }
169
170 public:
171 inline nodes_storage(int n_blocks, int size = 0) : n_blocks(n_blocks), i(-1) {
172 for (int j = 0; j < size; ++j) nodes.push_back(make_new_node());
173 }
174 inline ~nodes_storage() {
175 for (auto &n : nodes) delete n;
176 }
177
178 // Change the number of stored nodes
179 inline void reserve(int size) {
180 if (size > nodes.size())
181 for (int j = nodes.size(); j < size; ++j) nodes.push_back(make_new_node());
182 }
183 inline int index() const { return i; }
184 inline bool is_index_reset() const { return i == -1; }
185 inline int reset_index() {
186 int i_ = i;
187 i = -1;
188 return i_;
189 }
190 inline node swap_next(node n) {
191 std::swap(n, nodes[++i]);
192 return n;
193 }
194 inline node swap_prev(node n) {
195 std::swap(n, nodes[i--]);
196 return n;
197 }
198 inline node take_next() { return nodes[++i]; }
199 inline node take_prev() { return nodes[i--]; }
200 };
201
202 public:
203
204 // attach auxiliary operators
205 op_desc attach_aux_operator(many_body_op_t const &op) {
206 aux_operators.push_back(h_diag->get_op_mat(op));
207 op_desc operator_desc{0, 0, true, -static_cast<int>(aux_operators.size())};
208 return operator_desc;
209 }
210
211 /*************************************************************************
212 * Ordinary binary search tree (BST) insertion of the trial nodes
213 *************************************************************************/
214 // We have a set of trial nodes, which we can glue, un-glue in the tree at will.
215 // This avoids allocations.
216
217 int tree_size = 0; // size of the tree +/- the added/deleted node
218
219 // a pool of trial nodes, ready to be glued in the tree. Max 4 to allow for double insertions
220 nodes_storage trial_nodes = {n_blocks, 4};
221
222 // for each inserted node, need to know {parent_of_node,child_is_left}
223 std::vector<std::pair<node, bool>> inserted_nodes = {{nullptr, false}, {nullptr, false}, {nullptr, false}, {nullptr, false}};
224
225 node try_insert_impl(node h, node n) { // implementation
226 if (h == nullptr) return n;
227 if (h->key == n->key) throw rbt_insert_error{};
228 auto smaller = tree.get_comparator()(n->key, h->key);
229 if (smaller)
230 h->left = try_insert_impl(h->left, n);
231 else
232 h->right = try_insert_impl(h->right, n);
233 if (inserted_nodes[trial_nodes.index()].first == nullptr) inserted_nodes[trial_nodes.index()] = {h, smaller};
234 h->modified = true;
235 return h;
236 }
237
238 // unlink all glued trial nodes
239 void cancel_insert_impl() {
240 for (int i = 0; i <= trial_nodes.index(); ++i) {
241 auto &r = inserted_nodes[i];
242 if (r.first != nullptr) (r.second ? r.first->left : r.first->right)= nullptr;
243 }
244 if (tree_size <= trial_nodes.index() + 1) tree.get_root() = nullptr;
245 }
246
247 /*************************************************************************
248 * Node Insertion
249 *************************************************************************/
250
251 public:
252 // Put a trial node at tau for operator op using an ordinary BST insertion (ie. not red black)
253 void try_insert(time_pt const &tau, op_desc const &op) {
254 if (trial_nodes.index() > 3) TRIQS_RUNTIME_ERROR << "Error : more than 4 insertions ";
255 auto &root = tree.get_root();
256 node n = trial_nodes.take_next(); // get the next available node
257 inserted_nodes[trial_nodes.index()] = {nullptr, false};
258 n->reset(tau, op); // change the time and op of the node
259 root = try_insert_impl(root, n); // insert it using a regular BST, no red black
260 tree_size++;
261 }
262
263 // Remove all trial nodes from the tree
264 void cancel_insert() {
265 cancel_insert_impl();
266 trial_nodes.reset_index();
267 tree_size = tree.size();
268 tree.clear_modified();
269 check_cache_integrity();
270 }
271
272 // confirm the insertion of the nodes, with red black balance
273 void confirm_insert() {
274 cancel_insert_impl(); // remove BST inserted nodes
275 // then reinsert the nodes in in balanced RBT
276 int imax = trial_nodes.reset_index();
277 for (int i = 0; i <= imax; ++i) {
278 node n = trial_nodes.take_next();
279 tree.insert(n->key, {n->op, n_blocks});
280 }
281 trial_nodes.reset_index();
282 update_cache();
283 tree_size = tree.size();
284 tree.clear_modified();
285 check_cache_integrity();
286 }
287
288 /*************************************************************************
289 * Node Removal
290 *************************************************************************/
291 private:
292 std::vector<node> removed_nodes;
293 std::vector<time_pt> removed_keys;
294
295 public:
296 // Find and mark as deleted the nth operator with fixed dagger and block_index
297 // n=0 : first operator, n=1, second, etc...
298 time_pt try_delete(int n, int block_index, bool dagger) noexcept {
299 // traverse the tree, looking for the nth operator of the correct dagger, block_index
300 int i = 0;
301 node x = find_if(tree, [&](node no) {
302 if (no->op.dagger == dagger && no->op.block_index == block_index) ++i;
303 return i == n + 1;
304 });
305 removed_nodes.push_back(x); // store the node
306 removed_keys.push_back(x->key); // store the key
307 tree.set_modified_from_root_to(x->key); // mark all nodes on path from node to root as modified
308 x->delete_flag = true; // mark the node for deletion
309 tree_size--;
310 return x->key;
311 }
312
313 // Clean all the delete flags
314 void cancel_delete() {
315 for (auto &n : removed_nodes) n->delete_flag = false;
316 removed_nodes.clear();
317 removed_keys.clear();
318 tree_size = tree.size();
319 tree.clear_modified();
320 check_cache_integrity();
321 }
322
323 // Confirm deletion: the nodes flagged for deletion are truly deleted
324 void confirm_delete() {
325 for (auto &k : removed_keys) tree.delete_node(k); // CANNOT use the node here
326 removed_nodes.clear();
327 removed_keys.clear();
328 update_cache();
329 tree_size = tree.size();
330 tree.clear_modified();
331 check_cache_integrity();
332 }
333
334 /*************************************************************************
335 * Node shift (=insertion+deletion)
336 *************************************************************************/
337
338 // No try_shift implemented. Use combination of try_insert and try_delete instead.
339
340 // Cancel the shift
341 void cancel_shift() {
342
343 // Inserted nodes
344 cancel_insert_impl();
345 trial_nodes.reset_index();
346
347 // Deleted nodes
348 for (auto &n : removed_nodes) n->delete_flag = false;
349 removed_nodes.clear();
350 removed_keys.clear();
351
352 tree_size = tree.size();
353 tree.clear_modified();
354 check_cache_integrity();
355 }
356
357 // Confirm the shift of the node, with red black balance
358 void confirm_shift() {
359
360 // Inserted nodes
361 cancel_insert_impl(); // first remove BST inserted nodes
362
363 // then reinsert the nodes used for real in rb tree
364 int imax = trial_nodes.reset_index();
365 for (int i = 0; i <= imax; ++i) {
366 node n = trial_nodes.take_next();
367 tree.insert(n->key, {n->op, n_blocks});
368 }
369 trial_nodes.reset_index();
370
371 // Deleted nodes
372 for (auto &k : removed_keys) tree.delete_node(k); // CANNOT use the node here
373 removed_nodes.clear();
374 removed_keys.clear();
375
376 // update cache only at the end
377 update_cache();
378 tree_size = tree.size();
379 tree.clear_modified();
380 check_cache_integrity();
381 }
382
383 /*************************************************************************
384 * Node replacement (replace op_desc according to a substitution table)
385 *************************************************************************/
386 private:
387 // Store copies of the nodes to be replaced
388 nodes_storage backup_nodes = {n_blocks};
389
390 node try_replace_impl(node n, configuration::oplist_t const &updated_ops) noexcept {
391
392 node new_left = nullptr, new_right = nullptr;
393 if (n->left) new_left = try_replace_impl(n->left, updated_ops);
394 if (n->right) new_right = try_replace_impl(n->right, updated_ops);
395
396 auto const &op = n->op;
397 auto it = updated_ops.find(n->key);
398 bool op_changed = it != updated_ops.end();
399 auto const &new_op = op_changed ? it->second : op;
400 node new_node = n;
401 if (op_changed || new_left != n->left || new_right != n->right) {
402 auto key = n->key;
403 auto color = n->color;
404 auto N = n->N;
405
406 new_node = backup_nodes.swap_next(n);
407 if (op_changed)
408 new_node->reset(key, new_op);
409 else
410 new_node->reset(key, op);
411 new_node->left = new_left;
412 new_node->right = new_right;
413 new_node->color = color;
414 new_node->N = N;
415 new_node->modified = true;
416 }
417 return new_node;
418 }
419
420 node cancel_replace_impl(node n) {
421 node n_in_tree = n;
422 if (n_in_tree && n_in_tree->modified) n= backup_nodes.swap_prev(n);
423 if (n_in_tree->right) cancel_replace_impl(n_in_tree->right);
424 if (n_in_tree->left) cancel_replace_impl(n_in_tree->left);
425 return n;
426 }
427
428 public:
429 void try_replace(configuration::oplist_t const &updated_ops) {
430 if (tree_size == 0) return;
431
432 if (!backup_nodes.is_index_reset()) TRIQS_RUNTIME_ERROR << "impurity_trace: improper use of try_replace()";
433 backup_nodes.reserve(tree.size());
434 auto &root = tree.get_root();
435 root = try_replace_impl(root, updated_ops);
436 }
437
438 void confirm_replace() {
439 backup_nodes.reset_index();
440 update_cache();
441 tree.clear_modified();
442 check_cache_integrity();
443 }
444
445 void cancel_replace() {
446 if (tree_size == 0 || backup_nodes.is_index_reset()) return;
447 auto &root = tree.get_root();
448 root = cancel_replace_impl(root);
449 check_cache_integrity();
450 }
451
452 private:
453 // ---------------- Histograms ----------------
454 struct histograms_t {
455
456 // How many block non zero at root of the tree
457 histogram &n_block_at_root;
458 // how many block kept after the truncation with the bound
459 histogram &n_block_kept;
460
461 // What is the dominant block in the trace computation ? Sorted by number or energy
462 histogram &dominant_block_bound;
463 histogram &dominant_block_trace;
464 histogram &dominant_block_energy_bound;
465 histogram &dominant_block_energy_trace;
466
467 // Various ratios : trace/bound, trace/first term of the trace, etc..
468 histogram &trace_over_norm;
469 histogram &trace_abs_over_norm;
470 histogram &trace_over_trace_abs;
471 histogram &trace_over_bound;
472 histogram &trace_first_over_sec_term;
473 histogram &trace_first_term_trace;
474
475#define ADD_HISTO(NAME, HISTO) NAME(histos.emplace((#NAME), (HISTO)).first->second)
476 histograms_t(int n_subspaces, histo_map_t &histos)
477 : ADD_HISTO(n_block_at_root, histogram(0, n_subspaces)),
478 ADD_HISTO(n_block_kept, histogram(0, n_subspaces)),
479 ADD_HISTO(dominant_block_bound, histogram(0, n_subspaces)),
480 ADD_HISTO(dominant_block_trace, histogram(0, n_subspaces)),
481 ADD_HISTO(dominant_block_energy_bound, histogram(0, 100, 100)),
482 ADD_HISTO(dominant_block_energy_trace, histogram(0, 100, 100)),
483 ADD_HISTO(trace_over_norm, histogram(0, 1.5, 100)),
484 ADD_HISTO(trace_abs_over_norm, histogram(0, 1.5, 100)),
485 ADD_HISTO(trace_over_trace_abs, histogram(0, 1.5, 100)),
486 ADD_HISTO(trace_over_bound, histogram(0, 1.5, 100)),
487 ADD_HISTO(trace_first_over_sec_term, histogram(0, 1.0, 100)),
488 ADD_HISTO(trace_first_term_trace, histogram(0, 1.0, 100)) {}
489#undef ADD_HISTO
490 };
491 std::unique_ptr<histograms_t> histo;
492
494 friend std::ostream &operator<<(std::ostream &out, impurity_trace const & imp_trace);
495 };
496} // namespace triqs_cthyb