![]() |
Object-oriented Scientific Computing Library: Version 0.910
|
00001 /* 00002 ------------------------------------------------------------------- 00003 00004 Copyright (C) 2006-2012, Andrew W. Steiner 00005 00006 This file is part of O2scl. 00007 00008 O2scl is free software; you can redistribute it and/or modify 00009 it under the terms of the GNU General Public License as published by 00010 the Free Software Foundation; either version 3 of the License, or 00011 (at your option) any later version. 00012 00013 O2scl is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 GNU General Public License for more details. 00017 00018 You should have received a copy of the GNU General Public License 00019 along with O2scl. If not, see <http://www.gnu.org/licenses/>. 00020 00021 ------------------------------------------------------------------- 00022 */ 00023 /* deriv/deriv.c 00024 * 00025 * Copyright (C) 2004, 2007 Brian Gough 00026 * 00027 * This program is free software; you can redistribute it and/or modify 00028 * it under the terms of the GNU General Public License as published by 00029 * the Free Software Foundation; either version 3 of the License, or (at 00030 * your option) any later version. 00031 * 00032 * This program is distributed in the hope that it will be useful, but 00033 * WITHOUT ANY WARRANTY; without even the implied warranty of 00034 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00035 * General Public License for more details. 00036 * 00037 * You should have received a copy of the GNU General Public License 00038 * along with this program; if not, write to the Free Software 00039 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00040 * 02110-1301, USA. 00041 */ 00042 00043 #ifndef O2SCL_GSL_DERIV_H 00044 #define O2SCL_GSL_DERIV_H 00045 00046 #include <iostream> 00047 #include <cmath> 00048 00049 #include <gsl/gsl_deriv.h> 00050 #include <gsl/gsl_errno.h> 00051 00052 #include <o2scl/deriv.h> 00053 #include <o2scl/funct.h> 00054 00055 #ifndef DOXYGENP 00056 namespace o2scl { 00057 #endif 00058 00059 /** \brief Numerical differentiation (GSL) 00060 00061 This class computes the numerical derivative of a function. The 00062 stepsize \ref h should be specified before use. If similar 00063 functions are being differentiated in succession, the user may 00064 be able to increase the speed of later derivatives by setting 00065 the new stepsize equal to the optimized stepsize from the 00066 previous differentiation, by setting \ref h to \ref h_opt. 00067 00068 The derivative computation will never fail, but the results 00069 will be incorrect for sufficiently difficult functions or if 00070 the step size is not properly chosen. 00071 00072 Some successive derivative computations can be made more 00073 efficient by using the optimized stepsize in \ref 00074 gsl_deriv::h_opt , which is set by the most recent last 00075 derivative computation. 00076 00077 Setting \ref deriv::verbose to a number greater than zero 00078 results in output for each call to \ref central_deriv() which 00079 looks like: 00080 \verbatim 00081 gsl_deriv: 00082 step: 1.000000e-04 00083 abscissas: 4.999500e-01 4.999000e-01 5.000500e-01 5.001000e-01 00084 ordinates: 4.793377e-01 4.793816e-01 4.794694e-01 4.795132e-01 00085 res: 8.775825e-01 trc: 1.462163e-09 rnd: 7.361543e-12 00086 \endverbatim 00087 where the last line contains the result (<tt>res</tt>), the 00088 truncation error (<tt>trc</tt>) and the rounding error 00089 (<tt>rnd</tt>). If \ref deriv::verbose is greater than 1, a 00090 keypress is required after each iteration. 00091 00092 Computing first derivatives requires either 1 or 2 calls to \ref 00093 central_deriv() and thus either 4 or 8 function calls. This 00094 class never calls the error handler. 00095 00096 \note Second and third derivatives are computed by naive nested 00097 applications of the formula for the first derivative. No 00098 uncertainty for these derivatives is provided. 00099 00100 An example demonstrating the usage of this class is given in 00101 <tt>examples/ex_deriv.cpp</tt> and the \ref ex_deriv_sect . 00102 00103 \future Include the forward and backward GSL derivatives? 00104 */ 00105 template<class func_t=funct> class gsl_deriv : public deriv<func_t> { 00106 00107 public: 00108 00109 gsl_deriv() { 00110 h=0.0; 00111 h_opt=0.0; 00112 } 00113 00114 virtual ~gsl_deriv() {} 00115 00116 /** \brief Initial stepsize 00117 00118 This should be specified before a call to calc() or 00119 calc_err(). If it is zero, then \f$ x 10^{-4} \f$ will used, 00120 or if \c x is zero, then \f$ 10^{-4} \f$ will be used. 00121 */ 00122 double h; 00123 00124 /** \brief The last value of the optimized stepsize 00125 00126 This is initialized to zero in the constructor and set by 00127 calc_err() to the most recent value of the optimized stepsize. 00128 */ 00129 double h_opt; 00130 00131 /** \brief Calculate the first derivative of \c func w.r.t. x and 00132 uncertainty 00133 */ 00134 virtual int calc_err(double x, func_t &func, double &dfdx, double &err) { 00135 return calc_base<func_t>(x,func,dfdx,err); 00136 } 00137 00138 /// Return string denoting type ("gsl_deriv") 00139 virtual const char *type() { return "gsl_deriv"; } 00140 00141 #ifndef DOXYGEN_INTERNAL 00142 00143 protected: 00144 00145 /** \brief Internal template version of the derivative function 00146 */ 00147 template<class func2_t> int calc_base(double x, func_t &func, 00148 double &dfdx, double &err) { 00149 double hh; 00150 if (h==0.0) { 00151 if (x==0.0) hh=1.0e-4; 00152 else hh=1.0e-4*x; 00153 } else { 00154 hh=h; 00155 } 00156 00157 double r_0, round, trunc, error; 00158 00159 central_deriv(x,h,r_0,round,trunc,func); 00160 error = round + trunc; 00161 00162 if (round < trunc && (round > 0 && trunc > 0)) { 00163 double r_opt, round_opt, trunc_opt, error_opt; 00164 00165 /* Compute an optimised stepsize to minimize the total error, 00166 using the scaling of the truncation error (O(h^2)) and 00167 rounding error (O(1/h)). */ 00168 00169 h_opt = h * pow (round / (2.0 * trunc), 1.0 / 3.0); 00170 central_deriv(x,h_opt,r_opt,round_opt,trunc_opt,func); 00171 error_opt = round_opt + trunc_opt; 00172 00173 /* Check that the new error is smaller, and that the new derivative 00174 is consistent with the error bounds of the original estimate. */ 00175 00176 if (error_opt < error && fabs (r_opt - r_0) < 4.0 * error) { 00177 r_0 = r_opt; 00178 error = error_opt; 00179 } 00180 } 00181 00182 dfdx=r_0; 00183 err=error; 00184 00185 return 0; 00186 } 00187 00188 /** \brief Internal version of calc_err() for second 00189 and third derivatives 00190 */ 00191 virtual int calc_err_int 00192 (double x, funct &func, double &dfdx, double &err) { 00193 return calc_base<funct>(x,func,dfdx,err); 00194 } 00195 00196 /** \brief Compute derivative using 5-point rule 00197 00198 Compute the derivative using the 5-point rule (x-h, x-h/2, x, 00199 x+h/2, x+h) and the error using the difference between the 00200 5-point and the 3-point rule (x-h,x,x+h). Note that the 00201 central point is not used for either. 00202 00203 This must be a class template because it is used by 00204 both calc_err() and calc_err_int(). 00205 */ 00206 template<class func2_t> 00207 int central_deriv(double x, double hh, double &result, 00208 double &abserr_round, double &abserr_trunc, 00209 func2_t &func) { 00210 00211 double fm1, fp1, fmh, fph; 00212 00213 fm1=func(x-hh); 00214 fp1=func(x+hh); 00215 00216 fmh=func(x-hh/2); 00217 fph=func(x+hh/2); 00218 00219 double r3 = 0.5 * (fp1 - fm1); 00220 double r5 = (4.0 / 3.0) * (fph - fmh) - (1.0 / 3.0) * r3; 00221 00222 double e3 = (fabs (fp1) + fabs (fm1)) * GSL_DBL_EPSILON; 00223 double e5 = 2.0 * (fabs (fph) + fabs (fmh)) * GSL_DBL_EPSILON + e3; 00224 00225 /* The next term is due to finite precision in x+h = O (eps * x) */ 00226 00227 double dy=GSL_MAX(fabs(r3/hh),fabs(r5/hh))*fabs(x/hh)*GSL_DBL_EPSILON; 00228 00229 /* The truncation error in the r5 approximation itself is O(h^4). 00230 However, for safety, we estimate the error from r5-r3, which is 00231 O(h^2). By scaling h we will minimise this estimated error, not 00232 the actual truncation error in r5. 00233 */ 00234 00235 result = r5 / hh; 00236 /* Estimated truncation error O(h^2) */ 00237 abserr_trunc = fabs ((r5 - r3) / hh); 00238 /* Rounding error (cancellations) */ 00239 abserr_round = fabs (e5 / hh) + dy; 00240 00241 if (this->verbose>0) { 00242 std::cout << "gsl_deriv: " << std::endl; 00243 std::cout << "step: " << hh << std::endl; 00244 std::cout << "abscissas: " << x-hh/2 << " " << x-hh << " " 00245 << x+hh/2 << " " << x+hh << std::endl; 00246 std::cout << "ordinates: " << fm1 << " " << fmh << " " << fph << " " 00247 << fp1 << std::endl; 00248 std::cout << "res: " << result << " trc: " << abserr_trunc 00249 << " rnd: " << abserr_round << std::endl; 00250 if (this->verbose>1) { 00251 char ch; 00252 std::cin >> ch; 00253 } 00254 } 00255 00256 return 0; 00257 } 00258 00259 #endif 00260 00261 }; 00262 00263 #ifndef DOXYGENP 00264 } 00265 #endif 00266 00267 #endif 00268 00269 00270
Documentation generated with Doxygen. Provided under the GNU Free Documentation License (see License Information).