Theoretica
Scientific Computing
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
61 template<typename RealFunction>
63 RealFunction f, real a, real b,
64 real tol = OPTIMIZATION_TOL,
65 unsigned int max_iter = OPTIMIZATION_BISECTION_ITER) {
66
67 if(a > b) {
68 TH_MATH_ERROR("root_bisect", a, MathError::InvalidArgument);
69 return iter_result<real>(ConvergenceStatus::InvalidInput);
70 }
71
72 if(f(a) * f(b) >= 0) {
73 TH_MATH_ERROR("root_bisect", f(a) * f(b), MathError::InvalidArgument);
74 return iter_result<real>(ConvergenceStatus::InvalidInput);
75 }
76
77 real x_avg = 0.0;
78 real x_min = a;
79 real x_max = b;
80
81 unsigned int iter = 0;
82
83 while((x_max - x_min) > (2 * tol) && iter <= max_iter) {
84
85 x_avg = (x_max + x_min) / 2.0;
86
87 if(f(x_avg) * f(x_min) > 0)
88 x_min = x_avg;
89 else
90 x_max = x_avg;
91
92 ++iter;
93 }
94
95 if(iter > max_iter) {
96 TH_MATH_ERROR("root_bisect", x_avg, MathError::NoConvergence);
97 return iter_result<real>(ConvergenceStatus::MaxIterations, iter, abs(x_max - x_min) / 2.0);
98 }
99
100 return iter_result<real>(x_avg, iter, abs(x_max - x_min) / 2.0);
101 }
102
103
124 template<typename RealFunction>
126 RealFunction f, real a, real b, real tol = OPTIMIZATION_TOL,
127 unsigned int n0 = 1, real k1 = 0.0) {
128
129 // Default value for k1
130 if (k1 == 0.0)
131 k1 = 0.2 / (b - a);
132
133 if(a > b) {
134 TH_MATH_ERROR("root_itp", a, MathError::InvalidArgument);
135 return iter_result<real>(ConvergenceStatus::InvalidInput);
136 }
137
138 real y_a = f(a);
139 real y_b = f(b);
140
141 if(y_a * y_b >= 0) {
142 TH_MATH_ERROR("root_itp", y_a * y_b, MathError::InvalidArgument);
143 return iter_result<real>(ConvergenceStatus::InvalidInput);
144 }
145
146 // Monotonicity of the function
147 const int monotone = (y_a < y_b) ? 1 : -1;
148
149 real x_t, x_new;
150
151 const long int n_half = th::floor(th::log2((b - a) / tol));
152 const long int n_max = n_half + n0;
153
154 real eps = tol * pow(2.0, n_max);
155 long int iter = 0;
156
157 while((b - a) > (2 * tol) && iter <= n_max) {
158
159
160 // Interpolation
161 const real x_f = (a * y_b - b * y_a) / (y_b - y_a);
162 const real x_half = 0.5 * (a + b);
163
164
165 // Truncation
166 const int sigma = sgn(x_half - x_f);
167 const real delta = k1 * square(b - a);
168
169 if (delta <= abs(x_half - x_f))
170 x_t = x_f + sigma * delta;
171 else
172 x_t = x_half;
173
174
175 // Projection
176 const real r = eps - (b - a) / 2.0;
177
178 if (abs(x_t - x_half) <= r)
179 x_new = x_t;
180 else
181 x_new = x_half - sigma * r;
182
183
184 // Update
185 const real y_new = f(x_new);
186
187 if (monotone * y_new > 0) {
188 b = x_new;
189 y_b = y_new;
190 } else if (monotone * y_new < 0) {
191 a = x_new;
192 y_a = y_new;
193 } else {
194 // Falls exactly on the zero
195 return iter_result<real>(x_new, iter, 0.0);
196 }
197
198 eps *= 0.5;
199 iter++;
200 }
201
202 if(abs(b - a) > 2 * tol) {
203 TH_MATH_ERROR("root_itp", (a + b) / 2.0, MathError::NoConvergence);
204 return iter_result<real>(ConvergenceStatus::MaxIterations, iter, abs(b - a) / 2.0);
205 }
206
207 return iter_result<real>((a + b) / 2.0, iter, abs(b - a) / 2.0);
208 }
209
210
222 template<typename RealFunction>
224 RealFunction f, RealFunction Df, real guess = 0.0,
225 real tol = OPTIMIZATION_TOL, unsigned int max_iter = OPTIMIZATION_NEWTON_ITER) {
226
227 real x = guess;
228 real f_x = inf();
229 real Df_x = 0;
230 unsigned int iter = 0;
231
232 while(abs(f_x) > tol && iter <= max_iter) {
233
234 Df_x = Df(x);
235
236 // Check for division by zero
237 if (abs(Df_x) < MACH_EPSILON) {
238 TH_MATH_ERROR("root_newton", Df_x, MathError::DivByZero);
239 return iter_result<real>(ConvergenceStatus::Diverged, iter, f_x);
240 }
241
242 f_x = f(x);
243 x = x - (f_x / Df_x);
244 iter++;
245 }
246
247 if(iter > max_iter) {
248 TH_MATH_ERROR("root_newton", x, MathError::NoConvergence);
249 return iter_result<real>(ConvergenceStatus::MaxIterations, iter, abs(f_x));
250 }
251
252 return iter_result<real>(x, iter, abs(f_x));
253 }
254
255
269 dual(*f)(dual), real guess = 0,
270 real tol = OPTIMIZATION_TOL,
271 unsigned int max_iter = OPTIMIZATION_NEWTON_ITER) {
272
273 real x = guess;
274 dual s = dual(inf(), 0.0);
275 unsigned int iter = 0;
276
277 while(abs(s.Re()) > tol && iter <= max_iter) {
278
279 // Compute the function and its derivative at the same time
280 s = f(dual(x, 1.0));
281
282 x = x - (s.Re() / s.Dual());
283 iter++;
284 }
285
286 if(iter > max_iter) {
287 TH_MATH_ERROR("root_newton", x, MathError::NoConvergence);
288 return iter_result<real>(ConvergenceStatus::MaxIterations, iter, abs(s.Re()));
289 }
290
291 return iter_result<real>(x, iter, abs(s.Re()));
292 }
293
294
306 template <
307 typename Type = real,
308 typename ComplexFunction = std::function<complex<Type>(complex<Type>)>
309 >
311 ComplexFunction f, ComplexFunction df, complex<Type> guess,
312 real tol = OPTIMIZATION_TOL,
313 unsigned int max_iter = OPTIMIZATION_NEWTON_ITER) {
314
315 complex<Type> z = guess;
316 complex<Type> f_z = Type(inf());
317 unsigned int iter = 0;
318
319 while(f_z * f_z.conjugate() > (tol * tol) && iter <= max_iter) {
320
321 f_z = f(z);
322 z = z - (f_z / df(z));
323 iter++;
324 }
325
326 if(iter > max_iter) {
327 TH_MATH_ERROR("root_newton", z.Re(), MathError::NoConvergence);
328 return iter_result<complex<Type>>(ConvergenceStatus::MaxIterations, iter, f_z.norm());
329 }
330
331 return iter_result<complex<Type>>(z, iter, f_z.norm());
332 }
333
334
347 template<typename RealFunction>
349 RealFunction f, RealFunction Df,
350 RealFunction D2f, real guess = 0,
351 real tol = OPTIMIZATION_TOL,
352 unsigned int max_iter = OPTIMIZATION_HALLEY_ITER) {
353
354 real x = guess;
355 real f_x = inf();
356 unsigned int iter = 0;
357
358 while(abs(f_x) > tol && iter <= max_iter) {
359
360 f_x = f(x);
361 const real df_x = Df(x);
362
363 x = x - (2 * f_x * df_x) / (2 * square(df_x) - f_x * D2f(x));
364 iter++;
365 }
366
367 if(iter > max_iter) {
368 TH_MATH_ERROR("root_halley", x, MathError::NoConvergence);
369 return iter_result<real>(ConvergenceStatus::MaxIterations, iter, abs(f_x));
370 }
371
372 return iter_result<real>(x, iter, abs(f_x));
373 }
374
375
390 dual2(*f)(dual2), real guess = 0,
391 real tol = OPTIMIZATION_TOL,
392 unsigned int max_iter = OPTIMIZATION_HALLEY_ITER) {
393
394 real x = guess;
395 dual2 s = dual2(inf(), 0.0, 0.0);
396 unsigned int iter = 0;
397
398 while(abs(s.Re()) > tol && iter <= max_iter) {
399
400 // Compute the function value and the first and
401 // second derivatives at the same time.
402 s = f(dual2(x, 1, 0));
403
404 const real f_x = s.Re();
405 const real df_x = s.Dual1();
406 const real d2f_x = s.Dual2();
407
408 x = x - (2 * f_x * df_x) / (2 * square(df_x) - f_x * d2f_x);
409 iter++;
410 }
411
412 if(iter > max_iter) {
413 TH_MATH_ERROR("root_halley", x, MathError::NoConvergence);
414 return iter_result<real>(ConvergenceStatus::MaxIterations, iter, abs(s.Re()));
415 }
416
417 return iter_result<real>(x, iter, abs(s.Re()));
418 }
419
420
438 template<typename RealFunction>
440 RealFunction f, RealFunction Df,
441 RealFunction D2f, real guess = 0,
442 real tol = OPTIMIZATION_TOL,
443 unsigned int max_iter = OPTIMIZATION_CHEBYSHEV_ITER) {
444
445 real x = guess;
446 real f_x = inf();
447 unsigned int iter = 0;
448
449 while(abs(f_x) > tol && iter <= max_iter) {
450
451 f_x = f(x);
452 const real df_x = Df(x);
453 const real u = f_x / df_x;
454
455 x = x - u - square(u) * D2f(x) / (2.0 * df_x);
456
457 iter++;
458 }
459
460 if(iter > max_iter) {
461 TH_MATH_ERROR("root_chebyshev", x, MathError::NoConvergence);
462 return iter_result<real>(ConvergenceStatus::MaxIterations, iter, abs(f_x));
463 }
464
465 return iter_result<real>(x, iter, abs(f_x));
466 }
467
468
488 dual2(*f)(dual2), real guess = 0,
489 real tol = OPTIMIZATION_TOL,
490 unsigned int max_iter = OPTIMIZATION_CHEBYSHEV_ITER) {
491
492 real x = guess;
493 dual2 s = dual2(inf(), 0.0, 0.0);
494 unsigned int iter = 0;
495
496 while(abs(s.Re()) > tol && iter <= max_iter) {
497
498 s = f(dual2(x, 1.0, 0.0));
499
500 const real f_x = s.Re();
501 const real df_x = s.Dual1();
502 const real u = f_x / df_x;
503
504 x = x - u - square(u) * s.Dual2() / (2.0 * df_x);
505 iter++;
506 }
507
508 if(iter > max_iter) {
509 TH_MATH_ERROR("root_chebyshev", x, MathError::NoConvergence);
510 return iter_result<real>(ConvergenceStatus::MaxIterations, iter, abs(s.Re()));
511 }
512
513 return iter_result<real>(x, iter, abs(s.Re()));
514 }
515
516
533 template<typename RealFunction>
535 RealFunction f, RealFunction Df, real guess = 0.0,
536 real tol = OPTIMIZATION_TOL,
537 unsigned int max_iter = OPTIMIZATION_OSTROWSKI_ITER) {
538
539 real x = guess;
540 real f_x = inf();
541 unsigned int iter = 0;
542
543 while(abs(f_x) > tol && iter <= max_iter) {
544
545 f_x = f(x);
546 const real df_x = Df(x);
547 const real u = f_x / df_x;
548 const real f_xu = f(x - u);
549
550 x = x - u - (f_xu / df_x) * (f_x / (f_x - 2 * f_xu));
551
552 iter++;
553 }
554
555 if(iter > max_iter) {
556 TH_MATH_ERROR("root_ostrowski", x, MathError::NoConvergence);
557 return iter_result<real>(ConvergenceStatus::MaxIterations, iter, abs(f_x));
558 }
559
560 return iter_result<real>(x, iter, abs(f_x));
561 }
562
563
581 template<typename RealFunction>
583 RealFunction f, RealFunction Df, real guess = 0.0,
584 real tol = OPTIMIZATION_TOL,
585 unsigned int max_iter = OPTIMIZATION_JARRAT_ITER) {
586
587 real x = guess;
588 real f_x = inf();
589 unsigned int iter = 0;
590
591 while(abs(f_x) > tol && iter <= max_iter) {
592
593 f_x = f(x);
594 const real df_x = Df(x);
595 const real u = f_x / df_x;
596 const real f_xu = Df(x - 2.0 * u / 3.0);
597
598 x = x - 0.5 * u + f_x / (df_x - 3 * f_xu);
599 iter++;
600 }
601
602 if(iter > max_iter) {
603 TH_MATH_ERROR("root_jarrat", x, MathError::NoConvergence);
604 return iter_result<real>(ConvergenceStatus::MaxIterations, iter, abs(f_x));
605 }
606
607 return iter_result<real>(x, iter, abs(f_x));
608 }
609
610
624 template<typename RealFunction>
626 RealFunction f, real a, real b,
627 real tol = OPTIMIZATION_TOL,
628 unsigned int max_iter = OPTIMIZATION_BISECTION_ITER) {
629
630 return root_itp(f, a, b, tol, max_iter);
631 }
632
633
648 template<typename RealFunction, typename Vector = vec<real>>
649 inline Vector roots(
650 RealFunction f, real a, real b,
651 real tol = OPTIMIZATION_TOL, real steps = 10) {
652
653 if(steps == 0) {
654 TH_MATH_ERROR("roots", steps, MathError::DivByZero);
655 return {nan()};
656 }
657
658 // Find candidate intervals
659 auto intervals = find_root_brackets(f, a, b, steps);
660
661 Vector res;
662 res.resize(intervals.size(), nan());
663 size_t idx = 0;
664
665 // Iterate through all candidate intervals and refine the results
666 for (unsigned int i = 0; i < intervals.size(); ++i) {
667
668 // Check whether the extremes of the candidate intervals
669 // happen to coincide with the roots
670 if(abs(f(intervals[i][0])) <= MACH_EPSILON) {
671 res[idx] = intervals[i][0];
672 idx++;
673 continue;
674 }
675
676 if(abs(f(intervals[i][1])) <= MACH_EPSILON) {
677 res[idx] = intervals[i][1];
678 idx++;
679 continue;
680 }
681
682 // Approximate the roots using bisection inside each interval
683 real r = root_bisect(f, intervals[i][0], intervals[i][1], tol);
684
685 if (!is_nan(r)) {
686 res[idx] = r;
687 idx++;
688 }
689 }
690
691 return res;
692 }
693
694
705 template<typename Field, typename Vector = vec<Field>>
706 inline Vector roots_polynomial(
707 const polynomial<Field>& p, real tolerance = OPTIMIZATION_TOL,
708 unsigned int steps = 0) {
709
710 // Real polynomial degree
711 const unsigned int p_order = p.find_order();
712 if (p_order == 0) {
713 TH_MATH_ERROR("roots_polynomial", p.coeff[0], MathError::InvalidArgument);
714 return {nan()};
715 }
716
717 // Normalize the polynomial by the leading coefficient to apply Cauchy's bound
718 polynomial<Field> p_norm = p / p.coeff[p_order];
719 Field a_max = 0;
720
721 // Find the maximum absolute coefficient
722 for (unsigned int i = 0; i < p_order - 1; ++i)
723 a_max = max(a_max, abs(p_norm.coeff[i]));
724
725 // The roots are bounded in absolute value by the maximum
726 const Field M = 1 + a_max;
727
728 return roots(p_norm, -M, M, tolerance, steps > 0 ? steps : 10 * p_order);
729 }
730
731}
732
733#endif
Complex number in algebraic form .
Definition complex.h:26
Type Re() const
Get the real part of the complex number.
Definition complex.h:74
Type norm() const
Compute the norm of the complex number.
Definition complex.h:134
complex conjugate() const
Compute the conjugate of the complex number.
Definition complex.h:122
Second order dual number class.
Definition dual2.h:29
real Dual2() const
Return second order dual part.
Definition dual2.h:95
real Re() const
Return real part.
Definition dual2.h:85
real Dual1() const
Return first order dual part.
Definition dual2.h:90
Dual number class.
Definition dual.h:28
const real & Dual() const
Return dual part.
Definition dual.h:96
const real & Re() const
Return real part.
Definition dual.h:75
A polynomial of arbitrary order with coefficients of a specified type.
Definition polynomial.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:219
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:90
dual2 log2(dual2 x)
Compute the natural logarithm of a second order dual number.
Definition dual2_functions.h:210
iter_result< 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:439
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:534
Vector make_error()
Create a vector representing an error state, with all NaN values.
Definition algebra.h:103
auto max(const Vector &X)
Finds the maximum value inside a dataset.
Definition dataset.h:326
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:649
TH_CONSTEXPR real nan()
Return a quiet NaN number in floating point representation.
Definition error.h:74
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:62
constexpr real MACH_EPSILON
Machine epsilon for the real type.
Definition constants.h:216
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:582
iter_result< 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:125
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(real x, int n)
Compute the n-th root of x.
Definition real_analysis.h:812
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:96
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:348
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:223
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:706
TH_CONSTEXPR int floor(real x)
Compute the floor of x, as the maximum integer number that is smaller than x.
Definition real_analysis.h:271
A structure returned by iterative algorithms containing the computed value, convergence information,...
Definition iter_result.h:45