/home/runner/work/DiFfRG_current/DiFfRG_current/DiFfRG/include/DiFfRG/common/minimization.hh Source File#

DiFfRG: /home/runner/work/DiFfRG_current/DiFfRG_current/DiFfRG/include/DiFfRG/common/minimization.hh Source File
DiFfRG
Discretization Framework for functional Renormalization Group flows
minimization.hh
Go to the documentation of this file.
1#pragma once
2
3// standard library
4#include <array>
5#include <cmath>
6#include <limits>
7#include <string>
8
9#include <functional>
10#include <spdlog/spdlog.h>
11#include <stdexcept>
12
13// external libraries
14#include <gsl/gsl_errno.h>
15#include <gsl/gsl_math.h>
16#include <gsl/gsl_multimin.h>
17
18namespace DiFfRG
19{
25 template <int dim> class AbstractMinimizer
26 {
27 protected:
28 using FUN = std::function<double(const std::array<double, dim> &)>;
29
30 public:
38 AbstractMinimizer(const FUN &f, const double abs_tol = 1e-4, const int max_iter = 1000)
40 {
41 }
42
48 void set_abs_tol(const double abs_tol) { this->abs_tol = abs_tol; }
49
55 void set_max_iter(const uint max_iter) { this->max_iter = max_iter; }
56
62 uint get_iter() const { return iter; }
63
69 std::array<double, dim> minimize() { return this->minimize_impl(); }
70
71 protected:
73
74 double abs_tol;
77
78 virtual std::array<double, dim> minimize_impl() = 0;
79 };
80
81 // explicit specialization for 1D minimization
82 template <> class AbstractMinimizer<1>
83 {
84 protected:
85 using FUN = std::function<double(const double)>;
86 using FUN_ARR = std::function<double(const std::array<double, 1> &)>;
87
88 public:
96 AbstractMinimizer(const FUN &f, const double abs_tol = 1e-4, const int max_iter = 1000)
97 : f([f](const std::array<double, 1> &x) { return f(x[0]); }), abs_tol(abs_tol), max_iter(max_iter), iter(0)
98 {
99 }
100
108 AbstractMinimizer(const FUN_ARR &f, const double abs_tol = 1e-4, const int max_iter = 1000)
110 {
111 }
112
118 void set_abs_tol(const double abs_tol) { this->abs_tol = abs_tol; }
119
125 void set_max_iter(const uint max_iter) { this->max_iter = max_iter; }
126
132 uint get_iter() const { return iter; }
133
139 double minimize() { return this->minimize_impl()[0]; }
140
141 protected:
143
144 double abs_tol;
147
148 virtual std::array<double, 1> minimize_impl() = 0;
149 };
150
156 template <int dim> class GSLSimplexMinimizer : public AbstractMinimizer<dim>
157 {
159
160 public:
168 GSLSimplexMinimizer(const FUN &f, const double abs_tol = 1e-4, const int max_iter = 1000)
170 {
171 gsl_set_error_handler_off();
172 }
173
179 void set_step_size(const double step_size) { this->step_size = step_size; }
180
186 void set_x0(const std::array<double, dim> &x0) { this->x0 = x0; }
187
197 void set_f_target(const double f_target) { this->f_target = f_target; }
198
200 double get_min_value() const { return min_value; }
201
207 bool converged() const { return m_converged; }
208
210 const std::string &get_stop_reason() const { return stop_reason; }
211
212 static double gsl_wrap(const gsl_vector *v, void *params)
213 {
215 std::array<double, dim> x;
216 for (int i = 0; i < dim; ++i)
217 x[i] = gsl_vector_get(v, i);
218 return self->f(x);
219 }
220
221 protected:
222 virtual std::array<double, dim> minimize_impl() override
223 {
224 gsl_multimin_function gsl_f;
225 gsl_f.n = dim;
227 gsl_f.params = this;
228
229 gsl_vector *x = gsl_vector_alloc(dim);
230 for (int i = 0; i < dim; ++i)
231 gsl_vector_set(x, i, x0[i]);
232
233 gsl_vector *init_step = gsl_vector_alloc(dim);
234 gsl_vector_set_all(init_step, step_size);
235
236 const gsl_multimin_fminimizer_type *T = gsl_multimin_fminimizer_nmsimplex2;
237 gsl_multimin_fminimizer *s = gsl_multimin_fminimizer_alloc(T, dim);
238
239 gsl_multimin_fminimizer_set(s, &gsl_f, x, init_step);
240
241 const bool have_target = std::isfinite(f_target);
242 int status = GSL_CONTINUE;
243 bool iterate_failed = false;
244 this->m_converged = false;
245 this->stop_reason = "iteration limit reached";
246 this->iter = 0;
247 do {
248 this->iter++;
249 if (gsl_multimin_fminimizer_iterate(s)) {
250 iterate_failed = true;
251 this->stop_reason = "GSL simplex iteration failed";
252 break;
253 }
254
255 if (have_target && s->fval <= f_target) {
256 this->m_converged = true;
257 this->stop_reason = "objective reached the target";
258 break;
259 }
260
261 const double size = gsl_multimin_fminimizer_size(s);
262 status = gsl_multimin_test_size(size, this->abs_tol);
263 if (status == GSL_SUCCESS) {
264 // Without a target this IS the criterion; with one, the simplex ran out of room before
265 // the objective got there, which is a real answer about the model, not a failure.
266 this->m_converged = !have_target;
267 this->stop_reason = have_target ? "simplex collapsed short of the target" : "simplex size below tolerance";
268 break;
269 }
270 } while (this->iter < this->max_iter);
271
272 this->min_value = s->fval;
273
274 std::array<double, dim> result;
275 for (int i = 0; i < dim; ++i)
276 result[i] = gsl_vector_get(s->x, i);
277
278 gsl_multimin_fminimizer_free(s);
279 gsl_vector_free(x);
280
281 // Only a genuine solver breakdown is fatal. Stopping short of the target still yields the
282 // best point seen, and the caller can ask converged()/get_stop_reason() about it.
283 if (iterate_failed) throw std::runtime_error("Minimization failed: " + this->stop_reason);
284 if (!this->m_converged)
285 spdlog::warn("GSLSimplexMinimizer: {} after {} iterations; returning the best point with f = {:.6e}",
286 this->stop_reason, this->iter, this->min_value);
287
288 return result;
289 }
290
291 std::array<double, dim> x0;
292 double step_size;
293 double f_target = std::numeric_limits<double>::quiet_NaN();
294 double min_value = std::numeric_limits<double>::quiet_NaN();
295 bool m_converged = false;
296 std::string stop_reason;
297 };
298
303 {
305
306 public:
314 GSLMinimizer1D(const FUN &f, const double abs_tol = 1e-4, const int max_iter = 1000)
315 : AbstractMinimizer<1>(f, abs_tol, max_iter), x0(0.), min_x(-1.), max_x(1.), rel_tol(0.), m(method::brent)
316 {
317 gsl_set_error_handler_off();
318 }
319
324
330 void set_x0(const double x0) { this->x0 = x0; }
331
338 void set_bounds(const double min_x, const double max_x)
339 {
340 this->min_x = min_x;
341 this->max_x = max_x;
342 }
343
349 void set_method(const method m) { this->m = m; }
350
356 void set_rel_tol(const double rel_tol) { this->rel_tol = rel_tol; }
357
358 protected:
359 virtual std::array<double, 1> minimize_impl() override
360 {
361 gsl_function gsl_f;
362 gsl_f.function = &GSLMinimizer1D::gsl_wrap;
363 gsl_f.params = this;
364
365 const gsl_min_fminimizer_type *T;
366 switch (this->m) {
367 case golden_section:
368 T = gsl_min_fminimizer_goldensection;
369 break;
370 case brent:
371 T = gsl_min_fminimizer_brent;
372 break;
373 case quadratic:
374 T = gsl_min_fminimizer_quad_golden;
375 break;
376 default:
377 throw std::runtime_error("Unknown minimization method.");
378 }
379
380 gsl_min_fminimizer *s = gsl_min_fminimizer_alloc(T);
381
382 double x = this->x0 < this->min_x || this->x0 > this->max_x ? 0.5 * (this->min_x + this->max_x) : this->x0;
383 double x_lo = this->min_x;
384 double x_hi = this->max_x;
385
386 gsl_min_fminimizer_set(s, &gsl_f, x, x_lo, x_hi);
387
388 double prev_x = -x;
389 int status;
390 this->iter = 0;
391 int stuck = 0;
392 do {
393 this->iter++;
394 status = gsl_min_fminimizer_iterate(s);
395
396 x = gsl_min_fminimizer_x_minimum(s);
397 x_lo = gsl_min_fminimizer_x_lower(s);
398 x_hi = gsl_min_fminimizer_x_upper(s);
399
400 status = gsl_min_test_interval(x_lo, x_hi, this->abs_tol, this->rel_tol);
401
402 if (is_close(x, prev_x)) stuck++;
403 if (stuck > 3) std::runtime_error("Minimization got stuck at x = " + std::to_string(x));
404 prev_x = x;
405
406 } while (status == GSL_CONTINUE && this->iter < this->max_iter);
407
408 if (status != GSL_SUCCESS) throw std::runtime_error("Minimization failed.");
409
410 gsl_min_fminimizer_free(s);
411
412 return {x};
413 }
414
415 static double gsl_wrap(double x, void *params)
416 {
417 GSLMinimizer1D *self = (GSLMinimizer1D *)params;
418 return self->f({{x}});
419 }
420
421 double x0;
422 double min_x, max_x;
423 double rel_tol;
425 };
426} // namespace DiFfRG
double minimize()
Perform the minimization.
Definition minimization.hh:139
uint iter
Definition minimization.hh:146
std::function< double(const std::array< double, 1 > &)> FUN_ARR
Definition minimization.hh:86
virtual std::array< double, 1 > minimize_impl()=0
void set_abs_tol(const double abs_tol)
Set the absolute tolerance for the minimization.
Definition minimization.hh:118
uint get_iter() const
Get the number of iterations used in the last minimization.
Definition minimization.hh:132
AbstractMinimizer(const FUN &f, const double abs_tol=1e-4, const int max_iter=1000)
Construct a new AbstractMinimizer object.
Definition minimization.hh:96
void set_max_iter(const uint max_iter)
Set the maximum number of iterations.
Definition minimization.hh:125
double abs_tol
Definition minimization.hh:144
AbstractMinimizer(const FUN_ARR &f, const double abs_tol=1e-4, const int max_iter=1000)
Construct a new AbstractMinimizer object.
Definition minimization.hh:108
FUN_ARR f
Definition minimization.hh:142
std::function< double(const double)> FUN
Definition minimization.hh:85
uint max_iter
Definition minimization.hh:145
Abstract class for minimization in arbitrary dimensions.
Definition minimization.hh:26
uint iter
Definition minimization.hh:76
void set_max_iter(const uint max_iter)
Set the maximum number of iterations.
Definition minimization.hh:55
uint max_iter
Definition minimization.hh:75
std::function< double(const std::array< double, dim > &)> FUN
Definition minimization.hh:28
FUN f
Definition minimization.hh:72
AbstractMinimizer(const FUN &f, const double abs_tol=1e-4, const int max_iter=1000)
Construct a new AbstractMinimizer object.
Definition minimization.hh:38
double abs_tol
Definition minimization.hh:74
uint get_iter() const
Get the number of iterations used in the last minimization.
Definition minimization.hh:62
void set_abs_tol(const double abs_tol)
Set the absolute tolerance for the minimization.
Definition minimization.hh:48
std::array< double, dim > minimize()
Perform the minimization.
Definition minimization.hh:69
virtual std::array< double, dim > minimize_impl()=0
Minimizer in 1D using either the golden section, Brent or quadratic method from GSL.
Definition minimization.hh:303
static double gsl_wrap(double x, void *params)
Definition minimization.hh:415
method
List of available minimization methods.
Definition minimization.hh:323
@ quadratic
Definition minimization.hh:323
@ brent
Definition minimization.hh:323
@ golden_section
Definition minimization.hh:323
void set_bounds(const double min_x, const double max_x)
Set the bounds for the minimization.
Definition minimization.hh:338
AbstractMinimizer< 1 >::FUN FUN
Definition minimization.hh:304
void set_x0(const double x0)
Set the initial guess for the minimization.
Definition minimization.hh:330
void set_rel_tol(const double rel_tol)
Set the relative tolerance for the minimization. Default is 0.
Definition minimization.hh:356
double max_x
Definition minimization.hh:422
GSLMinimizer1D(const FUN &f, const double abs_tol=1e-4, const int max_iter=1000)
Construct a new GSLMinimizer1D object.
Definition minimization.hh:314
method m
Definition minimization.hh:424
double x0
Definition minimization.hh:421
double min_x
Definition minimization.hh:422
virtual std::array< double, 1 > minimize_impl() override
Definition minimization.hh:359
void set_method(const method m)
Set the minimization method. Default is Brent.
Definition minimization.hh:349
double rel_tol
Definition minimization.hh:423
Minimizer using the Nelder-Mead simplex algorithm from GSL.
Definition minimization.hh:157
virtual std::array< double, dim > minimize_impl() override
Definition minimization.hh:222
bool m_converged
Definition minimization.hh:295
AbstractMinimizer< dim >::FUN FUN
Definition minimization.hh:158
const std::string & get_stop_reason() const
Why the search stopped, for logging.
Definition minimization.hh:210
std::string stop_reason
Definition minimization.hh:296
static double gsl_wrap(const gsl_vector *v, void *params)
Definition minimization.hh:212
void set_x0(const std::array< double, dim > &x0)
Set the initial guess for the minimization.
Definition minimization.hh:186
double get_min_value() const
Objective value at the point returned by minimize(). NaN before it ran.
Definition minimization.hh:200
double step_size
Definition minimization.hh:292
std::array< double, dim > x0
Definition minimization.hh:291
double f_target
Definition minimization.hh:293
GSLSimplexMinimizer(const FUN &f, const double abs_tol=1e-4, const int max_iter=1000)
Construct a new GSLSimplexMinimizer object.
Definition minimization.hh:168
bool converged() const
Whether the search met its criterion: the f-target if one was set, otherwise the simplex-size test....
Definition minimization.hh:207
double min_value
Definition minimization.hh:294
void set_f_target(const double f_target)
Stop as soon as the objective falls to or below this value.
Definition minimization.hh:197
void set_step_size(const double step_size)
Set the initial step size for the minimization.
Definition minimization.hh:179
Definition complex_math.hh:10
unsigned int uint
Definition utils.hh:24
bool KOKKOS_INLINE_FUNCTION is_close(T1 a, T2 b, T3 eps_)
Function to evaluate whether two floats are equal to numerical precision. Tests for both relative and...
Definition math.hh:177