Object-oriented Scientific Computing Library: Version 0.910
gsl_deriv.h
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 
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Documentation generated with Doxygen. Provided under the GNU Free Documentation License (see License Information).

Get Object-oriented Scientific Computing
Lib at SourceForge.net. Fast, secure and Free Open Source software
downloads.