numerics
Loading...
Searching...
No Matches
roots.cpp
Go to the documentation of this file.
1/// @file roots.cpp
2#include "analysis/roots.hpp"
3#include <cmath>
4#include <stdexcept>
5
6namespace num {
7
9 real fa = f(a), fb = f(b);
10 if (fa * fb > 0.0)
11 throw std::invalid_argument("bisection: f(a) and f(b) must have opposite signs");
12
13 for (idx i = 0; i < max_iter; ++i) {
14 real mid = 0.5 * (a + b);
15 real fm = f(mid);
16 if (std::abs(fm) < tol || 0.5 * (b - a) < tol)
17 return {mid, i + 1, std::abs(fm), true};
18 if (fa * fm < 0.0) { b = mid; fb = fm; }
19 else { a = mid; fa = fm; }
20 }
21 real mid = 0.5 * (a + b);
22 return {mid, max_iter, std::abs(f(mid)), false};
23}
24
26 real x = x0;
27 for (idx i = 0; i < max_iter; ++i) {
28 real fx = f(x);
29 if (std::abs(fx) < tol)
30 return {x, i + 1, std::abs(fx), true};
31 real dfx = df(x);
32 if (std::abs(dfx) < 1e-14)
33 return {x, i + 1, std::abs(fx), false}; // near-zero derivative
34 x -= fx / dfx;
35 }
36 return {x, max_iter, std::abs(f(x)), false};
37}
38
40 real f0 = f(x0), f1 = f(x1);
41 for (idx i = 0; i < max_iter; ++i) {
42 if (std::abs(f1) < tol)
43 return {x1, i + 1, std::abs(f1), true};
44 real df = f1 - f0;
45 if (std::abs(df) < 1e-14)
46 return {x1, i + 1, std::abs(f1), false}; // stagnation
47 real x2 = x1 - f1 * (x1 - x0) / df;
48 x0 = x1; f0 = f1;
49 x1 = x2; f1 = f(x1);
50 }
51 return {x1, max_iter, std::abs(f1), false};
52}
53
55 real fa = f(a), fb = f(b);
56 if (fa * fb > 0.0)
57 throw std::invalid_argument("brent: f(a) and f(b) must have opposite signs");
58
59 // c is the point such that [b,c] is always a valid bracket
60 real c = a, fc = fa;
61 real d = b - a, e = d;
62
63 for (idx i = 0; i < max_iter; ++i) {
64 // ensure b is the best estimate
65 if (fb * fc > 0.0) { c = a; fc = fa; d = e = b - a; }
66 if (std::abs(fc) < std::abs(fb)) {
67 a = b; fa = fb;
68 b = c; fb = fc;
69 c = a; fc = fa;
70 }
71
72 real tol1 = 2.0 * 1e-15 * std::abs(b) + 0.5 * tol;
73 real mid = 0.5 * (c - b);
74
75 if (std::abs(mid) <= tol1 || std::abs(fb) < tol)
76 return {b, i + 1, std::abs(fb), true};
77
78 if (std::abs(e) >= tol1 && std::abs(fa) > std::abs(fb)) {
79 // attempt interpolation
80 real s = fb / fa;
81 real p, q;
82 if (a == c) {
83 // secant step
84 p = 2.0 * mid * s;
85 q = 1.0 - s;
86 } else {
87 // inverse quadratic interpolation
88 real r = fb / fc;
89 real t = fa / fc;
90 p = s * (2.0 * mid * t * (t - r) - (b - a) * (r - 1.0));
91 q = (t - 1.0) * (r - 1.0) * (s - 1.0);
92 }
93 if (p > 0.0) q = -q; else p = -p;
94
95 // accept interpolation only if step is smaller than previous
96 real e_prev = e;
97 if (2.0 * p < std::min(3.0 * mid * q - std::abs(tol1 * q),
98 std::abs(e_prev * q))) {
99 e = d;
100 d = p / q;
101 } else {
102 d = mid; e = mid;
103 }
104 } else {
105 d = mid; e = mid;
106 }
107
108 a = b; fa = fb;
109 b += (std::abs(d) > tol1) ? d : (mid > 0.0 ? tol1 : -tol1);
110 fb = f(b);
111 }
112 return {b, max_iter, std::abs(fb), false};
113}
114
115} // namespace num
RootResult secant(ScalarFn f, real x0, real x1, real tol=1e-10, idx max_iter=1000)
Secant method (Newton without derivative)
Definition roots.cpp:39
double real
Definition types.hpp:10
RootResult bisection(ScalarFn f, real a, real b, real tol=1e-10, idx max_iter=1000)
Bisection method.
Definition roots.cpp:8
RootResult brent(ScalarFn f, real a, real b, real tol=1e-10, idx max_iter=1000)
Brent's method (bisection + secant + inverse quadratic interpolation)
Definition roots.cpp:54
constexpr T ipow(T x) noexcept
Compute x^N at compile time via repeated squaring.
std::size_t idx
Definition types.hpp:11
RootResult newton(ScalarFn f, ScalarFn df, real x0, real tol=1e-10, idx max_iter=1000)
Newton-Raphson method.
Definition roots.cpp:25
constexpr real e
Definition math.hpp:41
std::function< real(real)> ScalarFn
Real-valued scalar function f(x)
Definition types.hpp:11
Root-finding methods for scalar equations f(x) = 0.