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 /* monte/miser.c 00024 * 00025 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Michael Booth 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 #ifndef O2SCL_GSL_MISER_H 00043 #define O2SCL_GSL_MISER_H 00044 00045 #include <iostream> 00046 #include <o2scl/misc.h> 00047 #include <o2scl/collection.h> 00048 #include <o2scl/mcarlo_inte.h> 00049 #include <o2scl/gsl_rnga.h> 00050 #include <gsl/gsl_math.h> 00051 #include <gsl/gsl_monte.h> 00052 #include <gsl/gsl_machine.h> 00053 #include <gsl/gsl_monte_miser.h> 00054 00055 #ifndef DOXYGENP 00056 namespace o2scl { 00057 #endif 00058 00059 /** 00060 \brief Multidimensional integration using Miser Monte Carlo (GSL) 00061 00062 \todoc Document the fact that min_calls and 00063 min_calls_per_bisection need to be set beforehand 00064 \todoc Document the member data 00065 \todo The testing file calls the error handler on the 00066 composite_inte section. Fix this. 00067 00068 Based on \ref Press90 . 00069 */ 00070 template<class param_t, class func_t=multi_funct<param_t>, 00071 class rng_t=gsl_rnga, class vec_t=ovector_base, class alloc_vec_t=ovector, 00072 class alloc_t=ovector_alloc> class gsl_miser : 00073 public mcarlo_inte<param_t,func_t,rng_t,vec_t> { 00074 00075 public: 00076 00077 /** 00078 \brief Introduce random variation into bisection (default 0.0) 00079 00080 From GSL documentation: 00081 \verbatim 00082 This parameter introduces a random fractional variation of size 00083 DITHER into each bisection, which can be used to break the 00084 symmetry of integrands which are concentrated near the exact 00085 center of the hypercubic integration region. The default value of 00086 dither is zero, so no variation is introduced. If needed, a 00087 typical value of DITHER is 0.1. 00088 \endverbatim 00089 */ 00090 double dither; 00091 00092 /** 00093 \brief Specify fraction of function calls for estimating variance 00094 00095 From GSL documentation: 00096 \verbatim 00097 This parameter specifies the fraction of the currently available 00098 number of function calls which are allocated to estimating the 00099 variance at each recursive step. The default value is 0.1. 00100 \endverbatim 00101 */ 00102 double estimate_frac; 00103 00104 /** 00105 \brief How estimated variances for two sub-regions are combined 00106 00107 From GSL documentation: 00108 \verbatim 00109 This parameter controls how the estimated variances for the two 00110 sub-regions of a bisection are combined when allocating points. 00111 With recursive sampling the overall variance should scale better 00112 than 1/N, since the values from the sub-regions will be obtained 00113 using a procedure which explicitly minimizes their variance. To 00114 accommodate this behavior the MISER algorithm allows the total 00115 variance to depend on a scaling parameter \alpha, 00116 00117 \Var(f) = {\sigma_a \over N_a^\alpha} + {\sigma_b \over N_b^\alpha}. 00118 00119 The authors of the original paper describing MISER recommend the 00120 value \alpha = 2 as a good choice, obtained from numerical 00121 experiments, and this is used as the default value in this 00122 implementation. 00123 \endverbatim 00124 */ 00125 double alpha; 00126 00127 /** 00128 \brief Minimum number of calls to estimate the variance 00129 (default 100) 00130 00131 From GSL documentation: 00132 \verbatim 00133 This parameter specifies the minimum number of function calls 00134 required for each estimate of the variance. If the number of 00135 function calls allocated to the estimate using ESTIMATE_FRAC falls 00136 below MIN_CALLS then MIN_CALLS are used instead. This ensures 00137 that each estimate maintains a reasonable level of accuracy. The 00138 default value of MIN_CALLS is `16 * dim'. 00139 \endverbatim 00140 */ 00141 size_t min_calls; 00142 00143 /** 00144 \brief Minimum number of calls required to proceed with bisection 00145 (default 4000) 00146 00147 From GSL documentation: 00148 \verbatim 00149 This parameter specifies the minimum number of function calls 00150 required to proceed with a bisection step. When a recursive step 00151 has fewer calls available than MIN_CALLS_PER_BISECTION it performs 00152 a plain Monte Carlo estimate of the current sub-region and 00153 terminates its branch of the recursion. The default value of this 00154 parameter is `32 * min_calls'. 00155 \endverbatim 00156 */ 00157 size_t min_calls_per_bisection; 00158 00159 #ifndef DOXYGEN_INTERNAL 00160 00161 protected: 00162 00163 /// Desc 00164 size_t dim; 00165 /// Desc 00166 double *xmid; 00167 /// Desc 00168 double *sigma_l; 00169 /// Desc 00170 double *sigma_r; 00171 /// Desc 00172 double *fmax_l; 00173 /// Desc 00174 double *fmax_r; 00175 /// Desc 00176 double *fmin_l; 00177 /// Desc 00178 double *fmin_r; 00179 /// Desc 00180 double *fsum_l; 00181 /// Desc 00182 double *fsum_r; 00183 /// Desc 00184 double *fsum2_l; 00185 /// Desc 00186 double *fsum2_r; 00187 /// Desc 00188 size_t *hits_l; 00189 /// Desc 00190 size_t *hits_r; 00191 00192 /// Desc 00193 virtual int estimate_corrmc(func_t &func, size_t ndim, 00194 const vec_t &xl, const vec_t &xu, 00195 param_t &pa, size_t calls, double &res, 00196 double &err, const double lxmid[], 00197 double lsigma_l[], double lsigma_r[]) { 00198 size_t i, n; 00199 00200 double m = 0.0, q = 0.0; 00201 double vol = 1.0; 00202 00203 for (i = 0; i < dim; i++) { 00204 vol *= xu[i] - xl[i]; 00205 hits_l[i] = hits_r[i] = 0; 00206 fsum_l[i] = fsum_r[i] = 0.0; 00207 fsum2_l[i] = fsum2_r[i] = 0.0; 00208 lsigma_l[i] = lsigma_r[i] = -1; 00209 } 00210 00211 for (n = 0; n < calls; n++) { 00212 double fval; 00213 00214 unsigned int j = (n/2) % dim; 00215 unsigned int side = (n % 2); 00216 00217 for (i = 0; i < dim; i++) { 00218 00219 // The equivalent of gsl_rng_uniform_pos() 00220 double z; 00221 do { z=this->def_rng.random(); } while (z==0); 00222 00223 if (i != j) { 00224 x[i] = xl[i] + z * (xu[i] - xl[i]); 00225 } else { 00226 if (side == 0) { 00227 x[i] = lxmid[i] + z * (xu[i] - lxmid[i]); 00228 } else { 00229 x[i] = xl[i] + z * (lxmid[i] - xl[i]); 00230 } 00231 } 00232 } 00233 00234 func(ndim,x,fval,pa); 00235 00236 /* recurrence for mean and variance */ 00237 { 00238 double d = fval - m; 00239 m += d / (n + 1.0); 00240 q += d * d * (n / (n + 1.0)); 00241 } 00242 00243 /* compute the variances on each side of the bisection */ 00244 for (i = 0; i < dim; i++) { 00245 if (x[i] <= lxmid[i]) { 00246 fsum_l[i] += fval; 00247 fsum2_l[i] += fval * fval; 00248 hits_l[i]++; 00249 } else { 00250 fsum_r[i] += fval; 00251 fsum2_r[i] += fval * fval; 00252 hits_r[i]++; 00253 } 00254 } 00255 } 00256 00257 for (i = 0; i < dim; i++) { 00258 double fraction_l = (lxmid[i] - xl[i]) / (xu[i] - xl[i]); 00259 00260 if (hits_l[i] > 0) { 00261 fsum_l[i] /= hits_l[i]; 00262 lsigma_l[i] = sqrt (fsum2_l[i] - fsum_l[i] * fsum_l[i] / hits_l[i]); 00263 lsigma_l[i] *= fraction_l * vol / hits_l[i]; 00264 } 00265 00266 if (hits_r[i] > 0) { 00267 fsum_r[i] /= hits_r[i]; 00268 lsigma_r[i] = sqrt (fsum2_r[i] - fsum_r[i] * fsum_r[i] / hits_r[i]); 00269 lsigma_r[i] *= (1 - fraction_l) * vol / hits_r[i]; 00270 } 00271 } 00272 00273 res = vol * m; 00274 00275 if (calls < 2) { 00276 err = GSL_POSINF; 00277 } else { 00278 err = vol * sqrt (q / (calls * (calls - 1.0))); 00279 } 00280 00281 return gsl_success; 00282 } 00283 00284 /// Memory allocator 00285 alloc_t ao; 00286 00287 /// The most recent integration point 00288 alloc_vec_t x; 00289 00290 #endif 00291 00292 public: 00293 00294 gsl_miser() { 00295 estimate_frac=0.1; 00296 alpha=2.0; 00297 dither=0.0; 00298 min_calls=100; 00299 min_calls_per_bisection=4000; 00300 } 00301 00302 /// Allocate memory 00303 virtual int allocate(size_t ldim) { 00304 00305 const char *names[11]= 00306 {"xmid","sigma_l","sigma_r","fmax_l","fmax_r","fmin_l", 00307 "fmin_r","fsum_l","fsum_r","fsum2_l","fsum2_r"}; 00308 double *ptrs[11]; 00309 int ret=gsl_alloc_arrays<double>(11,ldim,names,ptrs, 00310 "gsl_miser::allocate()"); 00311 if (ret!=0) return ret; 00312 00313 xmid=ptrs[0]; 00314 sigma_l=ptrs[1]; 00315 sigma_r=ptrs[2]; 00316 fmax_l=ptrs[3]; 00317 fmax_r=ptrs[4]; 00318 fmin_l=ptrs[5]; 00319 fmin_r=ptrs[6]; 00320 fsum_l=ptrs[7]; 00321 fsum_r=ptrs[8]; 00322 fsum2_l=ptrs[9]; 00323 fsum2_r=ptrs[10]; 00324 00325 const char *names2[2]={"hits_l","hits_r"}; 00326 size_t *ptrs2[2]; 00327 ret=gsl_alloc_arrays<size_t>(2,ldim,names2,ptrs2, 00328 "gsl_miser::allocate()"); 00329 if (ret==0) { 00330 ao.allocate(x,ldim); 00331 00332 hits_l=ptrs2[0]; 00333 hits_r=ptrs2[1]; 00334 00335 dim=ldim; 00336 } 00337 00338 return ret; 00339 00340 } 00341 00342 /// Free allocated memory 00343 virtual int free() { 00344 std::free(hits_r); 00345 std::free(hits_l); 00346 std::free(fsum2_r); 00347 std::free(fsum2_l); 00348 std::free(fsum_r); 00349 std::free(fsum_l); 00350 std::free(fmin_r); 00351 std::free(fmin_l); 00352 std::free(fmax_r); 00353 std::free(fmax_l); 00354 std::free(sigma_r); 00355 std::free(sigma_l); 00356 std::free(xmid); 00357 ao.free(x); 00358 return 0; 00359 } 00360 00361 /** \brief Integrate function \c func over the hypercube from 00362 \f$ x_i=\mathrm{xl}_i \f$ to \f$ x_i=\mathrm{xu}_i \f$ for 00363 \f$ 0<i< \f$ ndim-1 00364 */ 00365 virtual int miser_minteg_err(func_t &func, size_t ndim, const vec_t &xl, 00366 const vec_t &xu, size_t calls, param_t &pa, 00367 double &res, double &err) { 00368 00369 size_t n, estimate_calls, calls_l, calls_r; 00370 size_t i; 00371 size_t i_bisect; 00372 int found_best; 00373 00374 double res_est = 0, err_est = 0; 00375 double res_r = 0, err_r = 0, res_l = 0, err_l = 0; 00376 double xbi_l, xbi_m, xbi_r, s; 00377 00378 double vol; 00379 double weight_l, weight_r; 00380 00381 for (i = 0; i < dim; i++) { 00382 if (xu[i] <= xl[i]) { 00383 O2SCL_ERR2_RET("Upper limit must be greater than lower limit ", 00384 "in gsl_miser::miser_minteg_err().",gsl_einval); 00385 } 00386 if (xu[i] - xl[i] > GSL_DBL_MAX) { 00387 O2SCL_ERR2_RET("Range of integration is too large ", 00388 "in gsl_miser::miser_minteg_err().",gsl_einval); 00389 } 00390 } 00391 00392 if (alpha < 0) { 00393 O2SCL_ERR2_RET("Parameter 'alpha' must be non-negative ", 00394 "in gsl_miser::miser_minteg_err().",gsl_einval); 00395 } 00396 00397 /* Compute volume */ 00398 00399 vol = 1; 00400 00401 for (i = 0; i < dim; i++) { 00402 vol *= xu[i] - xl[i]; 00403 } 00404 00405 if (calls < min_calls_per_bisection) { 00406 double m = 0.0, q = 0.0; 00407 00408 if (calls < 2) { 00409 O2SCL_ERR2_RET("Insufficient calls for subvolume ", 00410 "in gsl_miser::miser_minteg_err().",gsl_einval); 00411 } 00412 00413 for (n = 0; n < calls; n++) { 00414 /* Choose a random point in the integration region */ 00415 00416 for (i = 0; i < dim; i++) { 00417 00418 // The equivalent of gsl_rng_uniform_pos() 00419 double rdn; 00420 do { rdn=this->def_rng.random(); } while (rdn==0); 00421 00422 x[i] = xl[i] + rdn * (xu[i] - xl[i]); 00423 } 00424 00425 { 00426 double fval; 00427 func(ndim,x,fval,pa); 00428 00429 /* recurrence for mean and variance */ 00430 00431 double d = fval - m; 00432 m += d / (n + 1.0); 00433 q += d * d * (n / (n + 1.0)); 00434 } 00435 } 00436 00437 res = vol * m; 00438 00439 err = vol * sqrt (q / (calls * (calls - 1.0))); 00440 00441 return gsl_success; 00442 } 00443 00444 // We explicitly make the typecast here 00445 estimate_calls=((size_t)GSL_MAX(min_calls,calls*(estimate_frac))); 00446 00447 if (estimate_calls < 4 * dim) { 00448 O2SCL_ERR2_RET("Insufficient calls to sample all halfspaces ", 00449 "in gsl_miser::miser_minteg_err().",gsl_esanity); 00450 } 00451 00452 /* Flip coins to bisect the integration region with some fuzz */ 00453 00454 for (i = 0; i < dim; i++) { 00455 s=(this->def_rng.random()-0.5) >= 0.0 ? dither : -dither; 00456 xmid[i] = (0.5 + s) * xl[i] + (0.5 - s) * xu[i]; 00457 } 00458 00459 /* 00460 The idea is to chose the direction to bisect based on which will 00461 give the smallest total variance. We could (and may do so later) 00462 use MC to compute these variances. But the NR guys simply estimate 00463 the variances by finding the min and max function values 00464 for each half-region for each bisection. 00465 */ 00466 00467 estimate_corrmc(func,dim,xl,xu,pa,estimate_calls, 00468 res_est,err_est,xmid,sigma_l,sigma_r); 00469 00470 /* We have now used up some calls for the estimation */ 00471 00472 calls -= estimate_calls; 00473 00474 /* Now find direction with the smallest total "variance" */ 00475 00476 { 00477 double best_var = GSL_DBL_MAX; 00478 double beta = 2.0 / (1.0 + alpha); 00479 found_best = 0; 00480 i_bisect = 0; 00481 weight_l = weight_r = 1.0; 00482 00483 for (i = 0; i < dim; i++) { 00484 if (sigma_l[i] >= 0 && sigma_r[i] >= 0) { 00485 /* estimates are okay */ 00486 double var = pow (sigma_l[i], beta) + pow (sigma_r[i], beta); 00487 00488 if (var <= best_var) { 00489 found_best = 1; 00490 best_var = var; 00491 i_bisect = i; 00492 weight_l = pow (sigma_l[i], beta); 00493 weight_r = pow (sigma_r[i], beta); 00494 if (weight_l==0 && weight_r==0) { 00495 weight_l=1; 00496 weight_r=1; 00497 } 00498 } 00499 } else { 00500 if (sigma_l[i] < 0) { 00501 O2SCL_ERR2_RET("No points in left-half space ", 00502 "in gsl_miser::miser_minteg_err().",gsl_esanity); 00503 } 00504 if (sigma_r[i] < 0) { 00505 O2SCL_ERR2_RET("No points in right-half space ", 00506 "in gsl_miser::miser_minteg_err().",gsl_esanity); 00507 } 00508 } 00509 } 00510 } 00511 00512 if (!found_best) { 00513 /* All estimates were the same, so chose a direction at random */ 00514 00515 i_bisect = this->def_rng.random_int(dim); 00516 } 00517 00518 xbi_l = xl[i_bisect]; 00519 xbi_m = xmid[i_bisect]; 00520 xbi_r = xu[i_bisect]; 00521 00522 /* Get the actual fractional sizes of the two "halves", and 00523 distribute the remaining calls among them */ 00524 00525 { 00526 double fraction_l = fabs ((xbi_m - xbi_l) / (xbi_r - xbi_l)); 00527 double fraction_r = 1 - fraction_l; 00528 00529 double a = fraction_l * weight_l; 00530 double b = fraction_r * weight_r; 00531 00532 calls_l = (size_t)(min_calls + (calls - 2 * min_calls) * a / (a + b)); 00533 calls_r = (size_t)(min_calls + (calls - 2 * min_calls) * b / (a + b)); 00534 } 00535 00536 /* Compute the integral for the left hand side of the bisection */ 00537 00538 /* Due to the recursive nature of the algorithm we must allocate 00539 some new memory for the integration limits for each recursive call */ 00540 00541 { 00542 int status; 00543 00544 alloc_vec_t xu_tmp; 00545 ao.allocate(xu_tmp,dim); 00546 00547 for (i = 0; i < dim; i++) { 00548 xu_tmp[i] = xu[i]; 00549 } 00550 00551 xu_tmp[i_bisect] = xbi_m; 00552 00553 status=miser_minteg_err(func,dim,xl,xu_tmp,calls_l,pa,res_l,err_l); 00554 00555 ao.free(xu_tmp); 00556 00557 if (status != gsl_success) { 00558 return status; 00559 } 00560 } 00561 00562 /* Compute the integral for the right hand side of the bisection */ 00563 00564 { 00565 int status; 00566 00567 alloc_vec_t xl_tmp; 00568 ao.allocate(xl_tmp,dim); 00569 00570 for (i = 0; i < dim; i++) { 00571 xl_tmp[i] = xl[i]; 00572 } 00573 00574 xl_tmp[i_bisect] = xbi_m; 00575 00576 status=miser_minteg_err(func,dim,xl_tmp,xu,calls_r,pa,res_r,err_r); 00577 00578 ao.free(xl_tmp); 00579 00580 if (status != gsl_success) { 00581 return status; 00582 } 00583 } 00584 00585 res=res_l+res_r; 00586 err=sqrt(err_l*err_l+err_r*err_r); 00587 00588 return 0; 00589 } 00590 00591 virtual ~gsl_miser() {} 00592 00593 /// Integrate function \c func from x=a to x=b. 00594 virtual int minteg_err(func_t &func, size_t ndim, const vec_t &a, 00595 const vec_t &b, param_t &pa, double &res, 00596 double &err) { 00597 allocate(ndim); 00598 min_calls=16*ndim; 00599 min_calls_per_bisection=32*min_calls; 00600 int ret=miser_minteg_err(func,ndim,a,b,this->n_points,pa,res,err); 00601 free(); 00602 return ret; 00603 } 00604 00605 /** \brief Integrate function \c func over the hypercube from 00606 \f$ x_i=a_i \f$ to \f$ x_i=b_i \f$ for 00607 \f$ 0<i< \f$ ndim-1 00608 */ 00609 virtual double minteg(func_t &func, size_t ndim, const vec_t &a, 00610 const vec_t &b, param_t &pa) { 00611 double res; 00612 minteg_err(func,ndim,a,b,pa,res,this->interror); 00613 return res; 00614 } 00615 00616 /// Return string denoting type ("gsl_miser") 00617 virtual const char *type() { return "gsl_miser"; } 00618 00619 }; 00620 00621 #ifndef DOXYGENP 00622 } 00623 #endif 00624 00625 #endif 00626
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