Isis 3 Programmer Reference
FunctionTools.h
Go to the documentation of this file.
1 
24 #ifndef FunctionTools_h
25 #define FunctionTools_h
26 
27 #include <math.h>
28 #include <cfloat>
29 
30 #include <QList>
31 #include <QString>
32 
33 #include "Constants.h"
34 #include "IException.h"
35 #include "IString.h"
36 
37 namespace Isis {
53  class FunctionTools {
54  public:
55 
56 
64  static QList <double> realLinearRoots(double coeffLinearTerm, double coeffConstTerm) {
65  double m=coeffLinearTerm, b=coeffConstTerm;
66 
67  QList<double> roots;
68 
69  //if the slope is zero there are either 0 or infinite roots
70  // for the present I see no need to handle the infinite roots situation more elegantly...
71  if (m==0.0)
72  return roots;
73 
74  roots << -b/m;
75 
76  return roots;
77  }
78 
87  static QList <double> realQuadraticRoots(double coeffQuadTerm, double coeffLinearTerm,
88  double coeffConstTerm) {
89  double A=coeffQuadTerm, B=coeffLinearTerm, C=coeffConstTerm;
90  double disc,q,temp; //helpers on the way to a solution
91 
92  if (A==0.0)
93  return realLinearRoots(coeffLinearTerm, coeffConstTerm);
94 
95  QList<double> roots;
96 
97  disc = B*B-4*A*C;
98  if (disc < 0) return roots; //no solution, return empty set
99  q = -0.5*(B + (B < 0 ? -1.0 : 1.0)*sqrt(disc));
100 
101  if (q == 0) roots << q/A;
102  else {
103  roots << q/A;
104  //after the first root make sure there are no duplicates
105  temp = C/q;
106  if (!roots.contains(temp)) roots << temp;
107  }
108  return roots;
109  }
110 
111 
112 
122  static QList <double> realCubicRoots(double coeffCubicTerm, double coeffQuadTerm,
123  double coeffLinearTerm, double coeffConstTerm) {
124  double a=coeffQuadTerm, b=coeffLinearTerm, c=coeffConstTerm;
125  double Q,R; //helpers on the way to a solution
126 
127  //first verify this is really a cubic
128  if (coeffCubicTerm == 0.0)
129  return realQuadraticRoots(coeffQuadTerm,coeffLinearTerm,coeffConstTerm);
130 
131 
132  //algorithm wants the leading coefficient to be 1.0
133  if ( coeffCubicTerm != 1.0) {
134  a /= coeffCubicTerm;
135  b /= coeffCubicTerm;
136  c /= coeffCubicTerm;
137  }
138 
139  QList<double> roots;
140 
141  Q = (a*a - 3.0*b) / 9.0;
142  R = (2*a*a*a - 9.0*a*b + 27.0*c) / 54.0;
143  //cout << "Q R: " << Q << " " << R << "\n";
144 
145  if ( a == 0.0 && b == 0.0 ) { //one simple root
146  roots << (c < 0 ? 1.0 : -1.0)*pow(fabs(c),1.0/3.0);
147  }
148  else if (R*R <= Q*Q*Q) { //there are three roots (one of them can be a double root)
149  double theta = acos(R/sqrt(Q*Q*Q)),temp;
150  //cout << "three roots, theta: " << theta << "\n";
151  Q = -2.0*sqrt(Q); //just done to save some FLOPs
152  roots << Q*cos(theta/3.0) - a /3.0;
153  //after the first root make sure there are no duplicates
154  temp = Q*cos((theta + TWOPI)/3.0) - a / 3.0;
155  if (!roots.contains(temp)) roots << temp;
156  temp = Q*cos((theta - TWOPI)/3.0) - a / 3.0;
157  if (!roots.contains(temp)) roots << temp;
158  }
159  else { //there is a single real root
160  double A, B;
161  A = (R < 0 ? 1.0 : -1.0 ) * pow(fabs(R) + sqrt(R*R - Q*Q*Q),1.0/3.0);
162  B = A == 0.0 ? 0.0 : Q / A;
163 
164  roots << (A + B) - a / 3.0;
165  }
166 
167  return roots;
168  }
169 
191  template <typename Functor>
192  static bool brentsRootFinder(Functor &func, const QList<double> pt1, const QList<double> pt2,
193  double tol, int maxIter, double &root) {
194  double a = pt1[0], b = pt2[0], c, d = 0;
195  double fa = pt1[1], fb = pt2[1], fc;
196  double p, q, r, s, t, tol1, bnew, fbnew, temp, deltaI, deltaF;
197 
198  // offset used for improved numerical stability
199  double offset = (a + b) / 2.0;
200  a -= offset;
201  b -= offset;
202 
203  // check to see if the points bracket a root(s), if the signs are equal they don't
204  if ( (fa > 0) - (fa < 0) == (fb > 0) - (fb < 0) ) {
205  QString msg = "The function evaluations of two bounding points passed to Brents Method "
206  "have the same sign. Therefore, they don't necessary bound a root. No "
207  "root finding will be attempted.\n";
209  return false;
210  }
211 
212  bool mflag = true;
213 
214  if (fabs(fa) < fabs(fb)) {
215  // if a is a better guess for the root than b, switch them
216  // b is always the current best guess for the root
217  temp = a;
218  a = b;
219  b = temp;
220  temp = fa;
221  fa = fb;
222  fb = temp;
223  }
224 
225  c = a;
226  fc = fa;
227 
228  for (int iter = 0; iter < maxIter; iter++) {
229  tol1 = DBL_EPSILON * 2.0 * fabs(b); // numerical tolerance
230  if (a != c && b != c) {
231  // inverse quadratic interpolation
232  r = fb / fc;
233  s = fb / fa;
234  t = fa / fc;
235  p = s * (t * (r - t) * (c - b) - (1 - r) * (b - a));
236  q = (t - 1) * (r - 1) * (s - 1);
237  bnew = b + p / q;
238  }
239  else {
240  // secant rule
241  bnew = b - fb * (b - a) / (fb - fa);
242  }
243 
244  // five tests follow to determine if the iterpolation methods are working better than
245  // bisection. p and q are setup as the bounds we want the new root guess to fall within.
246  // this enforces that the new root guess be withing the 3/4 of the interval closest to
247  // b, the current best guess.
248  temp = (3 * a + b) / 4.0;
249  p = temp < b ? temp : b;
250  q = b > temp ? b : temp;
251  deltaI = fabs(b - bnew); // magnitude of the interpolated correctio
252  if ( // if the root isn't within the 3/4 of the interval closest to b (the current best guess)
253  (bnew < p || bnew > q ) ||
254  // or if the last iteration was a bisection
255  // and the new correction is greater magnitude than half the magnitude of last
256  // correction, ie it's doing less to narrow the root than a bisection would
257  (mflag && deltaI >= fabs(b - c) / 2.0) ||
258  // or if the last iteration was an interpolation
259  // and the new correction magnitude is greater than
260  // the half the magnitude of the correction from two iterations ago,
261  // i.e. it's not converging faster than bisection
262  (!mflag && deltaI >= fabs(c - d) / 2.0) ||
263  // or if the last iteration was a bisection
264  // and the last correction was less than the numerical tolerance
265  // ie we are reaching the limits of our numerical
266  // ability to find a better root so lets do bisection, it's numerical safer
267  (mflag && fabs(b - c) < tol1) ||
268  // or it the last iteration was an interpolation
269  // and the correction from two iteration ago was less than the current
270  // numerical tolerance, ie we are reaching the limits of our numerical
271  // ability to find a better root so lets do bisection, it's numerical safer
272  (!mflag && fabs(c - d) < tol1) ) {
273 
274  // bisection method
275  bnew = (a + b) / 2.0;
276  mflag = true;
277  }
278  else {
279  mflag = false;
280  }
281  try {
282  fbnew = func(bnew + offset);
283  } catch (IException &e) {
284  QString msg = "Function evaluation failed at:" + toString(bnew) +
285  ". Function must be continuous and defined for the entire interval "
286  "inorder to gaurentee brentsRootFinder will work.";
288  }
289  d = c; // thus d always equals the best guess from two iterations ago
290  c = b; // thus c always equals the best guess from the previous iteration
291  fc = fb;
292 
293  if ( (fa > 0) - (fa < 0) == (fbnew > 0) - (fbnew < 0) ) {
294  // if b and bnew bracket the root
295  deltaF = fabs(a - bnew); // the Final magnitude of the correction
296  a = bnew;
297  fa = fbnew;
298  }
299  else {
300  // a and bnew bracket the root
301  deltaF = fabs(b - bnew); // the Final magnitude of the correction
302  b = bnew;
303  fb = fbnew;
304  }
305 
306  if (fabs(fa) < fabs(fb)) {
307  // if a is a better guess for the root than b, switch them
308  temp = a;
309  a = b;
310  b = temp;
311  temp = fa;
312  fa = fb;
313  fb = temp;
314  }
315  if (fabs(fb) < tol) {
316  // if the tolerance is meet
317  root = b + offset;
318  return true;
319  }
320  else if ( deltaF < tol1 && fabs(b) < 100.0 * tol) {
321  // we've reach the numerical limit to how well the root can be defined, and the function
322  // is at least approaching zero
323  // This was added specifically for the apollo pan camera, the camera classes (and maybe
324  // NAIF as well can not actually converge to zero for the extreme edges of some pan
325  // images (partial derivates WRT to line approach infinity). They can get close
326  // 'enough' however
327  root = b + offset;
328  return true;
329  }
330  else if (deltaF < tol1) {
331  // we've reached the limit of the numerical ability to refine the root and the function
332  // is not near zero. This is a clasically ill defined root (nearly vertical function)
333  // "This is not the [root] you're looking for"
334  return false;
335  }
336  }
337  // maximum number of iteration exceeded
338  return false;
339  } // end brentsRootFinder
340 
341 
342  private:
348  FunctionTools();
349 
354  ~FunctionTools();
355 
356  }; // End FuntionTools class definition
357 
358 }; // End namespace Isis
359 
360 #endif
static QList< double > realCubicRoots(double coeffCubicTerm, double coeffQuadTerm, double coeffLinearTerm, double coeffConstTerm)
Find the real roots of a cubic (1, 2, or 3) (see numerical recipies 3rd edtion page 227) Form: coeffC...
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition: IString.cpp:226
This error is for when a programmer made an API call that was illegal.
Definition: IException.h:162
~FunctionTools()
destructor
const double TWOPI
Two * PI, a complete revolution.
Definition: Constants.h:58
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:40
static QList< double > realQuadraticRoots(double coeffQuadTerm, double coeffLinearTerm, double coeffConstTerm)
The correct way to find the real roots of a quadratic (0, 1, or 2) (according to numerical recipies 3...
Definition: FunctionTools.h:87
static bool brentsRootFinder(Functor &func, const QList< double > pt1, const QList< double > pt2, double tol, int maxIter, double &root)
Van Wijngaarden-Dekker-Brent Method for root finding on a discreetly defined function meaning that we...
FunctionTools()
Constructor.
A collection of tools for mathmatical function root finding, maximization, etc (eventually) This clas...
Definition: FunctionTools.h:53
static QList< double > realLinearRoots(double coeffLinearTerm, double coeffConstTerm)
Find the real roots (0 or 1) of a linear equation Form: coeffLinearTerm*X + coeffConstTerm = 0...
Definition: FunctionTools.h:64
Isis exception class.
Definition: IException.h:107
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31