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