68 : stencil_(stencil), settings_(settings) {
70 throw std::invalid_argument(
71 "SpreadMinimizer: maximum_iterations must be at least 1, got " +
75 throw std::invalid_argument(
76 "SpreadMinimizer: maximum_step_reductions must not be negative, got " +
80 throw std::invalid_argument(
81 "SpreadMinimizer: restart_interval must be at least 1, got " +
85 throw std::invalid_argument(
86 "SpreadMinimizer: relative_gradient_tolerance must not be negative, got " +
90 throw std::invalid_argument(
91 "SpreadMinimizer: step_reduction_factor must lie in (0, 1), got " +
95 throw std::invalid_argument(
96 "SpreadMinimizer: step_growth_factor must be at least 1, got " +
108 GaugeState state = starting_gauge(overlaps, functional);
109 const Real initial_minimizable = state.terms.minimizable_bohr_squared();
121 Real gradient_floor = 0.0;
123 const std::vector<DenseMatrix<Complex>> gradients =
124 functional.
gradient(state.rotated, state.terms);
125 const Real gradient_norm = norm_per_kpoint(gradients);
126 if (iteration == 1) {
128 std::max(gradient_norm, std::numeric_limits<Real>::min());
130 if (gradient_norm < gradient_floor) {
131 return concluded(Ending::Stationary,
136 initial_minimizable);
139 const std::vector<DenseMatrix<Complex>>& direction =
141 std::optional<GaugeState> lowered =
142 line_search(overlaps, functional, state, direction, step);
149 return concluded(Ending::StepCollapsed,
154 initial_minimizable);
156 state = std::move(*lowered);
157 window.
record(state.terms.minimizable_bohr_squared());
175 const Real settled_gradient_norm =
176 norm_per_kpoint(functional.
gradient(state.rotated, state.terms));
177 return concluded(Ending::SpreadSettled,
180 settled_gradient_norm,
182 initial_minimizable);
186 const Real gradient_norm = norm_per_kpoint(functional.
gradient(state.rotated, state.terms));
187 return concluded(Ending::BudgetExhausted,
192 initial_minimizable);
198 std::vector<DenseMatrix<Complex>> gauges;
204 enum class Ending { Stationary, StepCollapsed, SpreadSettled, BudgetExhausted };
207 [[nodiscard]] GaugeState starting_gauge(
const BlochOverlaps& overlaps,
208 const SpreadFunctional& functional)
const {
209 std::vector<DenseMatrix<Complex>> gauges(
211 DenseMatrix<Complex>::identity(functional.number_of_wannier_functions()));
212 BlochOverlaps rotated = overlaps.transformed_by(gauges, gauges, stencil_);
213 SpreadTerms terms = functional.evaluate(rotated);
214 return GaugeState{std::move(gauges), std::move(rotated), std::move(terms)};
225 [[nodiscard]] std::optional<GaugeState>
226 line_search(
const BlochOverlaps& overlaps,
227 const SpreadFunctional& functional,
228 const GaugeState& state,
229 const std::vector<DenseMatrix<Complex>>& direction,
232 std::vector<DenseMatrix<Complex>> gauges = advanced(state.gauges, direction, step);
233 BlochOverlaps rotated = overlaps.transformed_by(gauges, gauges, stencil_);
234 SpreadTerms terms = functional.evaluate(rotated);
235 if (terms.minimizable_bohr_squared() < state.terms.minimizable_bohr_squared()) {
236 return GaugeState{std::move(gauges), std::move(rotated), std::move(terms)};
244 [[nodiscard]]
static std::vector<DenseMatrix<Complex>>
245 advanced(
const std::vector<DenseMatrix<Complex>>& gauges,
246 const std::vector<DenseMatrix<Complex>>& directions,
248 std::vector<DenseMatrix<Complex>> result;
249 result.reserve(gauges.size());
250 for (std::size_t kpoint = 0; kpoint < gauges.size(); kpoint++) {
261 [[nodiscard]]
static Real norm_per_kpoint(
const std::vector<DenseMatrix<Complex>>& gradients) {
263 static_cast<Real>(gradients.size()));
276 [[nodiscard]] SpreadMinimizationResult concluded(Ending ending,
281 Real initial_minimizable)
const {
282 SpreadMinimizationResult result;
283 result.iterations = iterations;
284 result.initial_minimizable_bohr_squared = initial_minimizable;
285 result.gradient_norm = gradient_norm;
286 result.converged = ending != Ending::BudgetExhausted;
287 result.stationary = ending == Ending::Stationary ||
288 (ending == Ending::SpreadSettled && gradient_norm < gradient_floor);
289 report(ending, result, state.terms, gradient_floor);
290 result.gauges = std::move(state.gauges);
291 result.terms = std::move(state.terms);
295 void report(Ending ending,
296 const SpreadMinimizationResult& result,
297 const SpreadTerms& terms,
298 Real gradient_floor)
const {
299 if (ending == Ending::Stationary) {
300 log::info(
"localization: stationary after {} iterations, Omega = {:.9f} bohr^2",
302 terms.total_bohr_squared());
305 if (ending == Ending::BudgetExhausted) {
307 "localization ran out of iterations after {}: Omega = {:.9f} bohr^2 (Omega_I "
308 "{:.9f}, Omega_D {:.9f}, Omega_OD {:.9f}), gradient {:.3e}. The gauge returned "
309 "is the best one found and is never worse than the projection gauge it started "
310 "from, but the minimisation was cut off rather than finished.",
312 terms.total_bohr_squared(),
313 terms.gauge_invariant_bohr_squared,
314 terms.diagonal_bohr_squared,
315 terms.off_diagonal_bohr_squared,
316 result.gradient_norm);
319 if (result.stationary) {
320 log::info(
"localization: converged in {} iterations, Omega = {:.9f} bohr^2 (Omega_I "
321 "{:.9f}, Omega_D {:.9f}, Omega_OD {:.9f})",
323 terms.total_bohr_squared(),
324 terms.gauge_invariant_bohr_squared,
325 terms.diagonal_bohr_squared,
326 terms.off_diagonal_bohr_squared);
329 log::warn(
"localization stopped short after {} iterations: Omega = {:.9f} bohr^2 (Omega_I "
330 "{:.9f}, Omega_D {:.9f}, Omega_OD {:.9f}), down from {:.9f} minimisable at the "
331 "projection gauge, but the gradient is {:.3e} against the {:.3e} that would "
332 "make it stationary. {}, so this is as far as the minimisation goes; the gauge "
333 "is better than the projection gauge it started from and is not maximally "
334 "localised. Treat the model accordingly.",
336 terms.total_bohr_squared(),
337 terms.gauge_invariant_bohr_squared,
338 terms.diagonal_bohr_squared,
339 terms.off_diagonal_bohr_squared,
340 result.initial_minimizable_bohr_squared,
341 result.gradient_norm,
343 ending == Ending::StepCollapsed
344 ?
"No further step lowered Omega"
345 :
"Omega stopped moving while steps were still being accepted");
348 const KPointStencil& stencil_;
349 SpreadMinimizationSettings settings_;