00001 /* 00002 ------------------------------------------------------------------- 00003 00004 Copyright (C) 2006, 2007, 2008, 2009, 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 /** 00060 \brief Numerical differentiation (GSL) 00061 00062 This class computes the numerical derivative of a function. The 00063 stepsize \ref h should be specified before use. If similar 00064 functions are being differentiated in succession, the user may 00065 be able to increase the speed of later derivatives by setting 00066 the new stepsize equal to the optimized stepsize from the 00067 previous differentiation, by setting \ref h to \ref h_opt. 00068 00069 The derivative computation will never fail, but the results 00070 will be incorrect for sufficiently difficult functions or if 00071 the step size is not properly chosen. 00072 00073 Some successive derivative computations can be made more efficient 00074 by using the optimized stepsize in \ref gsl_deriv::h_opt , which 00075 is set by the most recent last derivative computation. 00076 00077 \note Second and third derivatives are computed by naive nested 00078 applications of the formula for the first derivative. 00079 No uncertainty for these derivatives is provided. 00080 00081 An example demonstrating the usage of this class is 00082 given in <tt>examples/ex_deriv.cpp</tt> and the \ref ex_deriv_sect . 00083 00084 \future Include the forward and backward GSL derivatives 00085 00086 */ 00087 template<class param_t, class func_t=funct<param_t> > 00088 class gsl_deriv : public deriv<param_t,func_t> { 00089 00090 public: 00091 00092 gsl_deriv() { 00093 h=0.0; 00094 h_opt=0.0; 00095 } 00096 00097 virtual ~gsl_deriv() {} 00098 00099 /** 00100 \brief Initial stepsize 00101 00102 This should be specified before a call to calc() or 00103 calc_err(). If it is zero, then \f$ x 10^{-4} \f$ will used, or 00104 if \c x is zero, then \f$ 10^{-4} \f$ will be used. 00105 */ 00106 double h; 00107 00108 /** 00109 \brief The last value of the optimized stepsize 00110 00111 This is initialized to zero in the constructor and set by 00112 calc_err() to the most recent value of the optimized stepsize. 00113 */ 00114 double h_opt; 00115 00116 /** \brief Calculate the first derivative of \c func w.r.t. x and 00117 uncertainty 00118 */ 00119 virtual int calc_err(double x, param_t &pa, func_t &func, 00120 double &dfdx, double &err) { 00121 00122 double hh; 00123 if (h==0.0) { 00124 if (x==0.0) hh=1.0e-4; 00125 else hh=1.0e-4*x; 00126 } else { 00127 hh=h; 00128 } 00129 00130 double r_0, round, trunc, error; 00131 00132 central_deriv(x,h,r_0,round,trunc,func,pa); 00133 error = round + trunc; 00134 00135 if (round < trunc && (round > 0 && trunc > 0)) { 00136 double r_opt, round_opt, trunc_opt, error_opt; 00137 00138 /* Compute an optimised stepsize to minimize the total error, 00139 using the scaling of the truncation error (O(h^2)) and 00140 rounding error (O(1/h)). */ 00141 00142 h_opt = h * pow (round / (2.0 * trunc), 1.0 / 3.0); 00143 central_deriv(x,h_opt,r_opt,round_opt,trunc_opt,func,pa); 00144 error_opt = round_opt + trunc_opt; 00145 00146 /* Check that the new error is smaller, and that the new derivative 00147 is consistent with the error bounds of the original estimate. */ 00148 00149 if (error_opt < error && fabs (r_opt - r_0) < 4.0 * error) { 00150 r_0 = r_opt; 00151 error = error_opt; 00152 } 00153 } 00154 00155 dfdx=r_0; 00156 err=error; 00157 00158 return 0; 00159 } 00160 00161 /// Return string denoting type ("gsl_deriv") 00162 virtual const char *type() { return "gsl_deriv"; } 00163 00164 #ifndef DOXYGEN_INTERNAL 00165 00166 protected: 00167 00168 /** \brief Internal version of calc_err() for second 00169 and third derivatives 00170 */ 00171 virtual int calc_err_int 00172 (double x, typename deriv<param_t,func_t>::dpars &pa, 00173 funct<typename deriv<param_t,func_t>::dpars> &func, 00174 double &dfdx, double &err) { 00175 00176 double hh; 00177 if (h==0.0) { 00178 if (x==0.0) hh=1.0e-4; 00179 else hh=1.0e-4*x; 00180 } else { 00181 hh=h; 00182 } 00183 00184 double r_0, round, trunc, error; 00185 00186 central_deriv(x,h,r_0,round,trunc,func,pa); 00187 error = round + trunc; 00188 00189 if (round < trunc && (round > 0 && trunc > 0)) { 00190 double r_opt, round_opt, trunc_opt, error_opt; 00191 00192 /* Compute an optimised stepsize to minimize the total error, 00193 using the scaling of the truncation error (O(h^2)) and 00194 rounding error (O(1/h)). 00195 */ 00196 00197 // This value 'hopt' is different from 'h_opt' above so that 00198 // the two derivative functions don't get confused about which 00199 // 'hopt' they are using. 00200 double hopt = h * pow (round / (2.0 * trunc), 1.0 / 3.0); 00201 central_deriv(x,hopt,r_opt,round_opt,trunc_opt,func,pa); 00202 error_opt = round_opt + trunc_opt; 00203 00204 /* Check that the new error is smaller, and that the new derivative 00205 is consistent with the error bounds of the original estimate. 00206 */ 00207 00208 if (error_opt < error && fabs (r_opt - r_0) < 4.0 * error) { 00209 r_0 = r_opt; 00210 error = error_opt; 00211 } 00212 } 00213 00214 dfdx=r_0; 00215 err=error; 00216 00217 return 0; 00218 } 00219 00220 /** 00221 \brief Compute derivative using 5-point rule 00222 00223 Compute the derivative using the 5-point rule (x-h, x-h/2, x, 00224 x+h/2, x+h) and the error using the difference between the 00225 5-point and the 3-point rule (x-h,x,x+h). Note that the 00226 central point is not used for either. 00227 00228 This must be a class template because it is used by 00229 both calc_err() and calc_err_int(). 00230 */ 00231 template<class func2_t, class param2_t> 00232 int central_deriv(double x, double hh, double &result, 00233 double &abserr_round, double &abserr_trunc, 00234 func2_t &func, param2_t &pa) { 00235 00236 double fm1, fp1, fmh, fph; 00237 00238 func(x-hh,fm1,pa); 00239 func(x+hh,fp1,pa); 00240 00241 func(x-hh/2,fmh,pa); 00242 func(x+hh/2,fph,pa); 00243 00244 double r3 = 0.5 * (fp1 - fm1); 00245 double r5 = (4.0 / 3.0) * (fph - fmh) - (1.0 / 3.0) * r3; 00246 00247 double e3 = (fabs (fp1) + fabs (fm1)) * GSL_DBL_EPSILON; 00248 double e5 = 2.0 * (fabs (fph) + fabs (fmh)) * GSL_DBL_EPSILON + e3; 00249 00250 /* The next term is due to finite precision in x+h = O (eps * x) */ 00251 00252 double dy=GSL_MAX(fabs(r3/hh),fabs(r5/hh))*fabs(x/hh)*GSL_DBL_EPSILON; 00253 00254 /* The truncation error in the r5 approximation itself is O(h^4). 00255 However, for safety, we estimate the error from r5-r3, which is 00256 O(h^2). By scaling h we will minimise this estimated error, not 00257 the actual truncation error in r5. 00258 */ 00259 00260 result = r5 / hh; 00261 /* Estimated truncation error O(h^2) */ 00262 abserr_trunc = fabs ((r5 - r3) / hh); 00263 /* Rounding error (cancellations) */ 00264 abserr_round = fabs (e5 / hh) + dy; 00265 00266 if (this->verbose>0) { 00267 std::cout << "gsl_deriv: " << std::endl; 00268 std::cout << "step: " << hh << std::endl; 00269 std::cout << "abcissas: " << x-hh/2 << " " << x-hh << " " 00270 << x+hh/2 << " " << x+hh << std::endl; 00271 std::cout << "ordinates: " << fm1 << " " << fmh << " " << fph << " " 00272 << fp1 << std::endl; 00273 std::cout << "res: " << result << " trc: " << abserr_trunc 00274 << " rnd: " << abserr_round << std::endl; 00275 } 00276 00277 return 0; 00278 } 00279 00280 #endif 00281 00282 }; 00283 00284 #ifndef DOXYGENP 00285 } 00286 #endif 00287 00288 #endif 00289 00290 00291
Documentation generated with Doxygen and provided under the GNU Free Documentation License. See License Information for details.
Project hosting provided by
,
O2scl Sourceforge Project Page