USGS

Isis 3.0 Object Programmers' Reference

Home

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,
193  const QList<double> pt2, double tol, int maxIter, double &root) {
194  double a=pt1[0], b=pt2[0], c, d=0, fa = pt1[1], fb = pt2[1], fc, p, q, r, s, t,tol1, bnew, fbnew, temp, deltaI,deltaF;
195 
196  //offset used for improved numerical stability
197  double offset = (a + b) / 2.0;
198  a -= offset;
199  b -= offset;
200 
201  //check to see if the points bracket a root(s), if the signs are equal they don't
202  if ( (fa > 0) - (fa < 0) == (fb > 0) - (fb < 0) ) {
203  QString msg = "The function evaluations of two bounding points passed to Brents Method "
204  "have the same sign. Therefore, they don't necessary bound a root. No "
205  "root finding will be attempted.\n";
207  return false;
208  }
209 
210  bool mflag=true;
211 
212  if (fabs(fa) < fabs(fb)) {
213  //if a is a better guess for the root than b, switch them
214  //b is always the current best guess for the root
215  temp = a;
216  a = b;
217  b = temp;
218  temp = fa;
219  fa = fb;
220  fb = temp;
221  }
222 
223  c=a;
224  fc=fa;
225 
226  for (int iter=0;iter<maxIter; iter++) {
227  tol1 = DBL_EPSILON*2.0*fabs(b); //numerical tolerance
228  if (a != c && b != c) {
229  //inverse quadratic interpolation
230  r = fb/fc;
231  s = fb/fa;
232  t = fa/fc;
233  p = s*(t*(r-t)*(c-b)-(1-r)*(b-a));
234  q = (t-1)*(r-1)*(s-1);
235  bnew = b + p/q;
236  }
237  else {
238  //secant rule
239  bnew = b - fb * (b - a) / (fb - fa);
240  }
241 
242  //five tests follow to determine if the iterpolation methods are working better than
243  //bisection. p and q are setup as the bounds we want the new root guess to fall within.
244  //this enforces that the new root guess be withing the 3/4 of the interval closest to
245  //b, the current best guess.
246  temp = (3*a+b)/4.0;
247  p = temp < b ? temp : b;
248  q = b > temp ? b : temp;
249  deltaI = fabs(b - bnew); //magnitude of the interpolated correctio
250  if ( //if the root isn't within the 3/4 of the interval closest to b (the current best guess)
251  (bnew < p || bnew > q ) ||
252  //or if the last iteration was a bisection
253  //and the new correction is greater magnitude than half the magnitude of last
254  //correction, ie it's doing less to narrow the root than a bisection would
255  (mflag && deltaI >= fabs(b-c)/2.0) ||
256  //or if the last iteration was an interpolation
257  //and the new correction magnitude is greater than
258  //the half the magnitude of the correction from two iterations ago,
259  //i.e. it's not converging faster than bisection
260  (!mflag && deltaI >= fabs(c-d)/2.0) ||
261  //or if the last iteration was a bisection
262  //and the last correction was less than the numerical tolerance
263  //ie we are reaching the limits of our numerical
264  //ability to find a better root so lets do bisection, it's numerical safer
265  (mflag && fabs(b-c) < tol1) ||
266  //or it the last iteration was an interpolation
267  //and the correction from two iteration ago was less than the current
268  //numerical tolerance, ie we are reaching the limits of our numerical
269  //ability to find a better root so lets do bisection, it's numerical safer
270  (!mflag && fabs(c-d) < tol1) ) {
271 
272  //bisection method
273  bnew = (a + b)/2.0;
274  mflag=true;
275  }
276  else {
277  mflag=false;
278  }
279  try {
280  fbnew = func(bnew + offset);
281  } catch (IException &e) {
282  QString msg = "Function evaluation failed at:" + toString(bnew) +
283  ". Function must be continuous and defined for the entire interval "
284  "inorder to gaurentee brentsRootFinder will work.";
286  }
287  d = c; //thus d always equals the best guess from two iterations ago
288  c = b; //thus c always equals the best giess from the previous iteration
289  fc = fb;
290 
291  if ( (fa > 0) - (fa < 0) == (fbnew > 0) - (fbnew < 0) ) {
292  //if b and bnew bracket the root
293  deltaF = fabs(a - bnew); //the Final magnitude of the correction
294  a = bnew;
295  fa = fbnew;
296  }
297  else {
298  //a and bnew bracket the root
299  deltaF = fabs(b - bnew); //the Final magnitude of the correction
300  b = bnew;
301  fb = fbnew;
302  }
303 
304  if (fabs(fa) < fabs(fb)) {
305  //if a is a better guess for the root than b, switch them
306  temp = a;
307  a = b;
308  b = temp;
309  temp = fa;
310  fa = fb;
311  fb = temp;
312  }
313  if (fabs(fb) < tol) {
314  //if the tolerance is meet
315  root = b + offset;
316  return true;
317  }
318  else if ( deltaF < tol1 && fabs(b) < 100.0*tol) {
319  //we've reach the numerical limit to how well the root can be defined, and the function
320  // is at least approaching zero
321  //This was added specifically for the apollo pan camera, the camera classes (and maybe
322  // NAIF as well can not actually converge to zero for the extreme edges of some pan
323  // images (partial derivates WRT to line approach infinity). They can get close
324  // 'enough' however
325  root = b + offset;
326  return true;
327  }
328  else if (deltaF < tol1) {
329  //we've reached the limit of the numerical ability to refine the root and the function
330  // is not near zero. This is a clasically ill defined root (nearly vertical function)
331  // "This is not the [root] you're looking for"
332  return false;
333  }
334  }
335  //maximum number of iteration exceeded
336  return false;
337  } //end brentsRootFinder
338 
339 
340  private:
345  FunctionTools(); //no definition to be supplied
346 
351  ~FunctionTools(); //no definition to be supplied
352  }; //end FuntionTools class definition
353 
354 }; //End namespace Isis
355 
356 #endif