Theoretica
Mathematical Library
Loading...
Searching...
No Matches
roots.h
Go to the documentation of this file.
1
5
6#ifndef THEORETICA_ROOTS_H
7#define THEORETICA_ROOTS_H
8
9#include "../core/function.h"
10#include "../calculus/deriv.h"
11#include "../autodiff/dual.h"
12#include "../autodiff/dual2.h"
13#include "../algebra/vec.h"
14#include "../complex/complex.h"
15#include "../core/iter_result.h"
16
17
18namespace theoretica {
19
20
30 template<typename RealFunction, typename Bracket = vec2>
31 inline std::vector<Bracket> find_root_brackets(
32 RealFunction f, real a, real b, unsigned int steps = 10) {
33
34 std::vector<Bracket> res;
35 const real dx = (b - a) / steps;
36
37 for (unsigned int i = 0; i < steps; ++i) {
38
39 const real x1 = a + i * dx;
40 const real x2 = a + (i + 1) * dx;
41
42 if(f(x1) * f(x2) <= 0)
43 res.push_back({x1, x2});
44 }
45
46 return res;
47 }
48
49
60 template<typename RealFunction>
61 inline iter_result<real> root_bisect(
62 RealFunction f, real a, real b,
63 real tol = OPTIMIZATION_TOL,
64 unsigned int max_iter = OPTIMIZATION_BISECTION_ITER) {
65
66 if(a > b) {
67 TH_MATH_ERROR("root_bisect", a, MathError::InvalidArgument);
68 return iter_result<real>(ConvergenceStatus::InvalidInput);
69 }
70
71 if(f(a) * f(b) >= 0) {
72 TH_MATH_ERROR("root_bisect", f(a) * f(b), MathError::InvalidArgument);
73 return iter_result<real>(ConvergenceStatus::InvalidInput);
74 }
75
76 real x_avg = 0.0;
77 real x_min = a;
78 real x_max = b;
79
80 unsigned int iter = 0;
81
82 while((x_max - x_min) > (2 * tol) && iter <= max_iter) {
83
84 x_avg = (x_max + x_min) / 2.0;
85
86 if(f(x_avg) * f(x_min) > 0)
87 x_min = x_avg;
88 else
89 x_max = x_avg;
90
91 ++iter;
92 }
93
94 if(iter > max_iter) {
95 TH_MATH_ERROR("root_bisect", x_avg, MathError::NoConvergence);
96 return iter_result<real>(ConvergenceStatus::MaxIterations, iter, abs(b - a) / 2.0);
97 }
98
99 return iter_result<real>(x_avg, iter, abs(b - a) / 2.0);
100 }
101
102
122 template<typename RealFunction>
124 RealFunction f, real a, real b, real tol = OPTIMIZATION_TOL,
125 unsigned int n0 = 1, real k1 = 0.0) {
126
127 // Default value for k1
128 if (k1 == 0.0)
129 k1 = 0.2 / (b - a);
130
131 if(a > b) {
132 TH_MATH_ERROR("root_itp", a, MathError::InvalidArgument);
133 return iter_result<real>(ConvergenceStatus::InvalidInput);
134 }
135
136 real y_a = f(a);
137 real y_b = f(b);
138
139 if(y_a * y_b >= 0) {
140 TH_MATH_ERROR("root_itp", y_a * y_b, MathError::InvalidArgument);
141 return iter_result<real>(ConvergenceStatus::InvalidInput);
142 }
143
144 // Monotonicity of the function
145 const int monotone = (y_a < y_b) ? 1 : -1;
146
147 real x_t, x_new;
148
149 const long int n_half = th::floor(th::log2((b - a) / tol));
150 const long int n_max = n_half + n0;
151
152 real eps = tol * pow(2.0, n_max);
153 long int iter = 0;
154
155 while((b - a) > (2 * tol) && iter <= n_max) {
156
157
158 // Interpolation
159 const real x_f = (a * y_b - b * y_a) / (y_b - y_a);
160 const real x_half = 0.5 * (a + b);
161
162
163 // Truncation
164 const int sigma = sgn(x_half - x_f);
165 const real delta = k1 * square(b - a);
166
167 if (delta <= abs(x_half - x_f))
168 x_t = x_f + sigma * delta;
169 else
170 x_t = x_half;
171
172
173 // Projection
174 const real r = eps - (b - a) / 2.0;
175
176 if (abs(x_t - x_half) <= r)
177 x_new = x_t;
178 else
179 x_new = x_half - sigma * r;
180
181
182 // Update
183 const real y_new = f(x_new);
184
185 if (monotone * y_new > 0) {
186 b = x_new;
187 y_b = y_new;
188 } else if (monotone * y_new < 0) {
189 a = x_new;
190 y_a = y_new;
191 } else {
192 return x_new;
193 }
194
195 eps *= 0.5;
196 iter++;
197 }
198
199 if(abs(b - a) > 2 * tol) {
200 TH_MATH_ERROR("root_itp", (a + b) / 2.0, MathError::NoConvergence);
201 return iter_result<real>(ConvergenceStatus::MaxIterations, iter, abs(b - a) / 2.0);
202 }
203
204 return iter_result<real>((a + b) / 2.0, iter, abs(b - a) / 2.0);
205 }
206
207
219 template<typename RealFunction>
220 inline iter_result<real> root_newton(
221 RealFunction f, RealFunction Df, real guess = 0.0,
222 real tol = OPTIMIZATION_TOL, unsigned int max_iter = OPTIMIZATION_NEWTON_ITER) {
223
224 real x = guess;
225 real f_x = inf();
226 real Df_x = 0;
227 unsigned int iter = 0;
228
229 while(abs(f_x) > tol && iter <= max_iter) {
230
231 Df_x = Df(x);
232
233 // Check for division by zero
234 if (abs(Df_x) < MACH_EPSILON) {
235 TH_MATH_ERROR("root_newton", Df_x, MathError::DivByZero);
236 return iter_result<real>(ConvergenceStatus::Diverged, iter, f_x);
237 }
238
239 f_x = f(x);
240 x = x - (f_x / Df_x);
241 iter++;
242 }
243
244 if(iter > max_iter) {
245 TH_MATH_ERROR("root_newton", x, MathError::NoConvergence);
246 return iter_result<real>(ConvergenceStatus::MaxIterations, iter, abs(f_x));
247 }
248
249 return iter_result<real>(x, iter, abs(f_x));
250 }
251
252
265 inline iter_result<real> root_newton(
266 dual(*f)(dual), real guess = 0,
267 real tol = OPTIMIZATION_TOL,
268 unsigned int max_iter = OPTIMIZATION_NEWTON_ITER) {
269
270 real x = guess;
271 dual s = dual(inf(), 0.0);
272 unsigned int iter = 0;
273
274 while(abs(s.Re()) > tol && iter <= max_iter) {
275
276 // Compute the function and its derivative at the same time
277 s = f(dual(x, 1.0));
278
279 x = x - (s.Re() / s.Dual());
280 iter++;
281 }
282
283 if(iter > max_iter) {
284 TH_MATH_ERROR("root_newton", x, MathError::NoConvergence);
285 return iter_result<real>(ConvergenceStatus::MaxIterations, iter, abs(s.Re()));
286 }
287
288 return iter_result<real>(x, iter, abs(s.Re()));
289 }
290
291
303 template <
304 typename Type = real,
305 typename ComplexFunction = std::function<complex<Type>(complex<Type>)>
306 >
307 inline iter_result<complex<Type>> root_newton(
308 ComplexFunction f, ComplexFunction Df, complex<Type> guess,
309 real tol = OPTIMIZATION_TOL,
310 unsigned int max_iter = OPTIMIZATION_NEWTON_ITER) {
311
312 complex<Type> z = guess;
313 complex<Type> f_z = Type(inf());
314 unsigned int iter = 0;
315
316 while(f_z * f_z.conjugate() > tol * tol && iter <= max_iter) {
317
318 f_z = f(z);
319 z = z - (f_z / df(z));
320 iter++;
321 }
322
323 if(iter > max_iter) {
324 TH_MATH_ERROR("root_newton", z.Re(), MathError::NoConvergence);
325 return iter_result<complex<Type>>(ConvergenceStatus::MaxIterations, iter, f_z.norm());
326 }
327
328 return iter_result<complex<Type>>(z, iter, f_z.norm());
329 }
330
331
344 template<typename RealFunction>
345 inline iter_result<real> root_halley(
346 RealFunction f, RealFunction Df,
347 RealFunction D2f, real guess = 0,
348 real tol = OPTIMIZATION_TOL,
349 unsigned int max_iter = OPTIMIZATION_HALLEY_ITER) {
350
351 real x = guess;
352 real f_x = inf();
353 unsigned int iter = 0;
354
355 while(abs(f_x) > tol && iter <= max_iter) {
356
357 f_x = f(x);
358 const real df_x = Df(x);
359
360 x = x - (2 * f_x * df_x) / (2 * square(df_x) - f_x * D2f(x));
361 iter++;
362 }
363
364 if(iter > max_iter) {
365 TH_MATH_ERROR("root_halley", x, MathError::NoConvergence);
366 return iter_result<real>(ConvergenceStatus::MaxIterations, iter, abs(f_x));
367 }
368
369 return iter_result<real>(x, iter, abs(f_x));
370 }
371
372
386 inline iter_result<real> root_halley(
387 dual2(*f)(dual2), real guess = 0,
388 real tol = OPTIMIZATION_TOL,
389 unsigned int max_iter = OPTIMIZATION_HALLEY_ITER) {
390
391 real x = guess;
392 dual2 s = dual2(inf(), 0.0, 0.0);
393 unsigned int iter = 0;
394
395 while(abs(s.Re()) > tol && iter <= max_iter) {
396
397 // Compute the function value and the first and
398 // second derivatives at the same time.
399 s = f(dual2(x, 1, 0));
400
401 const real f_x = s.Re();
402 const real df_x = s.Dual1();
403 const real d2f_x = s.Dual2();
404
405 x = x - (2 * f_x * df_x) / (2 * square(df_x) - f_x * d2f_x);
406 iter++;
407 }
408
409 if(iter > max_iter) {
410 TH_MATH_ERROR("root_halley", x, MathError::NoConvergence);
411 return iter_result<real>(ConvergenceStatus::MaxIterations, iter, abs(s.Re()));
412 }
413
414 return iter_result<real>(x, iter, abs(s.Re()));
415 }
416
417
428 template<typename RealFunction>
429 inline iter_result<real> root_steffensen(
430 RealFunction f, real guess = 0,
431 real tol = OPTIMIZATION_TOL,
432 unsigned int max_iter = OPTIMIZATION_STEFFENSEN_ITER) {
433
434 real x = guess;
435 real f_x = inf();
436 unsigned int iter = 0;
437
438 while(abs(f_x) > tol && iter <= max_iter) {
439
440 const real f_x = f(x);
441 const real g_x = (f(x + f_x) / f_x) - 1;
442
443 x = x - (f_x / g_x);
444 iter++;
445 }
446
447 if(iter > max_iter) {
448 TH_MATH_ERROR("root_steffensen", x, MathError::NoConvergence);
449 return iter_result<real>(ConvergenceStatus::MaxIterations, iter, abs(f_x));
450 }
451
452 return iter_result<real>(x, iter, abs(f_x));
453 }
454
455
473 template<typename RealFunction>
475 RealFunction f, RealFunction Df,
476 RealFunction D2f, real guess = 0,
477 real tol = OPTIMIZATION_TOL,
478 unsigned int max_iter = OPTIMIZATION_CHEBYSHEV_ITER) {
479
480 real x = guess;
481 real f_x = inf();
482 unsigned int iter = 0;
483
484 while(abs(f_x) > tol && iter <= max_iter) {
485
486 f_x = f(x);
487 const real df_x = Df(x);
488 const real u = f_x / df_x;
489
490 x = x - u - square(u) * D2f(x) / (2.0 * df_x);
491
492 iter++;
493 }
494
495 if(iter > max_iter) {
496 TH_MATH_ERROR("root_chebyshev", x, MathError::NoConvergence);
497 return iter_result<real>(ConvergenceStatus::MaxIterations, iter, abs(f_x));
498 }
499
500 return iter_result<real>(x, iter, abs(f_x));
501 }
502
503
522 inline iter_result<real> root_chebyshev(
523 dual2(*f)(dual2), real guess = 0,
524 real tol = OPTIMIZATION_TOL,
525 unsigned int max_iter = OPTIMIZATION_CHEBYSHEV_ITER) {
526
527 real x = guess;
528 dual2 s = dual2(inf(), 0.0, 0.0);
529 unsigned int iter = 0;
530
531 while(abs(s.Re()) > tol && iter <= max_iter) {
532
533 s = f(dual2(x, 1.0, 0.0));
534
535 const real f_x = s.Re();
536 const real df_x = s.Dual1();
537 const real u = f_x / df_x;
538
539 x = x - u - square(u) * s.Dual2() / (2.0 * df_x);
540 iter++;
541 }
542
543 if(iter > max_iter) {
544 TH_MATH_ERROR("root_chebyshev", x, MathError::NoConvergence);
545 return iter_result<real>(ConvergenceStatus::MaxIterations, iter, abs(s.Re()));
546 }
547
548 return iter_result<real>(x, iter, abs(s.Re()));
549 }
550
551
568 template<typename RealFunction>
569 inline iter_result<real> root_ostrowski(
570 RealFunction f, RealFunction Df, real guess = 0.0,
571 real tol = OPTIMIZATION_TOL,
572 unsigned int max_iter = OPTIMIZATION_OSTROWSKI_ITER) {
573
574 real x = guess;
575 real f_x = inf();
576 unsigned int iter = 0;
577
578 while(abs(f_x) > tol && iter <= max_iter) {
579
580 f_x = f(x);
581 const real df_x = Df(x);
582 const real u = f_x / df_x;
583 const real f_xu = f(x - u);
584
585 x = x - u - (f_xu / df_x) * (f_x / (f_x - 2 * f_xu));
586
587 iter++;
588 }
589
590 if(iter > max_iter) {
591 TH_MATH_ERROR("root_ostrowski", x, MathError::NoConvergence);
592 return iter_result<real>(ConvergenceStatus::MaxIterations, iter, abs(f_x));
593 }
594
595 return iter_result<real>(x, iter, abs(f_x));
596 }
597
598
616 template<typename RealFunction>
617 inline iter_result<real> root_jarrat(
618 RealFunction f, RealFunction Df, real guess = 0.0,
619 real tol = OPTIMIZATION_TOL,
620 unsigned int max_iter = OPTIMIZATION_JARRAT_ITER) {
621
622 real x = guess;
623 real f_x = inf();
624 unsigned int iter = 0;
625
626 while(abs(f_x) > tol && iter <= max_iter) {
627
628 f_x = f(x);
629 const real df_x = Df(x);
630 const real u = f_x / df_x;
631 const real f_xu = Df(x - 2.0 * u / 3.0);
632
633 x = x - 0.5 * u + f_x / (df_x - 3 * f_xu);
634 iter++;
635 }
636
637 if(iter > max_iter) {
638 TH_MATH_ERROR("root_jarrat", x, MathError::NoConvergence);
639 return iter_result<real>(ConvergenceStatus::MaxIterations, iter, abs(f_x));
640 }
641
642 return iter_result<real>(x, iter, abs(f_x));
643 }
644
645
660 template<typename RealFunction, typename Vector = vec<real>>
661 inline Vector roots(
662 RealFunction f, real a, real b,
663 real tol = OPTIMIZATION_TOL, real steps = 10) {
664
665 if(steps == 0) {
666 TH_MATH_ERROR("roots", steps, MathError::DivByZero);
667 return {nan()};
668 }
669
670 // Find candidate intervals
671 auto intervals = find_root_brackets(f, a, b, steps);
672
673 Vector res;
674 res.resize(intervals.size(), nan());
675 size_t idx = 0;
676
677 // Iterate through all candidate intervals and refine the results
678 for (unsigned int i = 0; i < intervals.size(); ++i) {
679
680 // Check whether the extremes of the candidate intervals
681 // happen to coincide with the roots
682 if(abs(f(intervals[i][0])) <= MACH_EPSILON) {
683 res[idx] = intervals[i][0];
684 idx++;
685 continue;
686 }
687
688 if(abs(f(intervals[i][1])) <= MACH_EPSILON) {
689 res[idx] = intervals[i][1];
690 idx++;
691 continue;
692 }
693
694 // Approximate the roots using bisection inside each interval
695 real r = root_bisect(f, intervals[i][0], intervals[i][1], tol);
696
697 if (!is_nan(r)) {
698 res[idx] = r;
699 idx++;
700 }
701 }
702
703 return res;
704 }
705
706
717 template<typename Field, typename Vector = vec<Field>>
718 inline Vector roots_polynomial(
719 const polynomial<Field>& p, real tolerance = OPTIMIZATION_TOL,
720 unsigned int steps = 0) {
721
722 // Real polynomial degree
723 const unsigned int p_order = p.find_order();
724 if (p_order == 0) {
725 TH_MATH_ERROR("roots_polynomial", p.coeff[0], MathError::InvalidArgument);
726 return {nan()};
727 }
728
729 // Normalize the polynomial by the leading coefficient to apply Cauchy's bound
730 polynomial<Field> p_norm = p / p.coeff[p_order];
731 Field a_max = 0;
732
733 // Find the maximum absolute coefficient
734 for (unsigned int i = 0; i < p_order - 1; ++i)
735 a_max = max(a_max, abs(p_norm.coeff[i]));
736
737 // The roots are bounded in absolute value by the maximum
738 const Field M = 1 + a_max;
739
740 return roots(p_norm, -M, M, tolerance, steps > 0 ? steps : 10 * p_order);
741 }
742
743}
744
745#endif
Second order dual number class.
Definition dual2.h:29
Dual number class.
Definition dual.h:28
#define TH_MATH_ERROR(F_NAME, VALUE, EXCEPTION)
TH_MATH_ERROR is a macro which throws exceptions or modifies errno (depending on which compilation op...
Definition error.h:238
Main namespace of the library which contains all functions and objects.
Definition algebra.h:27
double real
A real number, defined as a floating point type.
Definition constants.h:207
bool is_nan(const T &x)
Check whether a generic variable is (equivalent to) a NaN number.
Definition error.h:94
dual2 log2(dual2 x)
Compute the natural logarithm of a second order dual number.
Definition dual2_functions.h:166
iter_result< real > root_ostrowski(RealFunction f, RealFunction Df, real guess=0.0, real tol=OPTIMIZATION_TOL, unsigned int max_iter=OPTIMIZATION_OSTROWSKI_ITER)
Find a root of a univariate real function using Ostrowski's method.
Definition roots.h:569
iter_result< real > root_steffensen(RealFunction f, real guess=0, real tol=OPTIMIZATION_TOL, unsigned int max_iter=OPTIMIZATION_STEFFENSEN_ITER)
Find a root of a univariate real function using Steffensen's method.
Definition roots.h:429
auto max(const Vector &X)
Finds the maximum value inside a dataset.
Definition dataset.h:330
Vector roots(RealFunction f, real a, real b, real tol=OPTIMIZATION_TOL, real steps=10)
Find the roots of a univariate real function inside a given interval, by first searching for candidat...
Definition roots.h:661
TH_CONSTEXPR real nan()
Return a quiet NaN number in floating point representation.
Definition error.h:78
iter_result< real > root_bisect(RealFunction f, real a, real b, real tol=OPTIMIZATION_TOL, unsigned int max_iter=OPTIMIZATION_BISECTION_ITER)
Find the root of a univariate real function using bisection inside a compact interval [a,...
Definition roots.h:61
constexpr real MACH_EPSILON
Machine epsilon for the real type.
Definition constants.h:216
real root_itp(RealFunction f, real a, real b, real tol=OPTIMIZATION_TOL, unsigned int n0=1, real k1=0.0)
Find a root of a univariate real function using the ITP (Interpolate-Truncate-Project) method,...
Definition roots.h:123
iter_result< real > root_jarrat(RealFunction f, RealFunction Df, real guess=0.0, real tol=OPTIMIZATION_TOL, unsigned int max_iter=OPTIMIZATION_JARRAT_ITER)
Find a root of a univariate real function using Jarrat's method.
Definition roots.h:617
std::vector< Bracket > find_root_brackets(RealFunction f, real a, real b, unsigned int steps=10)
Find candidate intervals for root finding by evaluating a function at equidistant points inside an in...
Definition roots.h:31
real root_chebyshev(RealFunction f, RealFunction Df, RealFunction D2f, real guess=0, real tol=OPTIMIZATION_TOL, unsigned int max_iter=OPTIMIZATION_CHEBYSHEV_ITER)
Find a root of a univariate real function using Chebyshev's method.
Definition roots.h:474
int sgn(real x)
Return the sign of x (1 if positive, -1 if negative, 0 if null)
Definition real_analysis.h:259
TH_CONSTEXPR real inf()
Get positive infinity in floating point representation.
Definition error.h:100
iter_result< real > root_halley(RealFunction f, RealFunction Df, RealFunction D2f, real guess=0, real tol=OPTIMIZATION_TOL, unsigned int max_iter=OPTIMIZATION_HALLEY_ITER)
Find a root of a univariate real function using Halley's method.
Definition roots.h:345
iter_result< real > root_newton(RealFunction f, RealFunction Df, real guess=0.0, real tol=OPTIMIZATION_TOL, unsigned int max_iter=OPTIMIZATION_NEWTON_ITER)
Find a root of a univariate real function using Newton's method.
Definition roots.h:220
Vector roots_polynomial(const polynomial< Field > &p, real tolerance=OPTIMIZATION_TOL, unsigned int steps=0)
Find all the roots of a polynomial.
Definition roots.h:718
TH_CONSTEXPR int floor(real x)
Compute the floor of x Computes the maximum integer number that is smaller than x.
Definition real_analysis.h:271