cern_adapt.h

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