Object-oriented Scientific Computing Library: Version 0.910
cern_adapt.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 #ifndef O2SCL_CERN_ADAPT_H
00024 #define O2SCL_CERN_ADAPT_H
00025 
00026 #include <o2scl/inte.h>
00027 #include <o2scl/cern_gauss56.h>
00028 #include <o2scl/string_conv.h>
00029  
00030 #ifndef DOXYGENP
00031 namespace o2scl {
00032 #endif
00033 
00034   /** \brief Adaptive integration (CERNLIB)
00035     
00036       Uses a base integration object (default is \ref cern_gauss56) to
00037       perform adaptive integration by automatically subdividing the
00038       integration interval. At each step, the interval with the
00039       largest absolute uncertainty is divided in half. The routine
00040       succeeds if the absolute tolerance is less than \ref tol_abs or
00041       if the relative tolerance is less than \ref tol_rel, i.e.
00042       \f[
00043       \mathrm{err}\leq\mathrm{tol\_abs}~\mathrm{or}~
00044       \mathrm{err}\leq\mathrm{tol\_rel}\cdot|I|
00045       \f]
00046       where \f$ I \f$ is the current estimate for the integral and \f$
00047       \mathrm{err} \f$ is the current estimate for the uncertainty. If
00048       the number of subdivisions exceeds the template parameter \c
00049       nsub, the error handler is called, since the integration may not
00050       have been successful. The number of subdivisions used in the
00051       last integration can be obtained from get_nsubdivisions().
00052 
00053       The template parameter \c nsub, is the maximum number of
00054       subdivisions. It is automatically set to 100 in the original
00055       CERNLIB routine, and defaults to 100 here. The default base
00056       integration object is of type \ref cern_gauss56. This is the
00057       CERNLIB default, but can be modified by calling set_inte().
00058       
00059       This class is based on the CERNLIB routines RADAPT and
00060       DADAPT which are documented at
00061       http://wwwasdoc.web.cern.ch/wwwasdoc/shortwrupsdir/d102/top.html
00062       
00063       \future 
00064       - Allow user to set the initial subdivisions?
00065       - It might be interesting to directly compare the performance
00066       of this class to \ref gsl_inte_qag .
00067       - There is a fixme entry in the code which could be resolved.
00068       - Output the point where most subdividing was required?
00069   */
00070   template<class func_t=funct, size_t nsub=100> 
00071     class cern_adapt : public inte<func_t> {
00072 
00073 #ifndef DOXYGEN_INTERNAL
00074 
00075       protected:
00076 
00077       /// Lower end of subdivision
00078       double xlo[nsub];
00079 
00080       /// High end of subdivision
00081       double xhi[nsub];
00082 
00083       /// Value of integral for subdivision
00084       double tval[nsub];
00085 
00086       /// Squared error for subdivision
00087       double ters[nsub];
00088       
00089       /// Previous number of subdivisions
00090       int prev_subdiv;
00091 
00092       /// The base integration object
00093       inte<func_t> *it;
00094       
00095 #endif
00096       
00097       public:
00098   
00099       cern_adapt() {
00100         nsubdiv=1;
00101         prev_subdiv=0;
00102 
00103         it=&def_inte;
00104       }
00105 
00106       /// \name Basic usage
00107       //@{
00108       /** \brief Integrate function \c func from \c a to \c b
00109           giving result \c res and error \c err
00110       */
00111       virtual int integ_err(func_t &func, double a, double b,
00112                             double &res, double &err) {
00113         
00114         double tvals=0.0, terss, xlob, xhib, yhib=0.0, te, root=0.0;
00115         int i, nsubdivd;
00116 
00117         this->last_conv=0;
00118   
00119         if (nsubdiv==0) {
00120           if (prev_subdiv==0) {
00121             // If the previous binning was requested, but
00122             // there is no previous binning stored, then
00123             // just shift to automatic binning
00124             nsubdivd=1;
00125           } else {
00126             tvals=0.0;
00127             terss=0.0;
00128             for(i=0;i<prev_subdiv;i++) {
00129               it->integ_err(func,xlo[i],xhi[i],tval[i],te);
00130               ters[i]=te*te;
00131               tvals+=tval[i];
00132               terss+=ters[i];
00133             }
00134             err=sqrt(2.0*terss);
00135             res=tvals;
00136             return 0;
00137           }
00138         }
00139   
00140         // Ensure we're not asking for too many divisions
00141         if (nsub<nsubdiv) {
00142           nsubdivd=nsub;
00143         } else {
00144           nsubdivd=nsubdiv;
00145         }
00146 
00147         // Compute the initial set of intervals and integral values
00148         xhib=a;
00149         double bin=(b-a)/((double)nsubdivd);
00150         for(i=0;i<nsubdivd;i++) {
00151           xlo[i]=xhib;
00152           xlob=xlo[i];
00153           xhi[i]=xhib+bin;
00154           if (i==nsubdivd-1) xhi[i]=b;
00155           xhib=xhi[i];
00156           it->integ_err(func,xlob,xhib,tval[i],te);
00157           ters[i]=te*te;
00158         }
00159         prev_subdiv=nsubdivd;
00160 
00161         for(size_t iter=1;iter<=nsub;iter++) {
00162 
00163           // Compute the total value of the integrand
00164           // and the squared uncertainty
00165           tvals=tval[0];
00166           terss=ters[0];
00167           for(i=1;i<prev_subdiv;i++) {
00168             tvals+=tval[i];
00169             terss+=ters[i];
00170           }
00171           
00172           // Output iteration information
00173           if (this->verbose>0) {
00174             std::cout << "cern_adapt Iter: " << iter;
00175             std::cout.setf(std::ios::showpos);
00176             std::cout << " Res: " << tvals;
00177             std::cout.unsetf(std::ios::showpos);
00178             std::cout << " Err: " << sqrt(2.0*terss);
00179             if (this->tol_abs>this->tol_rel*fabs(tvals)) {
00180               std::cout << " Tol: " << this->tol_abs << std::endl;
00181             } else {
00182               std::cout << " Tol: " << this->tol_rel*fabs(tvals) << std::endl;
00183             }
00184             if (this->verbose>1) {
00185               char ch;
00186               std::cout << "Press a key and type enter to continue. " ;
00187               std::cin >> ch;
00188             }
00189           }
00190 
00191           // See if we're finished
00192           root=sqrt(2.0*terss);
00193           if (root<=this->tol_abs || root<=this->tol_rel*fabs(tvals)) {
00194             res=tvals;
00195             err=root;
00196             this->last_iter=iter;
00197             return 0;
00198           }
00199 
00200           // Test if we've run out of intervals
00201           if (prev_subdiv==nsub) {
00202             res=tvals;
00203             err=root;
00204             this->last_iter=iter;
00205             std::string s="Reached maximum number of "+itos(nsub)+
00206               "subdivisions in cern_adapt::integ_err().";
00207             this->last_conv=gsl_etable;
00208             O2SCL_CONV_RET(s.c_str(),gsl_etable,this->err_nonconv);
00209           }
00210 
00211           // Find the subdivision with the largest error
00212           double bige=ters[0];
00213           int ibig=0;
00214           for(i=1;i<prev_subdiv;i++) {
00215             if (ters[i]>bige) {
00216               bige=ters[i];
00217               ibig=i;
00218             }
00219           }
00220 
00221           // Subdivide that subdivision further
00222           xhi[prev_subdiv]=xhi[ibig];
00223           double xnew=(xlo[ibig]+xhi[ibig])/2.0;
00224           xhi[ibig]=xnew;
00225           xlo[prev_subdiv]=xnew;
00226           it->integ_err(func,xlo[ibig],xhi[ibig],tval[ibig],te);
00227           ters[ibig]=te*te;
00228           it->integ_err(func,xlo[prev_subdiv],
00229                         xhi[prev_subdiv],tval[prev_subdiv],te);
00230           ters[prev_subdiv]=te*te;
00231           prev_subdiv++;
00232 
00233         }
00234 
00235         // FIXME: Should we set an error here, or does this
00236         // only happen if we happen to need exactly nsub
00237         // intervals?
00238         res=tvals;
00239         err=root;
00240         return 0;
00241       }
00242       //@}
00243 
00244       /// \name Integration object
00245       //@{
00246       /// Set the base integration object to use
00247       int set_inte(inte<func_t> &i) {
00248         it=&i;
00249         return 0;
00250       }
00251       
00252       /// Default integration object
00253       cern_gauss56<func_t> def_inte;
00254       //@}
00255       
00256       /// \name Subdivisions
00257       //@{
00258       /** \brief Number of subdivisions 
00259 
00260           The options are
00261           - 0: Use previous binning and do not subdivide further \n
00262           - 1: Automatic - adapt until tolerance is attained (default) \n
00263           - n: (n>1) split first in n equal subdivisions, then adapt
00264           until tolerance is obtained.
00265       */
00266       size_t nsubdiv;
00267 
00268       /// Return the number of subdivisions used in the last integration
00269       size_t get_nsubdivisions() {
00270         return prev_subdiv;
00271       }
00272 
00273       /// Return the ith subdivision
00274       int get_ith_subdivision(size_t i, double &xlow, double &xhigh, 
00275                               double &value, double &errsq) {
00276         if (i<prev_subdiv) {
00277           xlow=xlo[i];
00278           xhigh=xhi[i];
00279           value=tval[i];
00280           errsq=ters[i];
00281         }
00282         return 0;
00283       }
00284       
00285       /// Return all of the subdivisions
00286       template<class vec_t> 
00287       int get_subdivisions(vec_t &xlow, vec_t &xhigh, vec_t &value, 
00288                            vec_t &errsq) {
00289 
00290         for(int i=0;i<prev_subdiv;i++) {
00291           xlow[i]=xlo[i];
00292           xhigh[i]=xhi[i];
00293           value[i]=tval[i];
00294           errsq[i]=ters[i];
00295         }
00296         return 0;
00297       }
00298       //@}
00299 
00300     };
00301 
00302 #ifndef DOXYGENP
00303 }
00304 #endif
00305 
00306 #endif
 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.