cern_adapt.h

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

Documentation generated with Doxygen and provided under the GNU Free Documentation License. See License Information for details.

Project hosting provided by SourceForge.net Logo, O2scl Sourceforge Project Page