numerics 0.1.0
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
8RootResult bisection(ScalarFn f, real a, real b, real tol, idx max_iter) {
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) {
19 b = mid;
20 fb = fm;
21 } else {
22 a = mid;
23 fa = fm;
24 }
25 }
26 real mid = 0.5 * (a + b);
27 return {mid, max_iter, std::abs(f(mid)), false};
28}
29
30RootResult newton(ScalarFn f, ScalarFn df, real x0, real tol, idx max_iter) {
31 real x = x0;
32 for (idx i = 0; i < max_iter; ++i) {
33 real fx = f(x);
34 if (std::abs(fx) < tol)
35 return {x, i + 1, std::abs(fx), true};
36 real dfx = df(x);
37 if (std::abs(dfx) < 1e-14)
38 return {x, i + 1, std::abs(fx), false}; // near-zero derivative
39 x -= fx / dfx;
40 }
41 return {x, max_iter, std::abs(f(x)), false};
42}
43
44RootResult secant(ScalarFn f, real x0, real x1, real tol, idx max_iter) {
45 real f0 = f(x0), f1 = f(x1);
46 for (idx i = 0; i < max_iter; ++i) {
47 if (std::abs(f1) < tol)
48 return {x1, i + 1, std::abs(f1), true};
49 real df = f1 - f0;
50 if (std::abs(df) < 1e-14)
51 return {x1, i + 1, std::abs(f1), false}; // stagnation
52 real x2 = x1 - f1 * (x1 - x0) / df;
53 x0 = x1;
54 f0 = f1;
55 x1 = x2;
56 f1 = f(x1);
57 }
58 return {x1, max_iter, std::abs(f1), false};
59}
60
61RootResult brent(ScalarFn f, real a, real b, real tol, idx max_iter) {
62 real fa = f(a), fb = f(b);
63 if (fa * fb > 0.0)
64 throw std::invalid_argument("brent: f(a) and f(b) must have opposite signs");
65
66 // c is the point such that [b,c] is always a valid bracket
67 real c = a, fc = fa;
68 real d = b - a, e = d;
69
70 for (idx i = 0; i < max_iter; ++i) {
71 // ensure b is the best estimate
72 if (fb * fc > 0.0) {
73 c = a;
74 fc = fa;
75 d = e = b - a;
76 }
77 if (std::abs(fc) < std::abs(fb)) {
78 a = b;
79 fa = fb;
80 b = c;
81 fb = fc;
82 c = a;
83 fc = fa;
84 }
85
86 real tol1 = 2.0 * 1e-15 * std::abs(b) + 0.5 * tol;
87 real mid = 0.5 * (c - b);
88
89 if (std::abs(mid) <= tol1 || std::abs(fb) < tol)
90 return {b, i + 1, std::abs(fb), true};
91
92 if (std::abs(e) >= tol1 && std::abs(fa) > std::abs(fb)) {
93 // attempt interpolation
94 real s = fb / fa;
95 real p, q;
96 if (a == c) {
97 // secant step
98 p = 2.0 * mid * s;
99 q = 1.0 - s;
100 } else {
101 // inverse quadratic interpolation
102 real r = fb / fc;
103 real t = fa / fc;
104 p = s * (2.0 * mid * t * (t - r) - (b - a) * (r - 1.0));
105 q = (t - 1.0) * (r - 1.0) * (s - 1.0);
106 }
107 if (p > 0.0)
108 q = -q;
109 else
110 p = -p;
111
112 // accept interpolation only if step is smaller than previous
113 real e_prev = e;
114 if (2.0 * p
115 < std::min(3.0 * mid * q - std::abs(tol1 * q), std::abs(e_prev * q))) {
116 e = d;
117 d = p / q;
118 } else {
119 d = mid;
120 e = mid;
121 }
122 } else {
123 d = mid;
124 e = mid;
125 }
126
127 a = b;
128 fa = fb;
129 b += (std::abs(d) > tol1) ? d : (mid > 0.0 ? tol1 : -tol1);
130 fb = f(b);
131 }
132 return {b, max_iter, std::abs(fb), false};
133}
134
135} // 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:44
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:61
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:30
constexpr real e
Definition math.hpp:44
std::function< real(real)> ScalarFn
Real-valued scalar function f(x)
Definition types.hpp:11
Root-finding methods for scalar equations f(x) = 0.