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_SCALAR_ROOT_FINDER_ALGO748_HH
14#define OPTIMIST_SCALAR_ROOT_FINDER_ALGO748_HH
15
17
18namespace Optimist
19{
20 namespace ScalarRootFinder
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 // Function types
52 using FunctionWrapper = typename Bracketing<Real, Algo748<Real>>::FunctionWrapper;
55
60
65 std::string name_impl() const {return "Algo748";}
66
67 private:
76 bool all_different(Real a, Real b, Real c, Real d) const {
77 return a != b && a != c && a != d && b != c && b != d && c != d;
78 }
79
90 bool bracketing(FunctionWrapper function) {
91
92 {
93 Real tolerance{0.7*this->m_tolerance_bracketing};
94 Real hba{(this->m_b - this->m_a)/2.0};
95 if (hba <= tolerance) {this->m_c = this->m_a + hba;}
96 else if (this->m_c <= this->m_a + tolerance) {this->m_c = this->m_a + tolerance;}
97 else if (this->m_c >= this->m_b - tolerance) {this->m_c = this->m_b - tolerance;}
98 }
99
100 // If f(c) = 0 set a = c and return true
101 this->evaluate_function(function, this->m_c, this->m_fc);
102 if (this->m_fc == 0) {
103 this->m_a = this->m_c; this->m_fa = 0.0;
104 this->m_d = 0.0; this->m_fd = 0.0;
105 return true;
106 } else {
107 // If f(c) is not zero, then determine the new enclosing interval
108 if (this->m_fa*this->m_fc < 0) {
109 // D <- B <- C
110 this->m_d = this->m_b; this->m_fd = this->m_fb;
111 this->m_b = this->m_c; this->m_fb = this->m_fc;
112 } else {
113 // D <- A <- C
114 this->m_d = this->m_a; this->m_fd = this->m_fa;
115 this->m_a = this->m_c; this->m_fa = this->m_fc;
116 }
117 return false;
118 }
119 }
120
127
128 // Compute the coefficients for the inverse cubic interpolation
129 Real d_1{this->m_b - this->m_a};
130 Real d_2{this->m_d - this->m_a};
131 Real d_3{this->m_e - this->m_a};
132
133 Real dd_0{d_1/(this->m_fb - this->m_fa)};
134 Real dd_1{(d_1-d_2)/(this->m_fb - this->m_fd)};
135 Real dd_2{(d_2-d_3)/(this->m_fd - this->m_fe)};
136
137 Real ddd_0 {(dd_0 - dd_1)/(this->m_fa - this->m_fd)};
138 Real ddd_1 {(dd_1 - dd_2)/(this->m_fb - this->m_fe)};
139
140 Real dddd_0{(ddd_0-ddd_1)/(this->m_fa-this->m_fe)};
141
142 // Compute the approximate root
143 Real root{this->m_a - this->m_fa*(dd_0 - this->m_fb*(ddd_0 - this->m_fd*dddd_0))};
144 Real tolerance{0.7*this->m_tolerance_bracketing};
145 if (root <= this->m_a + tolerance || root >= this->m_b - tolerance) {
146 root = (this->m_a + this->m_b)/2.0;
147 }
148 return root;
149 }
150
159
160 // Compute the coefficients for the quadratic interpolation
161 Real a_0{this->m_fa};
162 Real a_1{(this->m_fb - this->m_fa)/(this->m_b - this->m_a)};
163 Real a_2{((this->m_fd - this->m_fb)/(this->m_d - this->m_b)-a_1)/(this->m_d - this->m_a)};
164
165 // Safeguard to avoid overflow
166 if (a_2 == 0) {return this->m_a - a_0/a_1;}
167
168 // Determine the starting point of newton steps
169 Real c{a_2*this->m_fa > 0.0 ? this->m_a : this->m_b};
170
171 // Start the safeguarded newton steps
172 bool is_nonzero{true};
173 Real pc{0.0}, pdc{0.0};
174 for (Integer i{0}; i < n && is_nonzero ; ++i ) {
175 pc = a_0 + (a_1 + a_2*(c - this->m_b))*(c - this->m_a);
176 pdc = a_1 + a_2*((2.0 * c) - (this->m_a + this->m_b));
177 is_nonzero = pdc != 0;
178 if (is_nonzero) {c -= pc/pdc;}
179 }
180
181 if (is_nonzero) {return c;}
182 else {return this->m_a - a_0/a_1;}
183 }
184
185 public:
199
200 Real tolerance_step{this->m_tolerance_bracketing};
201 Real tolerance_function{this->m_tolerance_bracketing};
202
203 // Check for trivial solution
204 this->m_converged = this->m_fa == 0; if (this->m_converged) {return this->m_a;}
205 this->m_converged = this->m_fb == 0; if (this->m_converged) {return this->m_b;}
206
207 // Initialize to dumb values
208 this->m_e = QUIET_NAN;
209 this->m_fe = QUIET_NAN;
210
211 // While f(left) or f(right) are infinite perform bisection
212 while (!(std::isfinite(this->m_fa) && std::isfinite(this->m_fb))) {
213 ++this->m_iterations;
214 this->m_c = (this->m_a + this->m_b)/2.0;
215 this->evaluate_function(function, this->m_c, this->m_fc);
216 this->m_converged = this->m_fc == 0;
217 if (this->m_converged) {return this->m_c;}
218 if (this->m_fa*this->m_fc < 0.0) {
219 // -> [a, c]
220 this->m_b = this->m_c; this->m_fb = this->m_fc;
221 } else {
222 // -> [c, b]
223 this->m_a = this->m_c; this->m_fa = this->m_fc;
224 }
225
226 // Check for convergence
227 Real abs_fa{std::abs(this->m_fa)};
228 Real abs_fb{std::abs(this->m_fb)};
229 this->m_converged = (this->m_b - this->m_a) < tolerance_step ||
230 abs_fa < tolerance_function ||abs_fb < tolerance_function;
231 if (this->m_converged) {return abs_fb < abs_fa ? this->m_b : this->m_a;}
232 }
233
234 {
235 // Impede the step c to be too close to the bounds a or b.
236 Real b_a{this->m_b - this->m_a};
237 Real r{b_a/(this->m_fb - this->m_fa)};
238 this->m_c = std::abs(this->m_fb) < std::abs(this->m_fa) ?
239 this->m_b + this->m_fb*r : this->m_a - this->m_fa*r;
240 Real delta{this->m_interval_shink*b_a}, a_min, b_max;
241 if (this->m_c < (a_min = this->m_a + delta)) {this->m_c = a_min;}
242 else if (this->m_c > (b_max = this->m_b - delta)) {this->m_c = b_max;}
243 }
244
245 // Call "bracketing" to get a shrinked enclosing interval and to update the termination criterion
246 this->m_converged = this->bracketing(function);
247 if (this->m_converged ) {return this->m_a;}
248 this->m_converged = false;
249
250 // The enclosing interval is recorded as [a0, b0] before executing the iteration steps
252 ++this->m_iterations;
253 Real a0_b0{this->m_b - this->m_a};
254
255 // Check the termination criterion
256 {
257 Real abs_fa {std::abs(this->m_fa)};
258 Real abs_fb {std::abs(this->m_fb)};
259 this->m_converged = abs_fa < tolerance_function || abs_fb < tolerance_function;
260 if (this->m_converged) {return abs_fa < abs_fb ? this->m_a : this->m_b;}
261 }
262
263 // Starting with the second iteration: newton quadratic or cubic interpolation
264 bool do_newton_quadratic{false};
265 if (!std::isfinite(this->m_fe)) {
266 do_newton_quadratic = true;
267 } else if (!this->all_different( this->m_fa, this->m_fb, this->m_fd, this->m_fe)) {
268 do_newton_quadratic = true;
269 } else {
270 this->m_c = this->cubic_interpolation();
271 do_newton_quadratic = (this->m_c-this->m_a)*(this->m_c-this->m_b) >= 0.0;
272 }
273 if (do_newton_quadratic) {this->m_c = this->newton_quadratic(2);}
274
275 this->m_e = this->m_d;
276 this->m_fe = this->m_fd;
277
278 // Call "bracketing" to get a shrinked enclosing interval and to update the termination criterion
279 this->m_converged = this->bracketing(function) || (this->m_b-this->m_a) <= this->m_tolerance;
280 if (this->m_converged) {
281 return std::abs(this->m_fa) < std::abs(this->m_fb) ? this->m_a : this->m_b;
282 }
283 if (!this->all_different(this->m_fa, this->m_fb, this->m_fd, this->m_fe)) {
284 do_newton_quadratic = true;
285 } else {
286 this->m_c = this->cubic_interpolation();
287 do_newton_quadratic = (this->m_c - this->m_a)*(this->m_c - this->m_b) >= 0.0;
288 }
289 if (do_newton_quadratic) {this->m_c = this->newton_quadratic(3);}
290
291 // Call "bracketing" to get a shrinked enclosing interval and to update the termination criterion
292 {
293 this->m_converged = this->bracketing(function) || (this->m_b-this->m_a) <= this->m_tolerance;
294 Real abs_fa{std::abs(this->m_fa)};
295 Real abs_fb{std::abs(this->m_fb)};
296 if (this->m_converged) {return abs_fa < abs_fb ? this->m_a : this->m_b;}
297
298 this->m_e = this->m_d;
299 this->m_fe = this->m_fd;
300
301 // Takes the double-size secant step
302 Real u, fu;
303 if (abs_fa < abs_fb) {u = this->m_a; fu = this->m_fa;}
304 else {u = this->m_b; fu = this->m_fb;}
305 Real hba{(this->m_b - this->m_a)/2.0};
306 this->m_c = u - 4.0*(fu/(this->m_fb - this->m_fa))*hba;
307 if (std::abs(this->m_c - u) > hba) {this->m_c = this->m_a + hba;}
308 }
309
310 // Call "bracketing" to get a shrinked enclosing interval and to update the termination criterion
311 this->m_converged = this->bracketing(function) || (this->m_b-this->m_a) <= this->m_tolerance_bracketing;
312 if (this->m_converged) {
313 return std::abs(this->m_fa) < std::abs(this->m_fb) ? this->m_a : this->m_b;
314 }
315
316 // Determine whether an additional bisection step is needed
317 if ((this->m_b-this->m_a) < this->m_mu*a0_b0) {continue;}
318
319 this->m_e = this->m_d;
320 this->m_fe = this->m_fd;
321
322 // Call "bracketing" to get a shrinked enclosing interval and to update the termination criterion
323 {
324 Real b_a{this->m_b-this->m_a};
325 this->m_c = this->m_a + b_a/2.0;
326 this->m_converged = this->bracketing(function) || b_a <= this->m_tolerance_bracketing;
327 }
328 }
329 // Return the approximate root
330 return std::abs(this->m_fa) < std::abs(this->m_fb) ? this->m_a : this->m_b;
331 }
332
333 }; // class Algo748
334
335 } // namespace ScalarRootFinder
336
337} // namespace Optimist
338
339#endif // OPTIMIST_SCALAR_ROOT_FINDER_ALGO748_HH
#define OPTIMIST_BASIC_CONSTANTS(Real)
Definition Optimist.hh:70
bool bracketing(FunctionWrapper function)
Definition Algo748.hh:90
Real cubic_interpolation()
Definition Algo748.hh:126
std::string name_impl() const
Definition Algo748.hh:65
static constexpr bool requires_function
Definition Algo748.hh:45
typename Bracketing< Real, Algo748< Real > >::SecondDerivativeWrapper SecondDerivativeWrapper
Definition Algo748.hh:54
Algo748()
Definition Algo748.hh:59
bool all_different(Real a, Real b, Real c, Real d) const
Definition Algo748.hh:76
typename Bracketing< Real, Algo748< Real > >::FunctionWrapper FunctionWrapper
Definition Algo748.hh:52
static constexpr bool requires_first_derivative
Definition Algo748.hh:46
static constexpr bool requires_second_derivative
Definition Algo748.hh:47
Real newton_quadratic(Integer n)
Definition Algo748.hh:158
typename Bracketing< Real, Algo748< Real > >::FirstDerivativeWrapper FirstDerivativeWrapper
Definition Algo748.hh:53
Real find_root_impl(FunctionWrapper function)
Definition Algo748.hh:198
Real tolerance() const
Definition Solver.hh:377
void evaluate_function(FunctionWrapper function, const InputType &x, OutputType &out)
Definition Solver.hh:710
Integer m_iterations
Definition Solver.hh:85
Integer m_max_iterations
Definition Solver.hh:86
Namespace for the Optimist library.
Definition Optimist.hh:87
OPTIMIST_DEFAULT_INTEGER_TYPE Integer
The Integer type as used for the API.
Definition Optimist.hh:95