Optimist  0.0.0
A C++ library for optimization
Loading...
Searching...
No Matches
Algo748.hh
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
2 * Copyright (c) 2025, Davide Stocco, Mattia Piazza and Enrico Bertolazzi. *
3 * *
4 * The Optimist project is distributed under the BSD 2-Clause License. *
5 * *
6 * Davide Stocco Mattia Piazza Enrico Bertolazzi *
7 * University of Trento University of Trento University of Trento *
8 * davide.stocco@unitn.it mattia.piazza@unitn.it enrico.bertolazzi@unitn.it *
9\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
10
11#pragma once
12
13#ifndef OPTIMIST_ROOTFINDER_ALGO748_HH
14#define OPTIMIST_ROOTFINDER_ALGO748_HH
15
17
18namespace Optimist
19{
20 namespace RootFinder
21 {
22
23 /*\
24 | _ _ _____ _ _ ___
25 | / \ | | __ _ __|___ | || | ( _ )
26 | / _ \ | |/ _` |/ _ \ / /| || |_ / _ \
27 | / ___ \| | (_| | (_) / / |__ _| (_) |
28 | /_/ \_\_|\__, |\___/_/ |_| \___/
29 | |___/
30 \*/
31
41 template <typename Real>
42 class Algo748 : public Bracketing<Real, Algo748<Real>>
43 {
44 public:
45 static constexpr bool requires_function{true};
46 static constexpr bool requires_first_derivative{false};
47 static constexpr bool requires_second_derivative{false};
48
50
51
55
60 std::string name_impl() const {return "Algo748";}
61
62 private:
71 bool all_different(Real a, Real b, Real c, Real d) const {
72 return a != b && a != c && a != d && b != c && b != d && c != d;
73 }
74
86 template <typename FunctionLambda>
87 bool bracketing(FunctionLambda && function)
88 {
89 #define CMD "Optimist::ScalarRootfinder::Algo748::bracketing(...): "
90
91 {
92 Real tolerance{0.7*this->m_tolerance_bracketing};
93 Real hba{(this->m_b - this->m_a)/2.0};
94 if (hba <= tolerance) {this->m_c = this->m_a + hba;}
95 else if (this->m_c <= this->m_a + tolerance) {this->m_c = this->m_a + tolerance;}
96 else if (this->m_c >= this->m_b - tolerance) {this->m_c = this->m_b - tolerance;}
97 }
98
99 // If f(c) = 0 set a = c and return true
100 bool success{this->evaluate_function(std::forward<FunctionLambda>(function), this->m_c, this->m_fc)};
102 CMD "function evaluation failed during bracketing.");
103 if (this->m_fc == 0) {
104 this->m_a = this->m_c; this->m_fa = 0.0;
105 this->m_d = 0.0; this->m_fd = 0.0;
106 return true;
107 } else {
108 // If f(c) is not zero, then determine the new enclosing interval
109 if (this->m_fa*this->m_fc < 0) {
110 // D <- B <- C
111 this->m_d = this->m_b; this->m_fd = this->m_fb;
112 this->m_b = this->m_c; this->m_fb = this->m_fc;
113 } else {
114 // D <- A <- C
115 this->m_d = this->m_a; this->m_fd = this->m_fa;
116 this->m_a = this->m_c; this->m_fa = this->m_fc;
117 }
118 return false;
119 }
120
121 #undef CMD
122 }
123
130 {
131 // Compute the coefficients for the inverse cubic interpolation
132 Real d_1{this->m_b - this->m_a};
133 Real d_2{this->m_d - this->m_a};
134 Real d_3{this->m_e - this->m_a};
135
136 Real dd_0{d_1/(this->m_fb - this->m_fa)};
137 Real dd_1{(d_1-d_2)/(this->m_fb - this->m_fd)};
138 Real dd_2{(d_2-d_3)/(this->m_fd - this->m_fe)};
139
140 Real ddd_0 {(dd_0 - dd_1)/(this->m_fa - this->m_fd)};
141 Real ddd_1 {(dd_1 - dd_2)/(this->m_fb - this->m_fe)};
142
143 Real dddd_0{(ddd_0-ddd_1)/(this->m_fa-this->m_fe)};
144
145 // Compute the approximate root
146 Real root{this->m_a - this->m_fa*(dd_0 - this->m_fb*(ddd_0 - this->m_fd*dddd_0))};
147 Real tolerance{0.7*this->m_tolerance_bracketing};
148 if (root <= this->m_a + tolerance || root >= this->m_b - tolerance) {
149 root = (this->m_a + this->m_b)/2.0;
150 }
151 return root;
152 }
153
162 {
163 // Compute the coefficients for the quadratic interpolation
164 Real a_0{this->m_fa};
165 Real a_1{(this->m_fb - this->m_fa)/(this->m_b - this->m_a)};
166 Real a_2{((this->m_fd - this->m_fb)/(this->m_d - this->m_b)-a_1)/(this->m_d - this->m_a)};
167
168 // Safeguard to avoid overflow
169 if (a_2 == 0) {return this->m_a - a_0/a_1;}
170
171 // Determine the starting point of newton steps
172 Real c{a_2*this->m_fa > 0.0 ? this->m_a : this->m_b};
173
174 // Start the safeguarded newton steps
175 bool is_nonzero{true};
176 Real pc{0.0}, pdc{0.0};
177 for (Integer i{0}; i < n && is_nonzero ; ++i ) {
178 pc = a_0 + (a_1 + a_2*(c - this->m_b))*(c - this->m_a);
179 pdc = a_1 + a_2*((2.0 * c) - (this->m_a + this->m_b));
180 is_nonzero = pdc != 0;
181 if (is_nonzero) {c -= pc/pdc;}
182 }
183
184 if (is_nonzero) {return c;}
185 else {return this->m_a - a_0/a_1;}
186 }
187
188 public:
202 template <typename FunctionLambda>
203 Real find_root_impl(FunctionLambda && function)
204 {
205 #define CMD "Optimist::ScalarRootfinder::Algo748::find_root_impl(...): "
206
207 bool success;
208 Real tolerance_step{this->m_tolerance_bracketing};
209 Real tolerance_function{this->m_tolerance_bracketing};
210
211 // Check for trivial solution
212 this->m_converged = this->m_fa == 0; if (this->m_converged) {return this->m_a;}
213 this->m_converged = this->m_fb == 0; if (this->m_converged) {return this->m_b;}
214
215 // Initialize to dumb values
216 this->m_e = QUIET_NAN;
217 this->m_fe = QUIET_NAN;
218
219 // While f(left) or f(right) are infinite perform bisection
220 while (!(std::isfinite(this->m_fa) && std::isfinite(this->m_fb))) {
221 ++this->m_iterations;
222 this->m_c = (this->m_a + this->m_b)/2.0;
223 success = this->evaluate_function(std::forward<FunctionLambda>(function), this->m_c, this->m_fc);
225 CMD "function evaluation failed at iteration " << this->m_iterations << ".");
226 this->m_converged = this->m_fc == 0;
227 if (this->m_converged) {return this->m_c;}
228 if (this->m_fa*this->m_fc < 0.0) {
229 // -> [a, c]
230 this->m_b = this->m_c; this->m_fb = this->m_fc;
231 } else {
232 // -> [c, b]
233 this->m_a = this->m_c; this->m_fa = this->m_fc;
234 }
235
236 // Check for convergence
237 Real abs_fa{std::abs(this->m_fa)};
238 Real abs_fb{std::abs(this->m_fb)};
239 this->m_converged = (this->m_b - this->m_a) < tolerance_step ||
240 abs_fa < tolerance_function ||abs_fb < tolerance_function;
241 if (this->m_converged) {return abs_fb < abs_fa ? this->m_b : this->m_a;}
242 }
243
244 {
245 // Impede the step c to be too close to the bounds a or b.
246 Real b_a{this->m_b - this->m_a};
247 Real r{b_a/(this->m_fb - this->m_fa)};
248 this->m_c = std::abs(this->m_fb) < std::abs(this->m_fa) ?
249 this->m_b + this->m_fb*r : this->m_a - this->m_fa*r;
250 Real delta{this->m_interval_shink*b_a}, a_min, b_max;
251 if (this->m_c < (a_min = this->m_a + delta)) {this->m_c = a_min;}
252 else if (this->m_c > (b_max = this->m_b - delta)) {this->m_c = b_max;}
253 }
254
255 // Call "bracketing" to get a shrinked enclosing interval and to update the termination criterion
256 this->m_converged = this->bracketing(std::forward<FunctionLambda>(function));
257 if (this->m_converged ) {return this->m_a;}
258 this->m_converged = false;
259
260 // The enclosing interval is recorded as [a0, b0] before executing the iteration steps
262 ++this->m_iterations;
263 Real a0_b0{this->m_b - this->m_a};
264
265 // Check the termination criterion
266 {
267 Real abs_fa {std::abs(this->m_fa)};
268 Real abs_fb {std::abs(this->m_fb)};
269 this->m_converged = abs_fa < tolerance_function || abs_fb < tolerance_function;
270 if (this->m_converged) {return abs_fa < abs_fb ? this->m_a : this->m_b;}
271 }
272
273 // Starting with the second iteration: newton quadratic or cubic interpolation
274 bool do_newton_quadratic{false};
275 if (!std::isfinite(this->m_fe)) {
276 do_newton_quadratic = true;
277 } else if (!this->all_different( this->m_fa, this->m_fb, this->m_fd, this->m_fe)) {
278 do_newton_quadratic = true;
279 } else {
280 this->m_c = this->cubic_interpolation();
281 do_newton_quadratic = (this->m_c-this->m_a)*(this->m_c-this->m_b) >= 0.0;
282 }
283 if (do_newton_quadratic) {this->m_c = this->newton_quadratic(2);}
284
285 this->m_e = this->m_d;
286 this->m_fe = this->m_fd;
287
288 // Call "bracketing" to get a shrinked enclosing interval and to update the termination criterion
289 this->m_converged = this->bracketing(std::forward<FunctionLambda>(function)) || (this->m_b-this->m_a) <= this->m_tolerance;
290 if (this->m_converged) {
291 return std::abs(this->m_fa) < std::abs(this->m_fb) ? this->m_a : this->m_b;
292 }
293 if (!this->all_different(this->m_fa, this->m_fb, this->m_fd, this->m_fe)) {
294 do_newton_quadratic = true;
295 } else {
296 this->m_c = this->cubic_interpolation();
297 do_newton_quadratic = (this->m_c - this->m_a)*(this->m_c - this->m_b) >= 0.0;
298 }
299 if (do_newton_quadratic) {this->m_c = this->newton_quadratic(3);}
300
301 // Call "bracketing" to get a shrinked enclosing interval and to update the termination criterion
302 {
303 this->m_converged = this->bracketing(std::forward<FunctionLambda>(function)) || (this->m_b-this->m_a) <= this->m_tolerance;
304 Real abs_fa{std::abs(this->m_fa)};
305 Real abs_fb{std::abs(this->m_fb)};
306 if (this->m_converged) {return abs_fa < abs_fb ? this->m_a : this->m_b;}
307
308 this->m_e = this->m_d;
309 this->m_fe = this->m_fd;
310
311 // Takes the double-size secant step
312 Real u, fu;
313 if (abs_fa < abs_fb) {u = this->m_a; fu = this->m_fa;}
314 else {u = this->m_b; fu = this->m_fb;}
315 Real hba{(this->m_b - this->m_a)/2.0};
316 this->m_c = u - 4.0*(fu/(this->m_fb - this->m_fa))*hba;
317 if (std::abs(this->m_c - u) > hba) {this->m_c = this->m_a + hba;}
318 }
319
320 // Call "bracketing" to get a shrinked enclosing interval and to update the termination criterion
321 this->m_converged = this->bracketing(std::forward<FunctionLambda>(function)) || (this->m_b-this->m_a) <= this->m_tolerance_bracketing;
322 if (this->m_converged) {
323 return std::abs(this->m_fa) < std::abs(this->m_fb) ? this->m_a : this->m_b;
324 }
325
326 // Determine whether an additional bisection step is needed
327 if ((this->m_b-this->m_a) < this->m_mu*a0_b0) {continue;}
328
329 this->m_e = this->m_d;
330 this->m_fe = this->m_fd;
331
332 // Call "bracketing" to get a shrinked enclosing interval and to update the termination criterion
333 {
334 Real b_a{this->m_b-this->m_a};
335 this->m_c = this->m_a + b_a/2.0;
336 this->m_converged = this->bracketing(std::forward<FunctionLambda>(function)) || b_a <= this->m_tolerance_bracketing;
337 }
338 }
339 // Return the approximate root
340 return std::abs(this->m_fa) < std::abs(this->m_fb) ? this->m_a : this->m_b;
341
342 #undef CMD
343 }
344
345 }; // class Algo748
346
347 } // namespace RootFinder
348
349} // namespace Optimist
350
351#endif // OPTIMIST_ROOTFINDER_ALGO748_HH
#define OPTIMIST_ASSERT_WARNING(COND, MSG)
Definition Optimist.hh:61
#define OPTIMIST_BASIC_CONSTANTS(Real)
Definition Optimist.hh:71
#define CMD
Real cubic_interpolation()
Definition Algo748.hh:129
static constexpr bool requires_second_derivative
Definition Algo748.hh:47
Real find_root_impl(FunctionLambda &&function)
Definition Algo748.hh:203
Algo748()
Definition Algo748.hh:54
std::string name_impl() const
Definition Algo748.hh:60
bool all_different(Real a, Real b, Real c, Real d) const
Definition Algo748.hh:71
static constexpr bool requires_function
Definition Algo748.hh:45
static constexpr bool requires_first_derivative
Definition Algo748.hh:46
Real newton_quadratic(Integer n)
Definition Algo748.hh:161
bool bracketing(FunctionLambda &&function)
Definition Algo748.hh:87
Real m_tolerance_bracketing
Definition Bracketing.hh:46
Integer m_max_iterations
Definition SolverBase.hh:85
Real tolerance() const
Definition SolverBase.hh:392
bool evaluate_function(FunctionLambda &&function, InputType const &x, OutputType &out)
Definition SolverBase.hh:768
Namespace for the Optimist library.
Definition Optimist.hh:88
OPTIMIST_DEFAULT_INTEGER_TYPE Integer
The Integer type as used for the API.
Definition Optimist.hh:96