Optimist  0.0.0
A C++ library for optimization
Loading...
Searching...
No Matches
Varona.hh
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
2 * Copyright (c) 2025, Davide Stocco and Enrico Bertolazzi. *
3 * *
4 * The Optimist project is distributed under the BSD 2-Clause License. *
5 * *
6 * Davide Stocco Enrico Bertolazzi *
7 * University of Trento University of Trento *
8 * davide.stocco@unitn.it enrico.bertolazzi@unitn.it *
9\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
10
11#pragma once
12
13#ifndef OPTIMIST_ROOTFINDER_VARONA4_HH
14#define OPTIMIST_ROOTFINDER_VARONA4_HH
15
17
18namespace Optimist {
19 namespace RootFinder {
20
21 /*\
22 | __ __ _ _
23 | \ \ / /_ _ _ __ ___ _ __ __ _| || |
24 | \ \ / / _` | '__/ _ \| '_ \ / _` | || |_
25 | \ V / (_| | | | (_) | | | | (_| |__ _|
26 | \_/ \__,_|_| \___/|_| |_|\__,_| |_|
27 |
28 \*/
29
35 template <typename Scalar>
36 class Varona : public RootFinder<Scalar, Varona<Scalar>> {
37 public:
38 static constexpr bool RequiresFunction{true};
39 static constexpr bool RequiresFirstDerivative{true};
40 static constexpr bool RequiresSecondDerivative{false};
41
43
44
47 using Method = enum class Method : Integer {
48 ORDER_4 = 41,
49 ORDER_8 = 8,
50 ORDER_16 = 16,
51 ORDER_32 = 32
52 };
53
54 private:
55 Method m_method{Method::ORDER_4};
56
57 public:
61 Varona() {}
62
67 constexpr std::string name_impl() const {
68 return "Varona";
69 }
70
75 Method method() const {
76 return this->m_method;
77 }
78
83 void method(Method t_method) {
84 this->m_method = t_method;
85 }
86
91 this->m_method = Method::ORDER_4;
92 }
93
98 this->m_method = Method::ORDER_8;
99 }
100
105 this->m_method = Method::ORDER_16;
106 }
107
112 this->m_method = Method::ORDER_32;
113 }
114
119 void set_method(Method t_method) {
120 this->m_method = t_method;
121 }
122
134 template <typename FunctionLambda, typename FirstDerivativeLambda>
135 bool solve_impl(FunctionLambda &&function,
136 FirstDerivativeLambda &&first_derivative,
137 Scalar x_ini,
138 Scalar &x_sol) {
139#define CMD "Optimist::RootFinder::Varona::solve(...): "
140
141 // Reset internal variables
142 this->reset_counters();
143
144 // Print header
145 if (this->m_verbose) {
146 this->header();
147 }
148
149 // Initialize variables
150 bool damped, success;
151 Scalar residuals, step_norm;
152 Scalar x_old, x_new, function_old, function_new, step_old, step_new;
153 Scalar first_derivative_old;
154
155 // Set initial iteration
156 x_old = x_ini;
157 success =
158 this->evaluate_function(std::forward<FunctionLambda>(function),
159 x_old,
160 function_old);
161 OPTIMIST_ASSERT(success,
162 CMD "function evaluation failed at the initial point.");
163
164 // Algorithm iterations
165 Scalar tolerance_residuals{this->m_tolerance};
166 Scalar tolerance_step_norm{this->m_tolerance * this->m_tolerance};
167 for (this->m_iterations = 1;
168 this->m_iterations < this->m_max_iterations;
169 ++this->m_iterations) {
170 // Evaluate first derivative
171 success = this->evaluate_first_derivative(
172 std::forward<FirstDerivativeLambda>(first_derivative),
173 x_old,
174 first_derivative_old);
175 OPTIMIST_ASSERT(success,
176 CMD "first derivative evaluation failed at iteration "
177 << this->m_iterations << ".");
178
179 // Calculate step
180 if (std::abs(first_derivative_old) < Varona::SQRT_EPSILON) {
182 "close-to-singular first derivative detected.");
183 first_derivative_old = (first_derivative_old > 0)
184 ? Varona::SQRT_EPSILON
185 : -Varona::SQRT_EPSILON;
186 }
187
188 this->compute_step(std::forward<FunctionLambda>(function),
189 x_old,
190 function_old,
191 first_derivative_old,
192 step_old);
193
194 // Check convergence
195 residuals = std::abs(function_old);
196 step_norm = std::abs(step_old);
197 if (this->m_verbose) {
198 this->info(residuals);
199 }
200 if (residuals < tolerance_residuals ||
201 step_norm < tolerance_step_norm) {
202 this->m_converged = true;
203 break;
204 }
205
206 if (this->m_damped) {
207 // Relax the iteration process
208 damped = this->damp(std::forward<FunctionLambda>(function),
209 x_old,
210 function_old,
211 step_old,
212 x_new,
213 function_new,
214 step_new);
215 OPTIMIST_ASSERT_WARNING(damped, CMD "damping failed.");
216 } else {
217 // Update point
218 x_new = x_old + step_old;
219 success =
220 this->evaluate_function(std::forward<FunctionLambda>(function),
221 x_new,
222 function_new);
223 OPTIMIST_ASSERT(success,
224 CMD "function evaluation failed at iteration "
225 << this->m_iterations << ".");
226 }
227
228 // Update internal variables
229 x_old = x_new;
230 function_old = function_new;
231 step_old = step_new;
232 }
233
234 // Print bottom
235 if (this->m_verbose) {
236 this->bottom();
237 }
238
239 // Convergence data
240 x_sol = x_old;
241 return this->m_converged;
242
243#undef CMD
244 }
245
246 protected:
255 template <typename FunctionLambda>
256 void compute_step(FunctionLambda &&function,
257 Scalar x_old,
258 Scalar function_old,
259 Scalar first_derivative_old,
260 Scalar &step_old) {
261#define CMD "Optimist::RootFinder::Varona::compute_step(...): "
262
263 bool success;
264 Scalar function_y, function_z, function_w, function_h, step_tmp, t, s,
265 u, v;
266 Scalar tolerance_residuals{this->m_tolerance};
267
268 // Base step
269 step_old = -function_old / first_derivative_old;
270
271 // Order 4 step
272 if (this->m_method == Method::ORDER_4 ||
273 this->m_method == Method::ORDER_8 ||
274 this->m_method == Method::ORDER_16 ||
275 this->m_method == Method::ORDER_32) {
276 success =
277 this->evaluate_function(std::forward<FunctionLambda>(function),
278 x_old + step_old,
279 function_y);
280 OPTIMIST_ASSERT(success,
281 CMD
282 "function evaluation failed during order 4 step.");
283 if (std::abs(function_y) < tolerance_residuals) {
284 return;
285 }
286 t = function_y / function_old;
287 step_tmp = this->Q(t) * (function_y / first_derivative_old);
288 if (std::isfinite(step_tmp)) {
289 step_old -= step_tmp;
290 } else {
291 return;
292 }
293 }
294
295 // Order 8 step (continued order 4)
296 if (this->m_method == Method::ORDER_8 ||
297 this->m_method == Method::ORDER_16 ||
298 this->m_method == Method::ORDER_32) {
299 success =
300 this->evaluate_function(std::forward<FunctionLambda>(function),
301 x_old + step_old,
302 function_z);
303 OPTIMIST_ASSERT(success,
304 CMD
305 "function evaluation failed during order 8 step.");
306 if (std::abs(function_z) < tolerance_residuals) {
307 return;
308 }
309 s = function_z / function_y;
310 step_tmp = this->W(t, s) * (function_z / first_derivative_old);
311 if (std::isfinite(step_tmp)) {
312 step_old -= step_tmp;
313 } else {
314 return;
315 }
316 }
317
318 // Order 16 step (continued order 8)
319 if (this->m_method == Method::ORDER_16 ||
320 this->m_method == Method::ORDER_32) {
321 success =
322 this->evaluate_function(std::forward<FunctionLambda>(function),
323 x_old + step_old,
324 function_w);
325 OPTIMIST_ASSERT(success,
326 CMD
327 "function evaluation failed during order 16 step.");
328 if (std::abs(function_w) < tolerance_residuals) {
329 return;
330 }
331 u = function_w / function_z;
332 step_tmp = this->H(t, s, u) * (function_w / first_derivative_old);
333 if (std::isfinite(step_tmp)) {
334 step_old -= step_tmp;
335 } else {
336 return;
337 }
338 }
339
340 // Order 32 step (continued order 16)
341 if (this->m_method == Method::ORDER_32) {
342 success =
343 this->evaluate_function(std::forward<FunctionLambda>(function),
344 x_old + step_old,
345 function_h);
346 OPTIMIST_ASSERT(success,
347 CMD
348 "function evaluation failed during order 32 step.");
349 if (std::abs(function_h) < tolerance_residuals) {
350 return;
351 }
352 v = (function_h / function_w);
353 step_tmp = this->J(t, s, u, v) * (function_h / first_derivative_old);
354 if (std::isfinite(step_tmp)) {
355 step_old -= step_tmp;
356 } else {
357 return;
358 }
359 }
360
361 OPTIMIST_ASSERT(std::isfinite(step_old), CMD "step is not finite.");
362
363#undef CMD
364 }
365
371 static Scalar Q(Scalar t) {
372 return 1.0 + 2.0 * t;
373 }
374
381 static Scalar W(Scalar t, Scalar s) {
382 return t * t * (1.0 - 4.0 * t) + (4.0 * s + 2.0) * t + s + 1.0;
383 }
384
392 static Scalar H(Scalar t, Scalar s, Scalar u) {
393 Scalar t1{t * t};
394 Scalar t2{t1 * t1};
395 Scalar t8{s * s};
396 Scalar t17{s * t8};
397 Scalar t23{static_cast<Scalar>(2.0) * u};
398 return ((8.0 * u + 6.0 * t2 + 4.0) * s -
399 (6.0 * t8 + 4.0 * (s + u + 1.0)) * t1 + 2.0 * t8 - 4.0 * t17 +
400 t23 + 2.0) *
401 t +
402 t1 * (t8 + s + u + 1.0) + (1.0 - 3.0 * t2 + t23) * s + u - t17 +
403 1.0;
404 }
405
414 static Scalar J(Scalar t, Scalar s, Scalar u, Scalar v) {
415 Scalar t1{s * s};
416 Scalar t2{t1 * t1};
417 Scalar t17{t * t};
418 Scalar t22{u * u};
419 Scalar t32{t17 * t17};
420 Scalar t34{t * t32};
421 Scalar t37{t * t17};
422 Scalar t46{static_cast<Scalar>(1.0) + v};
423 Scalar t65{u + static_cast<Scalar>(1.0) + v};
424 Scalar t76{static_cast<Scalar>(-2.0 * t22 + u + 4.0 * v + 2.0) * u};
425 return (-1.0 + 2.0 * t) * (2.0 + 5.0 * t) * u * t * t2 +
426 (4.0 * t + 1.0) * u * s * t2 +
427 (u * t22 - 2.0 * u * v - u - v - 1.0) *
428 (4.0 * t17 + 3.0 * t + 1.0) * (-1.0 + t) -
429 8.0 *
430 (t22 * (t17 / 2.0 - 1.0 / 4.0) +
431 u * (t17 * t32 - 5.0 / 8.0 * t34 - 3.0 / 4.0 * t32 +
432 3.0 / 8.0 * t37 + 3.0 / 4.0 * t17 - t / 8.0 -
433 1.0 / 4.0) +
434 3.0 / 4.0 * t46 * (t + 1.0 / 2.0) * (t - 2.0 / 3.0)) *
435 t * t1 +
436 4.0 *
437 (t22 * (-3.0 / 2.0 * t - 1.0 / 4.0) +
438 u * (t34 - t32 - 3.0 / 2.0 * t37 + t17 / 4.0 - t -
439 1.0 / 4.0) -
440 t46 * (t + 1.0 / 4.0)) *
441 s * t1 +
442 (1.0 + v + t65 * t17 - 4.0 * t65 * t37 - 3.0 * t65 * t32 +
443 6.0 * t65 * t34 + t76 + 4.0 * (1.0 + v + t76) * t) *
444 s;
445 }
446
447 }; // class Varona
448
449 } // namespace RootFinder
450
451} // namespace Optimist
452
453#endif // OPTIMIST_ROOTFINDER_VARONA4_HH
#define OPTIMIST_WARNING(MSG)
Definition Optimist.hh:55
#define OPTIMIST_BASIC_CONSTANTS(Scalar)
Definition Optimist.hh:69
#define OPTIMIST_ASSERT_WARNING(COND, MSG)
Definition Optimist.hh:61
#define OPTIMIST_ASSERT(COND, MSG)
Definition Optimist.hh:47
#define CMD
typename TypeTrait< Scalar >::Scalar Scalar
Definition RootFinder.hh:53
void enable_16th_order_method()
Definition Varona.hh:104
bool solve_impl(FunctionLambda &&function, FirstDerivativeLambda &&first_derivative, Scalar x_ini, Scalar &x_sol)
Definition Varona.hh:135
static Scalar W(Scalar t, Scalar s)
Definition Varona.hh:381
Method method() const
Definition Varona.hh:75
static Scalar J(Scalar t, Scalar s, Scalar u, Scalar v)
Definition Varona.hh:414
void enable_4th_order_method()
Definition Varona.hh:90
enum class Method :Integer { ORDER_4=41, ORDER_8=8, ORDER_16=16, ORDER_32=32 } Method
Definition Varona.hh:47
void enable_8th_order_method()
Definition Varona.hh:97
static Scalar H(Scalar t, Scalar s, Scalar u)
Definition Varona.hh:392
void enable_32th_order_method()
Definition Varona.hh:111
Method m_method
Definition Varona.hh:55
constexpr std::string name_impl() const
Definition Varona.hh:67
static constexpr bool RequiresFirstDerivative
Definition Varona.hh:39
Varona()
Definition Varona.hh:61
void set_method(Method t_method)
Definition Varona.hh:119
static constexpr bool RequiresSecondDerivative
Definition Varona.hh:40
static Scalar Q(Scalar t)
Definition Varona.hh:371
void compute_step(FunctionLambda &&function, Scalar x_old, Scalar function_old, Scalar first_derivative_old, Scalar &step_old)
Definition Varona.hh:256
static constexpr bool RequiresFunction
Definition Varona.hh:38
void method(Method t_method)
Definition Varona.hh:83
bool damp(FunctionLambda &&function, const Scalar &x_old, const Scalar &function_old, const Scalar &step_old, Scalar &x_new, Scalar &function_new, Scalar &step_new)
Definition SolverBase.hh:1109
void info(Scalar residuals, const std::string &notes="-")
Definition SolverBase.hh:1230
bool evaluate_function(FunctionLambda &&function, const Scalar &x, Scalar &out)
Definition SolverBase.hh:1040
bool evaluate_first_derivative(FirstDerivativeLambda &&function, const Scalar &x, FirstDerivative &out)
Definition SolverBase.hh:1061
Integer m_max_iterations
Definition SolverBase.hh:121
Scalar m_tolerance
Definition SolverBase.hh:128
void reset_counters()
Definition SolverBase.hh:1022
Integer m_iterations
Definition SolverBase.hh:120
Namespace for the Optimist library.
Definition Optimist.hh:89
OPTIMIST_DEFAULT_INTEGER_TYPE Integer
The Integer type as used for the API.
Definition Optimist.hh:97