62 histo(performance_analysis ? new histograms_t(h_diag_.n_subspaces(), *hist_map) : nullptr) {
65 for (
int bl = 0; bl < n_blocks; ++bl) density_matrix[bl] = bool_and_matrix{
false, matrix_t(get_block_dim(bl), get_block_dim(bl))};
69 auto rho = atomic_density_matrix(h_diag_, beta);
70 for (
int bl = 0; bl < n_blocks; ++bl) {
71 atomic_rho[bl] = bool_and_matrix{
true, rho[bl] * atomic_z};
72 for (
int u = 0; u < get_block_dim(bl); ++u) {
73 auto xx = std::abs(rho[bl](u, u));
74 atomic_norm += xx * xx;
77 atomic_norm = std::sqrt(atomic_norm);
101 int impurity_trace::compute_block_table(node n,
int b) {
103 if (b < 0) TRIQS_RUNTIME_ERROR <<
" b < 0";
104 if (!n->modified)
return n->cache.block_table[b];
106 int b1 = (n->right ? compute_block_table(n->right, b) : b);
107 if (b1 < 0)
return b1;
109 int b2 = (n->delete_flag ? b1 : get_op_block_map(n, b1));
110 if (b2 < 0)
return b2;
112 return (n->left ? compute_block_table(n->left, b2) : b2);
119 std::pair<int, double> impurity_trace::compute_block_table_and_bound(node n,
int b,
double lnorm_threshold,
bool use_threshold) {
121 if (b < 0) TRIQS_RUNTIME_ERROR <<
" b < 0";
122 if (!n->modified)
return {n->cache.block_table[b], n->cache.matrix_lnorms[b]};
128 std::tie(b1, lnorm) = compute_block_table_and_bound(n->right, b, lnorm_threshold, use_threshold);
129 if (b1 < 0)
return {b1, 0};
130 lnorm += n->cache.dtau_r * get_block_emin(b1);
132 if (use_threshold && (lnorm > lnorm_threshold))
return {-1, 0};
134 int b2 = (n->delete_flag ? b1 : get_op_block_map(n, b1));
135 if (b2 < 0)
return {b2, 0};
139 lnorm += n->cache.dtau_l * get_block_emin(b2);
140 if (use_threshold && (lnorm > lnorm_threshold))
return {-1, 0};
142 std::tie(b3, lnorm3) = compute_block_table_and_bound(n->left, b2, lnorm_threshold, use_threshold);
143 if (b3 < 0)
return {b3, 0};
147 if (use_threshold && (lnorm > lnorm_threshold))
return {-1, 0};
149 if (std::isinf(lnorm)) {
151 if (lnorm < 0) TRIQS_RUNTIME_ERROR <<
"Negative lnorm in compute_block_table_and_bound!";
160 std::pair<int, matrix_t> impurity_trace::compute_matrix(node n,
int b) {
162 if (b == -1)
return {-1, {}};
163 if (n ==
nullptr)
return {b, {}};
164 if (!n->modified && n->cache.matrix_norm_valid[b])
return {n->cache.block_table[b], n->cache.matrices[b]};
165 bool updating = (!n->modified && !n->cache.matrix_norm_valid[b]);
167 double dtau_l = 0, dtau_r = 0;
168 auto _ = arrays::range::all;
170 auto r = compute_matrix(n->right, b);
172 if (b1 == -1)
return {-1, {}};
174 int b2 = (n->delete_flag ? b1 : get_op_block_map(n, b1));
175 if (b2 == -1)
return {-1, {}};
177 matrix_t M = (!n->delete_flag ? get_op_block_matrix(n, b1) : nda::eye<h_scalar_t>(get_block_dim(b1)));
180 dtau_r = double(n->key - tree.min_key(n->right));
181 auto dim = M.shape()[1];
182 for (int i = 0; i < dim; ++i) M(_, i) *= std::exp(-dtau_r * get_block_eigenval(b1, i));
183 if ((r.second.shape()[0] == 1) && (r.second.shape()[1] == 1))
191 auto l = compute_matrix(n->left, b2);
193 if (b3 == -1) return {-1, {}};
194 dtau_l =
double(tree.max_key(n->left) - n->key);
195 auto dim = M.shape()[0];
196 for (
int i = 0; i < dim; ++i) M(i, _) *= std::exp(-dtau_l * get_block_eigenval(b2, i));
197 if ((l.second.shape()[0] == 1) && (l.second.shape()[1] == 1))
204 n->cache.matrices[b] = M;
205 n->cache.matrix_norm_valid[b] =
true;
208 if (use_norm_of_matrices_in_cache) {
209 auto norm = frobenius_norm(M);
210 if (std::abs(norm - frobenius_norm2(M)) > 1.e-12) TRIQS_RUNTIME_ERROR <<
" FROB PB" << M;
213 n->cache.matrix_lnorms[b] = -std::log(norm);
214 if (!isfinite(-std::log(norm))) { n->cache.matrix_lnorms[b] = double_max; }
218 return {b3, std::move(M)};
223 void impurity_trace::update_cache() { update_cache_impl(tree.get_root()); }
227 void impurity_trace::update_cache_impl(node n) {
229 if ((n ==
nullptr) || (!n->modified))
return;
230 if (n->delete_flag) TRIQS_RUNTIME_ERROR <<
" Internal Error: node flagged for deletion in cache update ";
231 update_cache_impl(n->left);
232 update_cache_impl(n->right);
233 n->cache.dtau_r = (n->right ? double(n->key - tree.min_key(n->right)) : 0);
234 n->cache.dtau_l = (n->left ? double(tree.max_key(n->left) - n->key) : 0);
235 for (
int b = 0; b < n_blocks; ++b) {
236 auto r = compute_block_table_and_bound(n, b, double_max,
false);
237 n->cache.block_table[b] = r.first;
238 n->cache.matrix_lnorms[b] = r.second;
239 n->cache.matrix_norm_valid[b] =
false;
247 void impurity_trace::update_dtau(node n) {
248 if ((n ==
nullptr) || (!n->modified))
return;
249 update_dtau(n->left);
250 update_dtau(n->right);
251 n->cache.dtau_r = (n->right ? double(n->key - tree.min_key(n->right)) : 0);
252 n->cache.dtau_l = (n->left ? double(tree.max_key(n->left) - n->key) : 0);
257 std::pair<h_scalar_t, h_scalar_t> impurity_trace::compute(
double p_yee,
double u_yee) {
259 double epsilon = 1.e-15;
260 auto log_epsilon0 = -std::log(1.e-15);
261 double lnorm_threshold = double_max - 100;
262 std::vector<std::pair<double, int>> init_to_sort_lnorm_b, to_sort_lnorm_b;
265 if (tree_size == 0) {
266 if (use_norm_as_weight) {
267 density_matrix = atomic_rho;
268 return {atomic_norm, atomic_z / atomic_norm};
270 return {atomic_z, 1};
273 auto root = tree.get_root();
275 double dtau_beta = beta - tree.min_key();
276 double dtau_0 = double(tree.max_key());
277 double dtau = dtau_beta + dtau_0;
290 for (
int b = 0; b < n_blocks; ++b) {
291 auto block_lnorm_pair = compute_block_table_and_bound(root, b, lnorm_threshold);
295 if (measure_density_matrix) {
296 static bool first_warning_issued =
false;
297 if ((not first_warning_issued) and (block_lnorm_pair.first != b) && (block_lnorm_pair.first != -1)) {
298 first_warning_issued =
true;
299 mpi::communicator world;
300 if (world.rank() == 0)
301 std::cerr <<
"\nWARNING: The product of atomic operators has a matrix element in the off-diagonal blocks!!! \n"
302 <<
"You will not be able to use the measured density matrix to calculate expectations values\n"
303 "of operators that do not commute with the local Hamiltonian!\n"
308 if (block_lnorm_pair.first == b) {
309 double lnorm = block_lnorm_pair.second + dtau * get_block_emin(b);
310 lnorm_threshold = std::min(lnorm_threshold, lnorm + log_epsilon0);
311 init_to_sort_lnorm_b.emplace_back(lnorm, b);
316 for (
auto const &b_b : init_to_sort_lnorm_b)
317 if (b_b.first <= lnorm_threshold) to_sort_lnorm_b.push_back(b_b);
319 if (histo) histo->n_block_at_root << to_sort_lnorm_b.size();
321 if (to_sort_lnorm_b.size() == 0)
return {0.0, 1};
324 std::sort(to_sort_lnorm_b.begin(), to_sort_lnorm_b.end());
328 h_scalar_t full_trace = 0, first_term = 0;
329 double norm_trace_sq = 0, trace_abs = 0;
332 for (
int bl = 0; bl < n_blocks; ++bl) density_matrix[bl].is_valid =
false;
334 auto trace_contrib_block = std::vector<std::pair<double, int>>{};
336 int n_bl = to_sort_lnorm_b.size();
337 auto bound_cumul = std::vector<double>(n_bl + 1);
342 bound_cumul[n_bl] = 0;
343 if (!use_norm_as_weight) {
344 for (
int bl = n_bl - 1; bl >= 0; --bl)
345 bound_cumul[bl] = bound_cumul[bl + 1] + std::exp(-to_sort_lnorm_b[bl].first) * get_block_dim(to_sort_lnorm_b[bl].second);
347 for (
int bl = n_bl - 1; bl >= 0; --bl) bound_cumul[bl] = bound_cumul[bl + 1] + std::exp(-to_sort_lnorm_b[bl].first) * std::sqrt(get_block_dim(to_sort_lnorm_b[bl].second));
351 for (bl = 0; bl < n_bl; ++bl) {
354 if ((bl > 0) && (bound_cumul[bl] <= std::abs(full_trace) * epsilon))
break;
356 int block_index = to_sort_lnorm_b[bl].second;
360 auto current_weight = (use_norm_as_weight ? std::sqrt(norm_trace_sq) : full_trace);
361 auto pmax = std::abs(p_yee) * (std::abs(current_weight) + bound_cumul[bl]);
362 if (pmax < u_yee)
return {0, 1};
366 auto b_mat = compute_matrix(root, block_index);
367 if (b_mat.first == -1) TRIQS_RUNTIME_ERROR <<
" Internal error : B = -1 after compute matrix : " << block_index;
369#ifdef TRACE_CHECK_AGAINST_LINEAR_COMPUTATION
370 auto b_mat2 = check_one_block_matrix_linear(root, block_index);
371 if (max_element(abs(b_mat.second - b_mat2)) > 1.e-10) TRIQS_RUNTIME_ERROR <<
" Matrix failed against linear computation";
375 h_scalar_t trace_partial = 0;
376 auto dim = get_block_dim(block_index);
377 for (
int u = 0; u < dim; ++u) {
378 auto x = b_mat.second(u, u) * std::exp(-dtau * get_block_eigenval(block_index, u));
380 trace_abs += std::abs(x);
383 if (use_norm_as_weight) {
385 density_matrix[block_index].is_valid =
true;
386 double norm_trace_sq_partial = 0;
387 auto &mat = density_matrix[block_index].mat;
388 for (
int u = 0; u < dim; ++u) {
389 for (
int v = 0; v < dim; ++v) {
390 mat(u, v) = b_mat.second(u, v) * std::exp(-dtau_beta * get_block_eigenval(block_index, u) - dtau_0 * get_block_eigenval(block_index, v));
391 double xx = std::abs(mat(u, v));
392 norm_trace_sq_partial += xx * xx;
395 norm_trace_sq += norm_trace_sq_partial;
397 if (std::abs(trace_partial) - 1.0000001 * std::sqrt(norm_trace_sq_partial) * get_block_dim(block_index) > 1.e-15)
398 TRIQS_RUNTIME_ERROR <<
"|trace| > dim * norm" << trace_partial <<
" " << std::sqrt(norm_trace_sq_partial) <<
" " << trace_abs;
399 auto dev = std::abs(trace_partial - trace(mat));
400 if (dev > 1.e-14) TRIQS_RUNTIME_ERROR <<
"Internal error : trace and density mismatch. Deviation: " << dev;
403#ifdef TRACE_CHECK_MATRIX_BOUNDED_BY_BOUND
404 if (std::abs(trace_partial) > 1.000001 * dim * std::exp(-to_sort_lnorm_b[bl].first))
405 TRIQS_RUNTIME_ERROR <<
"Matrix not bounded by the bound ! test is " << std::abs(trace_partial) <<
" < "
406 << dim * std::exp(-to_sort_lnorm_b[bl].first);
409 full_trace += trace_partial;
413 histo->trace_over_bound << std::abs(trace_partial) / std::exp(-to_sort_lnorm_b[bl].first);
414 trace_contrib_block.emplace_back(std::abs(trace_partial), block_index);
416 first_term = trace_partial;
417 histo->dominant_block_bound << block_index;
418 histo->dominant_block_energy_bound << get_block_emin(block_index);
419 }
else if (first_term != 0.0) {
420 histo->trace_first_over_sec_term << real(trace_partial / first_term);
425 double norm_trace = std::sqrt(norm_trace_sq);
426 if (!isfinite(full_trace)) TRIQS_RUNTIME_ERROR <<
" full_trace not finite" << full_trace;
429 if (histo && norm_trace != 0.0) {
430 histo->trace_over_norm << std::abs(full_trace) / norm_trace;
431 histo->trace_abs_over_norm << trace_abs / norm_trace;
432 histo->trace_over_trace_abs << real(full_trace / trace_abs);
433 std::sort(trace_contrib_block.begin(), trace_contrib_block.end(), std::greater<>());
434 histo->dominant_block_trace << begin(trace_contrib_block)->second;
435 histo->dominant_block_energy_trace << get_block_emin(begin(trace_contrib_block)->second);
436 histo->n_block_kept << bl;
437 histo->trace_first_term_trace << std::abs(first_term) / std::abs(full_trace);
441 if (!use_norm_as_weight)
return {full_trace, 1};
443 auto rw = full_trace / norm_trace;
444 if (!isfinite(rw)) rw = 1;
446 return {norm_trace, rw};
450 std::ostream &operator<<(std::ostream &out, impurity_trace
const &imp_trace) {
451 out <<
"Impurity trace: size = " << imp_trace.tree_size <<
"\n";
452 out <<
"Trial nodes: index = " << imp_trace.trial_nodes.index() <<
"\n";
453 imp_trace.tree.graphviz(out);
458#include "./impurity_trace.checks.cpp"