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)
98203template <scalar S>
99204auto 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}
0 commit comments