Skip to content

Commit 2e43d9e

Browse files
committed
Optimize diagonal FDM density rotations
1 parent 6de7501 commit 2e43d9e

4 files changed

Lines changed: 183 additions & 21 deletions

File tree

c++/dmnrg.hpp

Lines changed: 159 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
#include <stdexcept>
1111
#include <string>
1212
#include <cmath>
13+
#include <chrono>
14+
#include <limits>
15+
#include <utility>
1316

1417
#include "operators.hpp"
1518
#include "symmetry.hpp"
@@ -35,6 +38,98 @@ void check_trace_rho(const DensMatElements<S> &m, MF mult, const double ref_valu
3538
throw std::runtime_error(fmt::format("check_trace_rho() failed, tr={}, ref_value={}", tr, ref_value));
3639
}
3740

41+
class DmnrgProfile {
42+
public:
43+
using clock = std::chrono::steady_clock;
44+
using time_point = clock::time_point;
45+
enum class Contribution { generic, kk, dd };
46+
47+
DmnrgProfile(std::string label, const size_t N, const bool enabled) : label_(std::move(label)), N_(N), enabled_(enabled) {}
48+
49+
[[nodiscard]] bool enabled() const noexcept { return enabled_; }
50+
[[nodiscard]] static time_point now() { return clock::now(); }
51+
[[nodiscard]] static double elapsed(const time_point start) { return std::chrono::duration<double>(clock::now() - start).count(); }
52+
53+
void add_diag_load(const double seconds) { diag_load_seconds_ += seconds; }
54+
void add_init(const double seconds) { init_seconds_ += seconds; }
55+
void add_trace(const double seconds) { trace_seconds_ += seconds; }
56+
void add_save(const double seconds) { save_seconds_ += seconds; }
57+
void add_missing_rho() { ++missing_rho_; }
58+
void add_missing_diag() { ++missing_diag_; }
59+
void add_zero_dim() { ++zero_dim_; }
60+
void add_zero_omega() { ++zero_omega_; }
61+
void add_zero_rmax() { ++zero_rmax_; }
62+
63+
void add_rotate(const size_t q, const size_t r, const size_t element_size, const double seconds,
64+
const Contribution contribution = Contribution::generic, const bool diagonal = false) {
65+
++contributions_;
66+
rotate_seconds_ += seconds;
67+
if (contribution == Contribution::kk) {
68+
++kk_contributions_;
69+
kk_rotate_seconds_ += seconds;
70+
} else if (contribution == Contribution::dd) {
71+
++dd_contributions_;
72+
dd_rotate_seconds_ += seconds;
73+
}
74+
q_min_ = std::min(q_min_, q);
75+
q_max_ = std::max(q_max_, q);
76+
r_min_ = std::min(r_min_, r);
77+
r_max_ = std::max(r_max_, r);
78+
if (diagonal)
79+
multiply_adds_ += static_cast<long double>(q) * static_cast<long double>(r)
80+
+ static_cast<long double>(q) * static_cast<long double>(r) * static_cast<long double>(r);
81+
else
82+
multiply_adds_ += static_cast<long double>(q) * static_cast<long double>(q) * static_cast<long double>(r)
83+
+ static_cast<long double>(q) * static_cast<long double>(r) * static_cast<long double>(r);
84+
u_bytes_ += static_cast<long double>(q) * static_cast<long double>(r) * static_cast<long double>(element_size);
85+
rho_bytes_ += static_cast<long double>(diagonal ? q : q * q) * static_cast<long double>(element_size);
86+
out_bytes_ += static_cast<long double>(r) * static_cast<long double>(r) * static_cast<long double>(element_size);
87+
}
88+
89+
void report() const {
90+
if (!enabled_) return;
91+
const auto q_min = contributions_ ? q_min_ : 0;
92+
const auto r_min = contributions_ ? r_min_ : 0;
93+
std::cout << fmt::format(
94+
"[{} profile] N={} contrib={} rotate={:.6g}s kk={} kk_rotate={:.6g}s dd={} dd_rotate={:.6g}s diag_load={:.6g}s init={:.6g}s trace={:.6g}s save={:.6g}s "
95+
"q={}..{} r={}..{} madd={:.6g} U={:.6g}MB rho={:.6g}MB out={:.6g}MB missing_rho={} missing_diag={} zero_dim={} zero_omega={} zero_rmax={}",
96+
label_, N_, contributions_, rotate_seconds_, kk_contributions_, kk_rotate_seconds_, dd_contributions_, dd_rotate_seconds_,
97+
diag_load_seconds_, init_seconds_, trace_seconds_, save_seconds_,
98+
q_min, q_max_, r_min, r_max_, static_cast<double>(multiply_adds_), megabytes(u_bytes_), megabytes(rho_bytes_), megabytes(out_bytes_),
99+
missing_rho_, missing_diag_, zero_dim_, zero_omega_, zero_rmax_) << std::endl;
100+
}
101+
102+
private:
103+
[[nodiscard]] static double megabytes(const long double bytes) { return static_cast<double>(bytes / (1024.0L * 1024.0L)); }
104+
105+
std::string label_;
106+
size_t N_{};
107+
bool enabled_{};
108+
size_t contributions_{};
109+
size_t kk_contributions_{};
110+
size_t dd_contributions_{};
111+
size_t missing_rho_{};
112+
size_t missing_diag_{};
113+
size_t zero_dim_{};
114+
size_t zero_omega_{};
115+
size_t zero_rmax_{};
116+
size_t q_min_ = std::numeric_limits<size_t>::max();
117+
size_t q_max_{};
118+
size_t r_min_ = std::numeric_limits<size_t>::max();
119+
size_t r_max_{};
120+
long double multiply_adds_{};
121+
long double u_bytes_{};
122+
long double rho_bytes_{};
123+
long double out_bytes_{};
124+
double rotate_seconds_{};
125+
double kk_rotate_seconds_{};
126+
double dd_rotate_seconds_{};
127+
double diag_load_seconds_{};
128+
double init_seconds_{};
129+
double trace_seconds_{};
130+
double save_seconds_{};
131+
};
132+
38133
// Calculate rho_N, the density matrix at the last NRG iteration. It is
39134
// normalized to 1. Note: in CFS approach, we consider all states in the
40135
// last iteration to be "discarded".
@@ -73,43 +168,55 @@ void cdmI(const size_t i, // Subspace index
73168
const size_t N,
74169
const t_coef factor, // multiplicative factor that accounts for multiplicity
75170
const BackiterStore &store_all,
76-
const Params &P)
171+
const Params &P,
172+
DmnrgProfile *profile = nullptr,
173+
const DmnrgProfile::Contribution contribution = DmnrgProfile::Contribution::generic,
174+
const bool diagonal_rho = false)
77175
{
78176
my_assert(i < P.combs);
79177
nrglog('D', "cdmI i=" << i << " I1=" << I1 << " factor=" << factor);
80178
// Range of indexes r and r' in matrix C^{QS,N}_{r,r'}, cf. Eq. (3.55) in my dissertation.
81179
const auto dim = size2(rhoNEW);
82180
// number of states taken into account in the density-matrix at *current* (Nth) stage (in subspace I1)
83181
const auto nromega = size2(rhoN);
84-
if (nromega == 0 || dim == 0) return; // continue only if connection exists
182+
if (nromega == 0) { if (profile != nullptr) profile->add_zero_omega(); return; }
183+
if (dim == 0) { if (profile != nullptr) profile->add_zero_dim(); return; } // continue only if connection exists
85184
// rmax (info[I1].rmax[i]) is the range of r in U^N_I1(omega|ri), only those states that we actually kept..
86185
const auto rmax = store_all[N].at(I1).rmax.rmax(i);
87-
if (rmax == 0) return; // rmax can be zero in the case a subspace has been completely truncated
186+
if (rmax == 0) { if (profile != nullptr) profile->add_zero_rmax(); return; } // rmax can be zero in the case a subspace has been completely truncated
88187
my_assert(rmax == dim); // Otherwise, rmax must equal dim
89188
// Check range of omega: do the dimensions of C^N_I1(omega omega') and U^N_I1(omega|r1) match?
90189
my_assert(nromega <= diagI1.getnrstored());
91190
const auto &U0 = diagI1.U.get(i);
92191
const auto U = NRG::submatrix_const(U0, {0, nromega}, {0, size2(U0)});
93-
rotate<S>(rhoNEW, factor, U, rhoN);
192+
const auto rotate_start = profile != nullptr && profile->enabled() ? DmnrgProfile::now() : DmnrgProfile::time_point{};
193+
if (diagonal_rho)
194+
rotate_diagonal<S>(rhoNEW, factor, U, rhoN);
195+
else
196+
rotate<S>(rhoNEW, factor, U, rhoN);
197+
if (profile != nullptr && profile->enabled())
198+
profile->add_rotate(nromega, dim, sizeof(S), DmnrgProfile::elapsed(rotate_start), contribution, diagonal_rho);
94199
}
95200

96201
// Calculation of the shell-N REDUCED DENSITY MATRICES: Calculate rho at previous iteration (N-1) from rho
97202
// at the current iteration (N, rho)
98203
template<scalar S>
99204
auto calc_densitymatrix_iterN(const DiagInfo<S> &diag, const DensMatElements<S> &rho,
100-
const size_t N, const BackiterStore &store_all, const Symmetry<S> *Sym, const Params &P) {
205+
const size_t N, const BackiterStore &store_all, const Symmetry<S> *Sym, const Params &P,
206+
DmnrgProfile *profile = nullptr) {
101207
nrglog('D', "calc_densitymatrix_iterN N=" << N);
102208
DensMatElements<S> rhoPrev;
103209
for (const auto &[I, dimsub] : store_all[N - 1]) { // loop over all subspaces at *previous* iteration
104210
const auto dim = dimsub.kept();
105211
rhoPrev[I] = zero_matrix<S>(dim);
106-
if (dim == 0) continue;
212+
if (dim == 0) { if (profile != nullptr) profile->add_zero_dim(); continue; }
107213
const auto ns = Sym->new_subspaces(I);
108214
for (const auto &[i, sub] : ns | ranges::views::enumerate) {
109215
const auto x = rho.find(sub);
110216
const auto y = diag.find(sub);
111-
if (x != rho.end() && y != diag.end())
112-
cdmI(i, sub, x->second, y->second, rhoPrev[I], N, double(Sym->mult(sub)) / double(Sym->mult(I)), store_all, P);
217+
if (x == rho.end()) { if (profile != nullptr) profile->add_missing_rho(); continue; }
218+
if (y == diag.end()) { if (profile != nullptr) profile->add_missing_diag(); continue; }
219+
cdmI(i, sub, x->second, y->second, rhoPrev[I], N, double(Sym->mult(sub)) / double(Sym->mult(I)), store_all, P, profile);
113220
}
114221
}
115222
return rhoPrev;
@@ -137,10 +244,20 @@ void calc_densitymatrix(DensMatElements<S> &rho, const BackiterStore &store_all,
137244
const auto section_timing = mt.time_it("DM");
138245
for (size_t N = P.Nmax - 1; N > P.Ninit; N--) {
139246
std::cout << "[DM] " << N << std::endl;
247+
DmnrgProfile profile("DM", N, P.logletter('Y'));
248+
auto timer = DmnrgProfile::now();
140249
const DiagInfo<S> diag_loaded(N, P);
141-
auto rhoPrev = calc_densitymatrix_iterN(diag_loaded, rho, N, store_all, Sym, P); // need store_all for backiteration!
142-
if (P.checkrho) check_trace_rho(rhoPrev, Sym->multfnc()); // Make sure rho is normalized to 1.
250+
profile.add_diag_load(DmnrgProfile::elapsed(timer));
251+
auto rhoPrev = calc_densitymatrix_iterN(diag_loaded, rho, N, store_all, Sym, P, &profile); // need store_all for backiteration!
252+
if (P.checkrho) {
253+
timer = DmnrgProfile::now();
254+
check_trace_rho(rhoPrev, Sym->multfnc()); // Make sure rho is normalized to 1.
255+
profile.add_trace(DmnrgProfile::elapsed(timer));
256+
}
257+
timer = DmnrgProfile::now();
143258
rhoPrev.save(N-1, P, filename);
259+
profile.add_save(DmnrgProfile::elapsed(timer));
260+
profile.report();
144261
rho.swap(rhoPrev);
145262
}
146263
}
@@ -179,30 +296,45 @@ auto calc_fulldensitymatrix_iterN(const Step &step, // only required for step::l
179296
const DiagInfo<S> &diag,
180297
const DensMatElements<S> &rhoFDM, // input
181298
const size_t N, const ThermoStore<S> &store, const BackiterStore &store_all, const Stats<S> &stats,
182-
const Symmetry<S> *Sym, const Params &P) {
299+
const Symmetry<S> *Sym, const Params &P,
300+
DmnrgProfile *profile = nullptr) {
183301
nrglog('D', "calc_fulldensitymatrix_iterN N=" << N);
184302
DensMatElements<S> rhoDD;
185303
DensMatElements<S> rhoFDMPrev;
186-
if (!step.last(N))
304+
if (!step.last(N)) {
305+
const auto timer = profile != nullptr && profile->enabled() ? DmnrgProfile::now() : DmnrgProfile::time_point{};
187306
rhoDD = init_rho_FDM(N, store, stats, Sym->multfnc(), P.T, P.checkrho); // store here!
307+
if (profile != nullptr && profile->enabled()) profile->add_init(DmnrgProfile::elapsed(timer));
308+
}
188309
for (const auto &[I, ds] : store_all[N - 1]) { // loop over all subspaces at *previous* iteration, hence store_all here
189310
const auto subs = Sym->new_subspaces(I);
190311
const auto dim = ds.kept();
191312
rhoFDMPrev[I] = zero_matrix<S>(dim);
192-
if (!dim) continue;
313+
if (!dim) { if (profile != nullptr) profile->add_zero_dim(); continue; }
193314
for (const auto i : Sym->combs()) {
194315
const auto sub = subs[i];
195316
// DM construction for non-Abelian symmetries: must include the ratio of multiplicities as a coefficient.
196317
const auto coef = double(Sym->mult(sub)) / double(Sym->mult(I));
197318
// Contribution from the KK sector.
198319
const auto x1 = rhoFDM.find(sub);
199320
const auto y = diag.find(sub);
200-
if (x1 != rhoFDM.end() && y != diag.end())
201-
cdmI(i, sub, x1->second, y->second, rhoFDMPrev[I], N, coef, store_all, P);
321+
if (x1 == rhoFDM.end()) {
322+
if (profile != nullptr) profile->add_missing_rho();
323+
} else if (y == diag.end()) {
324+
if (profile != nullptr) profile->add_missing_diag();
325+
} else {
326+
cdmI(i, sub, x1->second, y->second, rhoFDMPrev[I], N, coef, store_all, P, profile, DmnrgProfile::Contribution::kk);
327+
}
202328
// Contribution from the DD sector. rhoDD -> rhoFDMPrev
203-
if (!step.last(N))
204-
if (const auto x2 = rhoDD.find(sub); x2 !=rhoDD.end() && y != diag.end())
205-
cdmI(i, sub, x2->second, y->second, rhoFDMPrev[I], N, coef, store_all, P);
329+
if (!step.last(N)) {
330+
if (const auto x2 = rhoDD.find(sub); x2 == rhoDD.end()) {
331+
if (profile != nullptr) profile->add_missing_rho();
332+
} else if (y == diag.end()) {
333+
if (profile != nullptr) profile->add_missing_diag();
334+
} else {
335+
cdmI(i, sub, x2->second, y->second, rhoFDMPrev[I], N, coef, store_all, P, profile, DmnrgProfile::Contribution::dd, true);
336+
}
337+
}
206338
// (Exception: for the N-1 iteration, the rhoPrev is already initialized with the DD sector of the last iteration.) }
207339
} // over combinations
208340
} // over subspaces
@@ -216,16 +348,24 @@ void calc_fulldensitymatrix(const Step &step, DensMatElements<S> &rhoFDM, const
216348
const auto section_timing = mt.time_it("FDM");
217349
for (size_t N = P.Nmax - 1; N > P.Ninit; N--) {
218350
std::cout << "[FDM] " << N << std::endl;
351+
DmnrgProfile profile("FDM", N, P.logletter('Y'));
352+
auto timer = DmnrgProfile::now();
219353
const DiagInfo<S> diag_loaded(N, P); // = load_and_project(N, Sym, P);
220-
auto rhoFDMPrev = calc_fulldensitymatrix_iterN(step, diag_loaded, rhoFDM, N, store, store_all, stats, Sym, P);
354+
profile.add_diag_load(DmnrgProfile::elapsed(timer));
355+
auto rhoFDMPrev = calc_fulldensitymatrix_iterN(step, diag_loaded, rhoFDM, N, store, store_all, stats, Sym, P, &profile);
221356
if (P.checkrho) {
357+
timer = DmnrgProfile::now();
222358
const auto tr = rhoFDMPrev.trace(Sym->multfnc());
223359
const auto expected = std::accumulate(stats.wn.begin() + N, stats.wn.begin() + P.Nmax, 0.0);
224360
const auto diff = (tr - expected) / expected;
225361
nrglog('w', "tr[rhoFDM(" << N << ")]=" << tr << " sum(wn)=" << expected << " diff=" << diff);
226362
my_assert(num_equal(diff, 0.0));
363+
profile.add_trace(DmnrgProfile::elapsed(timer));
227364
}
365+
timer = DmnrgProfile::now();
228366
rhoFDMPrev.save(N-1, P, filename);
367+
profile.add_save(DmnrgProfile::elapsed(timer));
368+
profile.report();
229369
rhoFDM.swap(rhoFDMPrev);
230370
}
231371
}

c++/numerics_Eigen.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,15 @@ void rotate(EM &M, const t_coef factor, const U_type &U, const EM &O) {
117117
}
118118
}
119119

120+
template<scalar S, typename U_type, Eigen_matrix EM, typename t_coef = coef_traits<S>> // XXX: U_type
121+
void rotate_diagonal(EM &M, const t_coef factor, const U_type &U, const EM &O) {
122+
if (finite_size(U)) {
123+
assert(size1(M) == size2(U) && size1(U) == size1(O) && size2(O) == size1(U) && size2(U) == size2(M));
124+
assert(my_isfinite(factor));
125+
M.noalias() += factor * U.adjoint() * O.diagonal().asDiagonal() * U;
126+
}
127+
}
128+
120129
template<scalar S>
121130
Eigen::Block<const EigenMatrix<S>> submatrix_const(const EigenMatrix<S> &M, const std::pair<size_t,size_t> &r1, const std::pair<size_t,size_t> &r2)
122131
{

c++/params.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -450,8 +450,9 @@ class Params {
450450
w - calculation of weights w_n
451451
M - MPI parallelization details
452452
! - debug internal variables
453-
D - DMNRG calculation details
454-
Z - report the values of different partition functions
453+
D - DMNRG calculation details
454+
Y - DMNRG profiling summary
455+
Z - report the values of different partition functions
455456
Useful combinations:
456457
@0 - high-level calculation flow
457458
is - debug matrix construction

test/unit/numerics/numerics.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,18 @@ TEST(numerics, rotate) {
697697
EXPECT_TRUE(r.isApprox(ref));
698698
}
699699

700+
TEST(numerics, rotate_diagonal) {
701+
auto u = generate_matrix<double>(3,2);
702+
auto o = NRG::zero_matrix<double>(3,3);
703+
u(0,0) = 1; u(0,1) = 2; u(1,0) = 3; u(1,1) = 4; u(2,0) = 5; u(2,1) = 6;
704+
o(0,0) = 2; o(1,1) = 3; o(2,2) = 4;
705+
auto diagonal = NRG::zero_matrix<double>(2,2);
706+
auto dense = NRG::zero_matrix<double>(2,2);
707+
rotate_diagonal<double>(diagonal, 1.0, u, o);
708+
rotate<double>(dense, 1.0, u, o);
709+
EXPECT_TRUE(diagonal.isApprox(dense));
710+
}
711+
700712
TEST(numerics, matrix_prod) {
701713
auto a = generate_matrix<double>(2,2);
702714
auto b = generate_matrix<double>(2,2);

0 commit comments

Comments
 (0)