65 const std::vector<std::vector<Complex>>& rho_out_g,
66 int number_of_spin_components)
override {
69 int number_of_spin =
static_cast<int>(rho_in_g.size());
70 int number_of_g_vectors =
static_cast<int>(rho_in_g[0].size());
76 int smooth_g_vector_count =
79 : number_of_g_vectors;
82 MixingRecord raw_residual;
83 raw_residual.data.resize(number_of_spin);
84 for (
int spin_index = 0; spin_index < number_of_spin; spin_index++) {
85 raw_residual.data[spin_index].resize(number_of_g_vectors);
86 for (
int g_index = 0; g_index < number_of_g_vectors; g_index++) {
87 raw_residual.data[spin_index][g_index] =
88 rho_out_g[spin_index][g_index] - rho_in_g[spin_index][g_index];
101 raw_residual.data, raw_residual.data, number_of_spin, smooth_g_vector_count);
105 if (residual_norm < best_residual_norm_ || best_residual_norm_ < 0.0) {
106 best_residual_norm_ = residual_norm;
107 stagnation_counter_ = 0;
109 stagnation_counter_++;
113 bool growth_detected = best_residual_norm_ > 0.0 &&
114 residual_norm > 5.0 * best_residual_norm_ &&
117 bool stagnation_detected =
120 if (growth_detected || stagnation_detected) {
121 log::info(
" Broyden mixer: {} (dr2={:.2e}, best={:.2e}, stagnation={}), "
122 "restarting history",
123 growth_detected ?
"residual growth" :
"stagnation",
126 stagnation_counter_);
127 delta_residuals_.clear();
128 delta_inputs_.clear();
129 metric_scaled_delta_residuals_.clear();
130 has_previous_ =
false;
131 iteration_counter_ = 1;
132 best_residual_norm_ = residual_norm;
133 stagnation_counter_ = 0;
142 MixingRecord preconditioned_residual;
143 preconditioned_residual.data.resize(number_of_spin);
144 for (
int spin_index = 0; spin_index < number_of_spin; spin_index++) {
153 bool apply_preconditioning = (spin_index == 0) && (
preconditioner_ !=
nullptr);
154 preconditioned_residual.data[spin_index] = raw_residual.data[spin_index];
155 if (apply_preconditioning) {
161 preconditioned_residual.data,
163 smooth_g_vector_count);
165 auto log_diagnostics = [&](
int number_of_history_entries) {
166 log::debug(
" Broyden diagnostics: raw_dr2={:.8e} preconditioned_dr2={:.8e} "
167 "history_entries={}",
169 preconditioned_residual_norm,
170 number_of_history_entries);
173 int iterations_used = std::min(iteration_counter_ - 1,
history_depth_);
175 if (iteration_counter_ > 1 && has_previous_) {
176 MixingRecord delta_residual_new, delta_input_new;
177 delta_residual_new.data.resize(number_of_spin);
178 delta_input_new.data.resize(number_of_spin);
179 for (
int spin_index = 0; spin_index < number_of_spin; spin_index++) {
180 delta_residual_new.data[spin_index].resize(smooth_g_vector_count);
181 delta_input_new.data[spin_index].resize(smooth_g_vector_count);
182 for (
int g_index = 0; g_index < smooth_g_vector_count; g_index++) {
184 delta_residual_new.data[spin_index][g_index] =
185 previous_preconditioned_residual_.data[spin_index][g_index] -
186 preconditioned_residual.data[spin_index][g_index];
188 delta_input_new.data[spin_index][g_index] =
189 previous_input_.data[spin_index][g_index] - rho_in_g[spin_index][g_index];
208 MixingRecord scaled_delta_residual_new =
209 build_metric_scaled_copy(delta_residual_new, number_of_spin, smooth_g_vector_count);
213 delta_residuals_.push_back(std::move(delta_residual_new));
214 delta_inputs_.push_back(std::move(delta_input_new));
215 metric_scaled_delta_residuals_.push_back(std::move(scaled_delta_residual_new));
218 delta_residuals_[position] = std::move(delta_residual_new);
219 delta_inputs_[position] = std::move(delta_input_new);
220 metric_scaled_delta_residuals_[position] = std::move(scaled_delta_residual_new);
229 previous_preconditioned_residual_.data.resize(number_of_spin);
230 previous_input_.data.resize(number_of_spin);
231 for (
int spin_index = 0; spin_index < number_of_spin; spin_index++) {
232 previous_preconditioned_residual_.data[spin_index].resize(smooth_g_vector_count);
233 previous_input_.data[spin_index].resize(smooth_g_vector_count);
234 for (
int g_index = 0; g_index < smooth_g_vector_count; g_index++) {
235 previous_preconditioned_residual_.data[spin_index][g_index] =
236 preconditioned_residual.data[spin_index][g_index];
237 previous_input_.data[spin_index][g_index] = rho_in_g[spin_index][g_index];
240 has_previous_ =
true;
243 if (iterations_used > 0 && !delta_residuals_.empty()) {
244 int actual_history =
static_cast<int>(delta_residuals_.size());
255 std::vector<Real> gram_matrix(actual_history * actual_history, 0.0);
256 for (
int history_row = 0; history_row < actual_history; history_row++) {
257 for (
int history_column = history_row; history_column < actual_history;
260 dot_product_of_scaled_and_raw(metric_scaled_delta_residuals_[history_row],
261 delta_residuals_[history_column],
262 number_of_spin_components);
263 gram_matrix[history_row + history_column * actual_history] = dot;
264 gram_matrix[history_column + history_row * actual_history] = dot;
283 std::vector<int> pivot_indices(actual_history);
284 std::vector<Real> lapack_work(actual_history);
291 pivot_indices.data(),
296 log::warn(
"Broyden mixing: DSYTRF failed (info={}), falling back to linear", info);
298 rho_in_g, preconditioned_residual, number_of_spin, number_of_g_vectors);
299 log_diagnostics(actual_history);
307 pivot_indices.data(),
311 log::warn(
"Broyden mixing: DSYTRI failed (info={}), falling back to linear", info);
313 rho_in_g, preconditioned_residual, number_of_spin, number_of_g_vectors);
314 log_diagnostics(actual_history);
319 for (
int history_row = 0; history_row < actual_history; history_row++) {
320 for (
int history_column = history_row + 1; history_column < actual_history;
322 gram_matrix[history_column + history_row * actual_history] =
323 gram_matrix[history_row + history_column * actual_history];
335 std::vector<Real> projection(actual_history);
336 for (
int history_index = 0; history_index < actual_history; history_index++) {
337 projection[history_index] =
338 dot_product_of_scaled_and_raw(metric_scaled_delta_residuals_[history_index],
339 preconditioned_residual,
340 number_of_spin_components);
353 const int unit_stride = 1;
354 for (
int history_index = 0; history_index < actual_history; history_index++) {
356 for (
int inner_index = 0; inner_index < actual_history; inner_index++) {
357 gamma += gram_matrix[inner_index + history_index * actual_history] *
358 projection[inner_index];
360 Complex negative_gamma(-gamma, 0.0);
362 for (
int spin_index = 0; spin_index < number_of_spin; spin_index++) {
365 delta_inputs_[history_index].data[spin_index].data(),
367 rho_in_g[spin_index].data(),
371 delta_residuals_[history_index].data[spin_index].data(),
373 preconditioned_residual.data[spin_index].data(),
387 rho_in_g, preconditioned_residual, number_of_spin, smooth_g_vector_count);
388 log_diagnostics(
static_cast<int>(delta_residuals_.size()));