-
Notifications
You must be signed in to change notification settings - Fork 2
/
brent.h
111 lines (98 loc) · 2.95 KB
/
brent.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/**
* Brent's method from the Bible.
*/
#ifndef _BRENT_H_
#define _BRENT_H_
#include <cmath>
#include <limits>
#include <stdexcept>
/* class Brent
* Solve a 1D root finding problem. Follows Numerical Recipes.
*/
class Brent {
public:
Brent(){};
Brent(double tol) : _tol(tol){};
Brent(double tol, int max_iter=128) : _tol(tol), _max_iter(max_iter){};
template <class System>
double solve(double a, double b, System& sys) {
//cout<<"starting brent with tol/maxint = "<<_tol<<"/"<<_max_iter<<endl;
using std::abs;
using std::min;
double fa = sys(a);
double fb = sys(b);
if (not(fa * fb <= 0))
throw std::invalid_argument("Root must be bracketed");
double EPS = std::numeric_limits<double>::epsilon();
double c = b, fc = fb;
double d=0, e=0;
for (int i = 0; i < _max_iter; i++) {
// Order interval
if (fb * fc > 0) {
c = a;
fc = fa;
e = d = b - a;
}
if (abs(fc) < abs(fb)) {
a = b;
b = c;
c = a;
fa = fb;
fb = fc;
fc = fa;
}
double tol1 = 2 * EPS * abs(b) + 0.5 * _tol;
double xm = 0.5 * (c - b);
if (abs(xm) <= tol1) return b;
// Interpolate to new root
if (abs(e) >= tol1 && abs(fa) > abs(fb)) {
double s = fb / fa;
double p, q;
if (a == c) { // Use linear
p = 2 * xm * s;
q = 1 - s;
} else { // Use inverse quadratic
double r;
q = fa / fc;
r = fb / fc;
p = s * (2 * xm * q * (q - r) - (b - a) * (r - 1));
q = (q - 1) * (r - 1) * (s - 1);
}
if (p > 0)
q = -q;
else
p = -p;
// Check whether the interpolation succeeded
if (2 * p < min(3 * xm * q - abs(tol1 * q), abs(e * q))) {
e = d; // Accept interpolation
d = p / q;
} else {
d = xm; // Use bisection
e = d;
}
} // Slow convergence
else {
d = xm;
e = d;
}
// Store previous best guess
a = b;
fa = fb;
// Evaluate new root
if (abs(d) > tol1)
b += d;
else {
if (xm > 0)
b += tol1;
else
b -= tol1;
}
fb = sys(b);
}
throw std::runtime_error("Brent's method did not converge");
}
private:
double _tol = 1e-12;
int _max_iter = 128;
};
#endif //_BRENT_H_