GeoModel - GeoModelCore
FunctionNumDeriv.cpp
1 //==========================================================================//
2 // Imported from the QAT library (qat.pitt.edu) by the copyright holder //
3 // Joe Boudreau. Software licensed under the terms and conditions of the //
4 // GNU Lesser Public License v3. //
5 //==========================================================================//
6 
7 #include "GeoGenericFunctions/FunctionNumDeriv.h"
8 #include <stdexcept>
9 #include <cmath> // for pow()
10 
11 namespace GeoGenfun {
12 FUNCTION_OBJECT_IMP(FunctionNumDeriv)
13 
14 FunctionNumDeriv::FunctionNumDeriv(const AbsFunction *arg1, unsigned int index):
15  _arg1(arg1->clone()),
16  _wrtIndex(index)
17 {
18 }
19 
20 FunctionNumDeriv::FunctionNumDeriv(const FunctionNumDeriv & right):
21  AbsFunction(right),
22  _arg1(right._arg1->clone()),
23  _wrtIndex(right._wrtIndex)
24 {
25 }
26 
27 
28 FunctionNumDeriv::~FunctionNumDeriv()
29 {
30  delete _arg1;
31 }
32 
33 unsigned int FunctionNumDeriv::dimensionality() const {
34  return _arg1->dimensionality();
35 }
36 
37 #define ROBUST_DERIVATIVES
38 #ifdef ROBUST_DERIVATIVES
39 
40 double FunctionNumDeriv::f_x (double x) const {
41  return (*_arg1)(x);
42 }
43 
44 
45 double FunctionNumDeriv::f_Arg (double x) const {
46  _xArg [_wrtIndex] = x;
47  return (*_arg1)(_xArg);
48 }
49 
50 
51 double FunctionNumDeriv::operator ()(double x) const
52 {
53  if (_wrtIndex!=0) throw std::runtime_error("FunctionNumDeriv: dimension mismatch");
54  return numericalDerivative ( &FunctionNumDeriv::f_x, x );
55 }
56 
57 double FunctionNumDeriv::operator ()(const Argument & x) const
58 {
59  if (_wrtIndex >= x.dimension()) throw std::runtime_error("FunctionNumDeriv: dimension mismatch");
60  _xArg = x;
61  double xx = x[_wrtIndex];
62  return numericalDerivative ( &FunctionNumDeriv::f_Arg, xx );
63 }
64 
65 
66 double FunctionNumDeriv::numericalDerivative
67  ( double (FunctionNumDeriv::*f)(double)const, double x ) const {
68 
69  const double h0 = 5 * std::pow(2.0, -17);
70 
71  const double maxErrorA = .0012; // These are the largest errors in steps
72  const double maxErrorB = .0000026; // A, B consistent with 8-digit accuracy.
73 
74  const double maxErrorC = .0003; // Largest acceptable validation discrepancy.
75 
76  // This value of gives 8-digit accuracy for 1250 > curvature scale < 1/1250.
77 
78  const int nItersMax = 6;
79  int nIters;
80  double bestError = 1.0E30;
81  double bestAns = 0;
82 
83  const double valFactor = std::pow(2.0, -16);
84 
85  const double w = 5.0/8;
86  const double wi2 = 64.0/25.0;
87  const double wi4 = wi2*wi2;
88 
89  double size = fabs((this->*f)(x));
90  if (size==0) size = std::pow(2.0, -53);
91 
92  const double adjustmentFactor[nItersMax] = {
93  1.0,
94  std::pow(2.0, -17),
95  std::pow(2.0, +17),
96  std::pow(2.0, -34),
97  std::pow(2.0, +34),
98  std::pow(2.0, -51) };
99 
100  for ( nIters = 0; nIters < nItersMax; ++nIters ) {
101 
102  double h = h0 * adjustmentFactor[nIters];
103 
104  // Step A: Three estimates based on h and two smaller values:
105 
106  double A1 = ((this->*f)(x+h) - (this->*f)(x-h))/(2.0*h);
107 // size = max(fabs(A1), size);
108  if (fabs(A1) > size) size = fabs(A1);
109 
110  double hh = w*h;
111  double A2 = ((this->*f)(x+hh) - (this->*f)(x-hh))/(2.0*hh);
112 // size = max(fabs(A2), size);
113  if (fabs(A2) > size) size = fabs(A2);
114 
115  hh *= w;
116  double A3 = ((this->*f)(x+hh) - (this->*f)(x-hh))/(2.0*hh);
117 // size = max(fabs(A3), size);
118  if (fabs(A3) > size) size = fabs(A3);
119 
120  if ( (fabs(A1-A2)/size > maxErrorA) || (fabs(A1-A3)/size > maxErrorA) ) {
121  continue;
122  }
123 
124  // Step B: Two second-order estimates based on h h*w, from A estimates
125 
126  double B1 = ( A2 * wi2 - A1 ) / ( wi2 - 1 );
127  double B2 = ( A3 * wi2 - A2 ) / ( wi2 - 1 );
128  if ( fabs(B1-B2)/size > maxErrorB ) {
129  continue;
130  }
131 
132  // Step C: Third-order estimate, from B estimates:
133 
134  double ans = ( B2 * wi4 - B1 ) / ( wi4 - 1 );
135  double err = fabs ( ans - B1 );
136  if ( err < bestError ) {
137  bestError = err;
138  bestAns = ans;
139  }
140 
141  // Validation estimate based on much smaller h value:
142 
143  hh = h * valFactor;
144  double val = ((this->*f)(x+hh) - (this->*f)(x-hh))/(2.0*hh);
145  if ( fabs(val-ans)/size > maxErrorC ) {
146  continue;
147  }
148 
149  // Having passed both apparent accuracy and validation, we are finished:
150  break;
151  }
152 
153  return bestAns;
154 
155 }
156 #endif // ROBUST_DERIVATIVES
157 
158 
159 
160 #ifdef SIMPLER_DERIVATIVES
161 double FunctionNumDeriv::operator ()(double x) const
162 {
163  if (_wrtIndex !=0) throw std::runtime_error("FunctionNumDeriv: dimension mismatch");
164  const double h=1.0E-6;
165  return ((*_arg1)(x+h) - (*_arg1)(x-h))/(2.0*h);
166 }
167 
168 double FunctionNumDeriv::operator ()(const Argument & x) const
169 {
170  if (_wrtIndex >= x.dimension()) throw std::runtime_error("FunctionNumDeriv: dimension mismatch");
171  const double h=1.0E-6;
172  Argument x1=x, x0=x;
173  x1[_wrtIndex] +=h;
174  x0[_wrtIndex] -=h;
175  return ((*_arg1)(x1) - (*_arg1)(x0))/(2.0*h);
176 }
177 #endif // SIMPLER_DERIVATIVES
178 
179 } // namespace GeoGenfun
Definition: Abs.h:15