/home/runner/work/DiFfRG_current/DiFfRG_current/DiFfRG/include/DiFfRG/timestepping/jacobian_diagnostics.hh Source File#

DiFfRG: /home/runner/work/DiFfRG_current/DiFfRG_current/DiFfRG/include/DiFfRG/timestepping/jacobian_diagnostics.hh Source File
DiFfRG
Discretization Framework for functional Renormalization Group flows
jacobian_diagnostics.hh
Go to the documentation of this file.
1#pragma once
2
5
6#include <algorithm>
7#include <chrono>
8#include <cmath>
9#include <cstddef>
10#include <limits>
11#include <stdexcept>
12#include <string>
13#include <vector>
14
15namespace DiFfRG
16{
18 double n_rows = 0.;
19 double nnz = 0.;
20 double max_abs_entry = 0.;
21 double frobenius_norm = 0.;
22 double one_norm = 0.;
23 double infinity_norm = 0.;
24 double min_abs_diagonal = 0.;
25 double max_abs_diagonal = 0.;
28 double min_row_max_abs = 0.;
29 double max_row_max_abs = 0.;
30 double min_column_max_abs = 0.;
31 double max_column_max_abs = 0.;
32 };
33
34 enum class ImplicitTimestepperKind : unsigned int {
35 ida = 0,
36 ida_boost_rk = 1,
37 ida_boost_abm = 2,
39 trbdf2 = 4
40 };
41
42 enum class ImplicitTimestepperStage : unsigned int { main = 0, trapezoidal = 1, bdf2 = 2 };
43
45 std::size_t jacobian_build_id = 0;
48 double step = std::numeric_limits<double>::quiet_NaN();
49 double retry_index = std::numeric_limits<double>::quiet_NaN();
50 double ida_step = std::numeric_limits<double>::quiet_NaN();
51 double alpha = std::numeric_limits<double>::quiet_NaN();
52 double beta = std::numeric_limits<double>::quiet_NaN();
53 double residual_weight = std::numeric_limits<double>::quiet_NaN();
54 double last_h = std::numeric_limits<double>::quiet_NaN();
55 double current_h = std::numeric_limits<double>::quiet_NaN();
56 };
57
59 {
60 public:
62 const ImplicitTimestepperKind stepper_kind,
63 const ImplicitTimestepperStage stage, const double current_h,
64 const double alpha, const double beta,
65 const double residual_weight) const
66 {
68 diagnostics.jacobian_build_id = build_id;
69 diagnostics.stepper_kind = stepper_kind;
70 diagnostics.stage = stage;
71 diagnostics.step = static_cast<double>(accepted_steps);
72 diagnostics.retry_index = static_cast<double>(retry_index);
73 diagnostics.alpha = alpha;
74 diagnostics.beta = beta;
75 diagnostics.residual_weight = residual_weight;
76 diagnostics.last_h = last_accepted_h;
77 diagnostics.current_h = current_h;
78 return diagnostics;
79 }
80
81 void finish_attempt(const bool accepted, const double attempted_h)
82 {
83 if (accepted) {
85 retry_index = 0;
86 last_accepted_h = attempted_h;
87 } else {
89 }
90 }
91
92 private:
93 std::size_t accepted_steps = 0;
94 std::size_t retry_index = 0;
95 double last_accepted_h = std::numeric_limits<double>::quiet_NaN();
96 };
97
99 double factorization_ms = std::numeric_limits<double>::quiet_NaN();
100 double factorization_success = std::numeric_limits<double>::quiet_NaN();
101 double scaled_rcond_estimate = std::numeric_limits<double>::quiet_NaN();
102 };
103
104 namespace internal
105 {
114 template <typename MatrixType> std::pair<std::size_t, std::size_t> owned_row_range(const MatrixType &matrix)
115 {
116 if constexpr (requires { matrix.local_range(); }) {
117 const auto range = matrix.local_range();
118 return {static_cast<std::size_t>(range.first), static_cast<std::size_t>(range.second)};
119 } else {
120 return {std::size_t(0), static_cast<std::size_t>(matrix.m())};
121 }
122 }
123
124 template <typename MatrixType> bool is_distributed_matrix(const MatrixType &)
125 {
126 return requires(const MatrixType &m) { m.local_range(); };
127 }
128
129 template <typename MatrixType, typename Visitor>
130 void visit_matrix_row(const MatrixType &matrix, const std::size_t row, Visitor &&visitor)
131 {
132 if constexpr (requires {
133 matrix.begin(row);
134 matrix.end(row);
135 }) {
136 for (auto entry = matrix.begin(row); entry != matrix.end(row); ++entry)
137 visitor(static_cast<std::size_t>(entry->column()), static_cast<double>(entry->value()));
138 } else {
139 for (std::size_t column = 0; column < matrix.n(); ++column)
140 visitor(column, static_cast<double>(matrix(row, column)));
141 }
142 }
143 } // namespace internal
144
145 template <typename MatrixType> JacobianMatrixDiagnostics analyze_jacobian_matrix(const MatrixType &matrix)
146 {
147 if (matrix.m() != matrix.n()) throw std::invalid_argument("analyze_jacobian_matrix: Jacobian must be square");
148
150 const std::size_t n = matrix.m();
151 result.n_rows = static_cast<double>(n);
152 if (n == 0) return result;
153
154 std::vector<double> column_sums(n, 0.);
155 std::vector<double> column_maxima(n, 0.);
156 double squared_frobenius_norm = 0.;
157 result.min_abs_diagonal = std::numeric_limits<double>::infinity();
158 result.min_diagonal_dominance = std::numeric_limits<double>::infinity();
159 result.min_row_max_abs = std::numeric_limits<double>::infinity();
160
161 // Only this rank's rows; the partial results are reduced below. On a serial matrix the range
162 // is the whole matrix and the reduction is a no-op.
163 const auto [row_begin, row_end] = internal::owned_row_range(matrix);
164 for (std::size_t row = row_begin; row < row_end; ++row) {
165 double diagonal = 0.;
166 double row_sum = 0.;
167 double row_maximum = 0.;
168 double off_diagonal_sum = 0.;
169
170 internal::visit_matrix_row(matrix, row, [&](const std::size_t column, const double value) {
171 const double abs_value = std::abs(value);
172 if (value != 0.) result.nnz += 1.;
173 result.max_abs_entry = std::max(result.max_abs_entry, abs_value);
174 squared_frobenius_norm += abs_value * abs_value;
175 row_sum += abs_value;
176 row_maximum = std::max(row_maximum, abs_value);
177 column_sums[column] += abs_value;
178 column_maxima[column] = std::max(column_maxima[column], abs_value);
179 if (column == row)
180 diagonal = abs_value;
181 else
182 off_diagonal_sum += abs_value;
183 });
184
185 result.infinity_norm = std::max(result.infinity_norm, row_sum);
186 result.min_row_max_abs = std::min(result.min_row_max_abs, row_maximum);
187 result.max_row_max_abs = std::max(result.max_row_max_abs, row_maximum);
188 result.min_abs_diagonal = std::min(result.min_abs_diagonal, diagonal);
189 result.max_abs_diagonal = std::max(result.max_abs_diagonal, diagonal);
190 if (diagonal == 0.) result.zero_diagonal_count += 1.;
191
192 const double dominance = off_diagonal_sum > 0. ? diagonal / off_diagonal_sum
193 : (diagonal > 0. ? std::numeric_limits<double>::infinity() : 0.);
194 result.min_diagonal_dominance = std::min(result.min_diagonal_dominance, dominance);
195 }
196
197 if constexpr (requires { matrix.get_mpi_communicator(); }) {
198 // Combine the per-rank partials. Column quantities are accumulated across ranks first,
199 // because a column is generally touched by rows several ranks own -- taking the extremum of
200 // per-rank column sums would understate the one-norm.
201 const auto comm = matrix.get_mpi_communicator();
202 MPI::sum_reduce(comm, column_sums.data(), static_cast<int>(column_sums.size()));
203 MPI::max_reduce(comm, column_maxima.data(), static_cast<int>(column_maxima.size()));
204
205 squared_frobenius_norm = MPI::sum_reduce(comm, squared_frobenius_norm);
206 result.nnz = MPI::sum_reduce(comm, result.nnz);
208
209 result.max_abs_entry = MPI::max_reduce(comm, result.max_abs_entry);
210 result.infinity_norm = MPI::max_reduce(comm, result.infinity_norm);
211 result.max_row_max_abs = MPI::max_reduce(comm, result.max_row_max_abs);
213 result.min_row_max_abs = MPI::min_reduce(comm, result.min_row_max_abs);
216 }
217
218 result.frobenius_norm = std::sqrt(squared_frobenius_norm);
219 result.min_column_max_abs = *std::min_element(column_maxima.begin(), column_maxima.end());
220 result.max_column_max_abs = *std::max_element(column_maxima.begin(), column_maxima.end());
221 result.one_norm = *std::max_element(column_sums.begin(), column_sums.end());
222 return result;
223 }
224
225 inline void record_jacobian_diagnostics(const DiagnosticPort &diagnostics, const std::string &table, const double t,
227 const JacobianMatrixDiagnostics &matrix,
228 const JacobianFactorizationDiagnostics &factorization)
229 {
230 diagnostics.record(table, t,
231 {{"jacobian_build_id", static_cast<double>(build.jacobian_build_id)},
232 {"stepper_kind", static_cast<double>(build.stepper_kind)},
233 {"stage", static_cast<double>(build.stage)},
234 {"step", build.step},
235 {"retry_index", build.retry_index},
236 {"ida_step", build.ida_step},
237 {"alpha", build.alpha},
238 {"beta", build.beta},
239 {"residual_weight", build.residual_weight},
240 {"last_h", build.last_h},
241 {"current_h", build.current_h},
242 {"n_rows", matrix.n_rows},
243 {"nnz", matrix.nnz},
244 {"max_abs_entry", matrix.max_abs_entry},
245 {"frobenius_norm", matrix.frobenius_norm},
246 {"one_norm", matrix.one_norm},
247 {"infinity_norm", matrix.infinity_norm},
248 {"min_abs_diagonal", matrix.min_abs_diagonal},
249 {"max_abs_diagonal", matrix.max_abs_diagonal},
250 {"zero_diagonal_count", matrix.zero_diagonal_count},
251 {"min_diagonal_dominance", matrix.min_diagonal_dominance},
252 {"min_row_max_abs", matrix.min_row_max_abs},
253 {"max_row_max_abs", matrix.max_row_max_abs},
254 {"min_column_max_abs", matrix.min_column_max_abs},
255 {"max_column_max_abs", matrix.max_column_max_abs},
256 {"factorization_ms", factorization.factorization_ms},
257 {"factorization_success", factorization.factorization_success},
258 {"scaled_rcond_estimate", factorization.scaled_rcond_estimate}});
259 }
260
264 template <typename LinearSolver, typename MatrixType>
265 void factorize_with_diagnostics(LinearSolver &solver, const MatrixType &matrix,
266 JacobianFactorizationDiagnostics &diagnostics, const bool estimate_condition)
267 {
268 if constexpr (LinearSolver::performs_factorization) {
269 const auto start = std::chrono::steady_clock::now();
270 try {
271 diagnostics.factorization_success = solver.invert() ? 1. : 0.;
272 } catch (...) {
273 diagnostics.factorization_success = 0.;
274 diagnostics.factorization_ms =
275 std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start).count();
276 throw;
277 }
278 diagnostics.factorization_ms =
279 std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start).count();
280 if (estimate_condition && diagnostics.factorization_success == 1.) {
281 try {
282 diagnostics.scaled_rcond_estimate = solver.estimate_scaled_rcond(matrix, 5);
283 } catch (...) {
284 diagnostics.scaled_rcond_estimate = std::numeric_limits<double>::quiet_NaN();
285 }
286 }
287 } else {
288 solver.invert();
289 }
290 }
291} // namespace DiFfRG
Definition diagnostic_port.hh:21
void record(const std::string &table, double time, const Record &fields) const
Definition jacobian_diagnostics.hh:59
double last_accepted_h
Definition jacobian_diagnostics.hh:95
std::size_t retry_index
Definition jacobian_diagnostics.hh:94
TimestepperJacobianBuildDiagnostics make_build(const std::size_t build_id, const ImplicitTimestepperKind stepper_kind, const ImplicitTimestepperStage stage, const double current_h, const double alpha, const double beta, const double residual_weight) const
Definition jacobian_diagnostics.hh:61
void finish_attempt(const bool accepted, const double attempted_h)
Definition jacobian_diagnostics.hh:81
std::size_t accepted_steps
Definition jacobian_diagnostics.hh:93
void max_reduce(MPI_Comm comm, int *data, int size)
void min_reduce(MPI_Comm comm, int *data, int size)
void sum_reduce(MPI_Comm comm, int *data, int size)
void visit_matrix_row(const MatrixType &matrix, const std::size_t row, Visitor &&visitor)
Definition jacobian_diagnostics.hh:130
bool is_distributed_matrix(const MatrixType &)
Definition jacobian_diagnostics.hh:124
std::pair< std::size_t, std::size_t > owned_row_range(const MatrixType &matrix)
The global rows this rank may read.
Definition jacobian_diagnostics.hh:114
Definition complex_math.hh:10
ImplicitTimestepperKind
Definition jacobian_diagnostics.hh:34
void factorize_with_diagnostics(LinearSolver &solver, const MatrixType &matrix, JacobianFactorizationDiagnostics &diagnostics, const bool estimate_condition)
Definition jacobian_diagnostics.hh:265
JacobianMatrixDiagnostics analyze_jacobian_matrix(const MatrixType &matrix)
Definition jacobian_diagnostics.hh:145
ImplicitTimestepperStage
Definition jacobian_diagnostics.hh:42
void record_jacobian_diagnostics(const DiagnosticPort &diagnostics, const std::string &table, const double t, const TimestepperJacobianBuildDiagnostics &build, const JacobianMatrixDiagnostics &matrix, const JacobianFactorizationDiagnostics &factorization)
Definition jacobian_diagnostics.hh:225
Definition jacobian_diagnostics.hh:98
double factorization_ms
Definition jacobian_diagnostics.hh:99
double factorization_success
Definition jacobian_diagnostics.hh:100
double scaled_rcond_estimate
Definition jacobian_diagnostics.hh:101
Definition jacobian_diagnostics.hh:17
double max_abs_diagonal
Definition jacobian_diagnostics.hh:25
double n_rows
Definition jacobian_diagnostics.hh:18
double zero_diagonal_count
Definition jacobian_diagnostics.hh:26
double nnz
Definition jacobian_diagnostics.hh:19
double min_row_max_abs
Definition jacobian_diagnostics.hh:28
double min_abs_diagonal
Definition jacobian_diagnostics.hh:24
double frobenius_norm
Definition jacobian_diagnostics.hh:21
double min_column_max_abs
Definition jacobian_diagnostics.hh:30
double max_column_max_abs
Definition jacobian_diagnostics.hh:31
double min_diagonal_dominance
Definition jacobian_diagnostics.hh:27
double max_row_max_abs
Definition jacobian_diagnostics.hh:29
double max_abs_entry
Definition jacobian_diagnostics.hh:20
double infinity_norm
Definition jacobian_diagnostics.hh:23
double one_norm
Definition jacobian_diagnostics.hh:22
Definition jacobian_diagnostics.hh:44
double beta
Definition jacobian_diagnostics.hh:52
double retry_index
Definition jacobian_diagnostics.hh:49
ImplicitTimestepperKind stepper_kind
Definition jacobian_diagnostics.hh:46
double residual_weight
Definition jacobian_diagnostics.hh:53
double current_h
Definition jacobian_diagnostics.hh:55
ImplicitTimestepperStage stage
Definition jacobian_diagnostics.hh:47
std::size_t jacobian_build_id
Definition jacobian_diagnostics.hh:45
double ida_step
Definition jacobian_diagnostics.hh:50
double alpha
Definition jacobian_diagnostics.hh:51
double step
Definition jacobian_diagnostics.hh:48
double last_h
Definition jacobian_diagnostics.hh:54