GteRootsBrentsMethod.h
Go to the documentation of this file.
1 // David Eberly, Geometric Tools, Redmond WA 98052
2 // Copyright (c) 1998-2017
3 // Distributed under the Boost Software License, Version 1.0.
4 // http://www.boost.org/LICENSE_1_0.txt
5 // http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
6 // File Version: 3.0.0 (2016/06/19)
7 
8 #pragma once
9 
10 #include <GTEngineDEF.h>
11 #include <cmath>
12 #include <functional>
13 
14 // This is an implementation of Brent's Method for computing a root of a
15 // function on an interval [t0,t1] for which F(t0)*F(t1) < 0. The method
16 // uses inverse quadratic interpolation to generate a root estimate but
17 // falls back to inverse linear interpolation (secant method) if
18 // necessary. Moreover, based on previous iterates, the method will fall
19 // back to bisection when it appears the interpolated estimate is not of
20 // sufficient quality.
21 //
22 // maxIterations:
23 // The maximum number of iterations used to locate a root. This
24 // should be positive.
25 // negFTolerance, posFTolerance:
26 // The root estimate t is accepted when the function value F(t)
27 // satisfies negFTolerance <= F(t) <= posFTolerance. The values
28 // must satisfy: negFTolerance <= 0, posFTolerance >= 0.
29 // stepTTolerance:
30 // Brent's Method requires additional tests before an interpolated
31 // t-value is accepted as the next root estimate. One of these
32 // tests compares the difference of consecutive iterates and
33 // requires it to be larger than a user-specified t-tolerance (to
34 // ensure progress is made). This parameter is that tolerance
35 // and should be nonnegative.
36 // convTTolerance:
37 // The root search is allowed to terminate when the current
38 // subinterval [tsub0,tsub1] is sufficiently small, say,
39 // |tsub1 - tsub0| <= tolerance. This parameter is that tolerance
40 // and should be nonnegative.
41 
42 namespace gte
43 {
44 
45 template <typename Real>
47 {
48 public:
49  // It is necessary that F(t0)*F(t1) <= 0, in which case the function
50  // returns 'true' and the 'root' is valid; otherwise, the function returns
51  // 'false' and 'root' is invalid (do not use it). When F(t0)*F(t1) > 0,
52  // the interval may very well contain a root but we cannot know that. The
53  // function also returns 'false' if t0 >= t1.
54 
55  static bool Find(std::function<Real(Real)> const& F, Real t0, Real t1,
56  unsigned int maxIterations, Real negFTolerance, Real posFTolerance,
57  Real stepTTolerance, Real convTTolerance, Real& root);
58 };
59 
60 
61 template <typename Real>
62 bool RootsBrentsMethod<Real>::Find(std::function<Real(Real)> const& F,
63  Real t0, Real t1, unsigned int maxIterations, Real negFTolerance,
64  Real posFTolerance, Real stepTTolerance, Real convTTolerance, Real& root)
65 {
66  // Parameter validation.
67  if (t1 <= t0
68  || maxIterations == 0
69  || negFTolerance > (Real)0
70  || posFTolerance < (Real)0
71  || stepTTolerance < (Real)0
72  || convTTolerance < (Real)0)
73  {
74  // The input is invalid.
75  return false;
76  }
77 
78  Real f0 = F(t0);
79  if (negFTolerance <= f0 && f0 <= posFTolerance)
80  {
81  // This endpoint is an approximate root that satisfies the function
82  // tolerance.
83  root = t0;
84  return true;
85  }
86 
87  Real f1 = F(t1);
88  if (negFTolerance <= f1 && f1 <= posFTolerance)
89  {
90  // This endpoint is an approximate root that satisfies the function
91  // tolerance.
92  root = t1;
93  return true;
94  }
95 
96  if (f0*f1 > (Real)0)
97  {
98  // The input interval must bound a root.
99  return false;
100  }
101 
102  if (std::abs(f0) < std::abs(f1))
103  {
104  // Swap t0 and t1 so that |F(t1)| <= |F(t0)|. The number t1 is
105  // considered to be the best estimate of the root.
106  std::swap(t0, t1);
107  std::swap(f0, f1);
108  }
109 
110  // Initialize values for the root search.
111  Real t2 = t0, t3 = t0, f2 = f0;
112  bool prevBisected = true;
113 
114  // The root search.
115  for (unsigned int i = 0; i < maxIterations; ++i)
116  {
117  Real fDiff01 = f0 - f1, fDiff02 = f0 - f2, fDiff12 = f1 - f2;
118  Real invFDiff01 = ((Real)1) / fDiff01;
119  Real s;
120  if (fDiff02 != (Real)0 && fDiff12 != (Real)0)
121  {
122  // Use inverse quadratic interpolation.
123  Real infFDiff02 = ((Real)1) / fDiff02;
124  Real invFDiff12 = ((Real)1) / fDiff12;
125  s =
126  t0 * f1 * f2 * invFDiff01 * infFDiff02 -
127  t1 * f0 * f2 * invFDiff01 * invFDiff12 +
128  t2 * f0 * f1 * infFDiff02 * invFDiff12;
129  }
130  else
131  {
132  // Use inverse linear interpolation (secant method).
133  s = (t1 * f0 - t0 * f1) * invFDiff01;
134  }
135 
136  // Compute values need in the accept-or-reject tests.
137  Real tDiffSAvr = s - ((Real)0.75) * t0 - ((Real)0.25) * t1;
138  Real tDiffS1 = s - t1;
139  Real absTDiffS1 = std::abs(tDiffS1);
140  Real absTDiff12 = std::abs(t1 - t2);
141  Real absTDiff23 = std::abs(t2 - t3);
142 
143  bool currBisected = false;
144  if (tDiffSAvr * tDiffS1 > (Real)0)
145  {
146  // The value s is not between 0.75*t0 + 0.25*t1 and t1. NOTE:
147  // The algorithm sometimes has t0 < t1 but sometimes t1 < t0, so
148  // the between-ness test does not use simple comparisons.
149  currBisected = true;
150  }
151  else if (prevBisected)
152  {
153  // The first of Brent's tests to determine whether to accept the
154  // interpolated s-value.
155  currBisected =
156  (absTDiffS1 >= ((Real)0.5) * absTDiff12) ||
157  (absTDiff12 <= stepTTolerance);
158  }
159  else
160  {
161  // The second of Brent's tests to determine whether to accept the
162  // interpolated s-value.
163  currBisected =
164  (absTDiffS1 >= ((Real)0.5) * absTDiff23) ||
165  (absTDiff23 <= stepTTolerance);
166  }
167 
168  if (currBisected)
169  {
170  // One of the additional tests failed, so reject the interpolated
171  // s-value and use bisection instead.
172  s = ((Real)0.5) * (t0 + t1);
173  if (s == t0 || s == t1)
174  {
175  // The numbers t0 and t1 are consecutive floating-point
176  // numbers.
177  root = s;
178  return true;
179  }
180  prevBisected = true;
181  }
182  else
183  {
184  prevBisected = false;
185  }
186 
187  // Evaluate the function at the new estimate and test for convergence.
188  Real fs = F(s);
189  if (negFTolerance <= fs && fs <= posFTolerance)
190  {
191  root = s;
192  return true;
193  }
194 
195  // Update the subinterval to include the new estimate as an endpoint.
196  t3 = t2;
197  t2 = t1;
198  f2 = f1;
199  if (f0 * fs < (Real)0)
200  {
201  t1 = s;
202  f1 = fs;
203  }
204  else
205  {
206  t0 = s;
207  f0 = fs;
208  }
209 
210  // Allow the algorithm to terminate when the subinterval is
211  // sufficiently small.
212  if (std::abs(t1 - t0) <= convTTolerance)
213  {
214  root = t1;
215  return true;
216  }
217 
218  // A loop invariant is that t1 is the root estimate, F(t0)*F(t1) < 0,
219  // and |F(t1)| <= |F(t0)|.
220  if (std::abs(f0) < std::abs(f1))
221  {
222  std::swap(t0, t1);
223  std::swap(f0, f1);
224  }
225  }
226 
227  // Failed to converge in the specified number of iterations.
228  return false;
229 }
230 
231 
232 }
gte::BSNumber< UIntegerType > abs(gte::BSNumber< UIntegerType > const &number)
Definition: GteBSNumber.h:966
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t0
Definition: glext.h:9013
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t1
Definition: glext.h:9013
static bool Find(std::function< Real(Real)> const &F, Real t0, Real t1, unsigned int maxIterations, Real negFTolerance, Real posFTolerance, Real stepTTolerance, Real convTTolerance, Real &root)
GLdouble s
Definition: glext.h:231


geometric_tools_engine
Author(s): Yijiang Huang
autogenerated on Thu Jul 18 2019 04:00:01