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 #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 00058 - Allow user to set the initial segments? 00059 - It might be interesting to directly compare the performance 00060 of this class to \ref gsl_inte_qag . 00061 */ 00062 template<class param_t, class func_t=funct<param_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; 00151 integ_err(func,a,b,pa,res,this->interror); 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 this->last_conv=0; 00166 00167 if (nseg==0) { 00168 if (nter==0) { 00169 // If the previous binning was requested, but 00170 // there is no previous binning stored, then 00171 // just shift to automatic binning 00172 nsegd=1; 00173 } else { 00174 tvals=0.0; 00175 terss=0.0; 00176 for(i=0;i<nter;i++) { 00177 it->integ_err(func,xlo[i],xhi[i],pa,tval[i],te); 00178 ters[i]=te*te; 00179 tvals+=tval[i]; 00180 terss+=ters[i]; 00181 } 00182 err=sqrt(2.0*terss); 00183 res=tvals; 00184 return 0; 00185 } 00186 } 00187 00188 // Ensure we're not asking for too many divisions 00189 if (nsub<nseg) { 00190 nsegd=nsub; 00191 } else { 00192 nsegd=nseg; 00193 } 00194 00195 // Compute the initial set of intervals and integral values 00196 xhib=a; 00197 double bin=(b-a)/((double)nsegd); 00198 for(i=0;i<nsegd;i++) { 00199 xlo[i]=xhib; 00200 xlob=xlo[i]; 00201 xhi[i]=xhib+bin; 00202 if (i==nsegd-1) xhi[i]=b; 00203 xhib=xhi[i]; 00204 it->integ_err(func,xlob,xhib,pa,tval[i],te); 00205 ters[i]=te*te; 00206 } 00207 nter=nsegd; 00208 00209 for(size_t iter=1;iter<=nsub;iter++) { 00210 00211 // Compute the total value of the integrand 00212 // and the squared uncertainty 00213 tvals=tval[0]; 00214 terss=ters[0]; 00215 for(i=1;i<nter;i++) { 00216 tvals+=tval[i]; 00217 terss+=ters[i]; 00218 } 00219 00220 // Output iteration information 00221 if (this->verbose>0) { 00222 std::cout << "cern_adapt Iter: " << iter; 00223 std::cout.setf(std::ios::showpos); 00224 std::cout << " Res: " << tvals; 00225 std::cout.unsetf(std::ios::showpos); 00226 std::cout << " Err: " << sqrt(2.0*terss); 00227 if (this->tolx>this->tolf*fabs(tvals)) { 00228 std::cout << " Tol: " << this->tolx << std::endl; 00229 } else { 00230 std::cout << " Tol: " << this->tolf*fabs(tvals) << std::endl; 00231 } 00232 if (this->verbose>1) { 00233 char ch; 00234 std::cout << "Press a key and type enter to continue. " ; 00235 std::cin >> ch; 00236 } 00237 } 00238 00239 // See if we're finished 00240 root=sqrt(2.0*terss); 00241 if (root<=this->tolx || root<=this->tolf*fabs(tvals)) { 00242 res=tvals; 00243 err=root; 00244 this->last_iter=iter; 00245 return 0; 00246 } 00247 00248 // Test if we've run out of intervals 00249 if (nter==nsub) { 00250 res=tvals; 00251 err=root; 00252 this->last_iter=iter; 00253 std::string s="Reached maximum number of "; 00254 s+="subdivisions in cern_adapt::integ_err()."; 00255 this->last_conv=gsl_etable; 00256 O2SCL_CONV_RET(s.c_str(),gsl_etable,this->err_nonconv); 00257 } 00258 00259 // Find the segment with the largest error 00260 double bige=ters[0]; 00261 int ibig=0; 00262 for(i=1;i<nter;i++) { 00263 if (ters[i]>bige) { 00264 bige=ters[i]; 00265 ibig=i; 00266 } 00267 } 00268 00269 // Subdivide that segment further 00270 xhi[nter]=xhi[ibig]; 00271 double xnew=(xlo[ibig]+xhi[ibig])/2.0; 00272 xhi[ibig]=xnew; 00273 xlo[nter]=xnew; 00274 it->integ_err(func,xlo[ibig],xhi[ibig],pa,tval[ibig],te); 00275 ters[ibig]=te*te; 00276 it->integ_err(func,xlo[nter],xhi[nter],pa,tval[nter],te); 00277 ters[nter]=te*te; 00278 nter++; 00279 00280 } 00281 00282 // FIXME: Should we set an error here, or does this 00283 // only happen if we happen to need exactly nsub 00284 // intervals? 00285 res=tvals; 00286 err=root; 00287 return 0; 00288 } 00289 00290 }; 00291 00292 #ifndef DOXYGENP 00293 } 00294 #endif 00295 00296 #endif
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