Version: SMASH-1.5
integrate.h
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2015-2018
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #ifndef SRC_INCLUDE_INTEGRATE_H_
11 #define SRC_INCLUDE_INTEGRATE_H_
12 
13 #include <cuba.h>
14 #include <gsl/gsl_integration.h>
15 #include <gsl/gsl_monte_plain.h>
16 #include <gsl/gsl_monte_vegas.h>
17 
18 #include <algorithm>
19 #include <iostream>
20 #include <memory>
21 #include <sstream>
22 #include <string>
23 #include <tuple>
24 #include <utility>
25 
26 #include "cxx14compat.h"
27 #include "fpenvironment.h"
28 #include "random.h"
29 
30 namespace smash {
31 
39  constexpr GslWorkspaceDeleter() = default;
40 
42  void operator()(gsl_integration_cquad_workspace *ptr) const {
43  if (ptr == nullptr) {
44  return;
45  }
46  gsl_integration_cquad_workspace_free(ptr);
47  }
48 };
49 
54 class Result : public std::pair<double, double> {
56  using Base = std::pair<double, double>;
57 
58  public:
60  using Base::pair;
61 
63  operator double() const { return Base::first; }
64 
66  double value() const { return Base::first; }
67 
69  double error() const { return Base::second; }
70 
79  void check_error(const std::string &integration_name,
80  double relative_tolerance = 5e-4,
81  double absolute_tolerance = 1e-9) const {
82  const double allowed_error =
83  std::max(absolute_tolerance, value() * relative_tolerance);
84  if (error() > allowed_error) {
85  std::stringstream error_msg;
86  error_msg << integration_name << " resulted in I = " << value() << " ± "
87  << error()
88  << ", but the required precision is either absolute error < "
89  << absolute_tolerance << " or relative error < "
90  << relative_tolerance << std::endl;
91  throw std::runtime_error(error_msg.str());
92  }
93  }
94 };
95 
106 class Integrator {
107  public:
122  explicit Integrator(size_t workspace_size = 1000)
123  : workspace_(gsl_integration_cquad_workspace_alloc(workspace_size)) {}
124 
138  template <typename F>
139  Result operator()(double a, double b, F &&fun) {
140  Result result;
141  const gsl_function gslfun{
142  /* important! The lambda cannot use captures, otherwise the
143  * conversion to a function pointer type is impossible. */
144  [](double x, void *type_erased) -> double {
145  auto &&f = *static_cast<F *>(type_erased);
146  return f(x);
147  },
148  &fun};
149  // We disable float traps when calling GSL code we cannot control.
150  DisableFloatTraps guard;
151  const int error_code = gsl_integration_cquad(
152  &gslfun, a, b, accuracy_absolute_, accuracy_relative_, workspace_.get(),
153  &result.first, &result.second,
154  nullptr /* Don't store the number of evaluations */);
155  if (error_code) {
156  std::stringstream err;
157  err << "GSL 1D deterministic integration: " << gsl_strerror(error_code);
158  throw std::runtime_error(err.str());
159  }
160  result.check_error("GSL 1D deterministic integration", accuracy_relative_,
162  return result;
163  }
164 
165  private:
167  std::unique_ptr<gsl_integration_cquad_workspace, GslWorkspaceDeleter>
169 
171  const double accuracy_absolute_ = 1.0e-9;
172 
174  const double accuracy_relative_ = 5.0e-4;
175 };
176 
189  public:
204  explicit Integrator1dMonte(size_t num_calls = 1E6)
205  : state_(gsl_monte_plain_alloc(1)),
206  rng_(gsl_rng_alloc(gsl_rng_mt19937)),
207  number_of_calls_(num_calls) {
208  gsl_monte_plain_init(state_);
209  // initialize the GSL RNG with a random seed
210  const uint32_t seed = random::uniform_int(0ul, ULONG_MAX);
211  gsl_rng_set(rng_, seed);
212  }
213 
216  gsl_monte_plain_free(state_);
217  gsl_rng_free(rng_);
218  }
219 
234  template <typename F>
235  Result operator()(double min, double max, F &&fun) {
236  Result result = {0, 0};
237 
238  const double lower[1] = {min};
239  const double upper[1] = {max};
240 
241  if (max <= min)
242  return result;
243 
244  const gsl_monte_function monte_fun{
245  // trick: pass integrand function as 'params'
246  [](double *x, size_t /*dim*/, void *params) -> double {
247  auto &&f = *static_cast<F *>(params);
248  return f(x[0]);
249  },
250  1, &fun};
251 
252  const int error_code =
253  gsl_monte_plain_integrate(&monte_fun, lower, upper, 1, number_of_calls_,
254  rng_, state_, &result.first, &result.second);
255  if (error_code) {
256  std::stringstream err;
257  err << "GSL 1D Monte-Carlo integration: " << gsl_strerror(error_code);
258  throw std::runtime_error(err.str());
259  }
260 
261  result.check_error("GSL 1D Monte-Carlo integration");
262 
263  return result;
264  }
265 
266  private:
268  gsl_monte_plain_state *state_;
269 
271  gsl_rng *rng_;
272 
274  const std::size_t number_of_calls_;
275 };
276 
289  public:
304  explicit Integrator2d(size_t num_calls = 1E6)
305  : state_(gsl_monte_plain_alloc(2)),
306  rng_(gsl_rng_alloc(gsl_rng_mt19937)),
307  number_of_calls_(num_calls) {
308  gsl_monte_plain_init(state_);
309  // initialize the GSL RNG with a random seed
310  const uint32_t seed = random::uniform_int(0ul, ULONG_MAX);
311  gsl_rng_set(rng_, seed);
312  }
313 
316  gsl_monte_plain_free(state_);
317  gsl_rng_free(rng_);
318  }
319 
336  template <typename F>
337  Result operator()(double min1, double max1, double min2, double max2,
338  F &&fun) {
339  Result result = {0, 0};
340 
341  const double lower[2] = {min1, min2};
342  const double upper[2] = {max1, max2};
343 
344  if (max1 <= min1 || max2 <= min2)
345  return result;
346 
347  const gsl_monte_function monte_fun{
348  // trick: pass integrand function as 'params'
349  [](double *x, size_t /*dim*/, void *params) -> double {
350  auto &&f = *static_cast<F *>(params);
351  return f(x[0], x[1]);
352  },
353  2, &fun};
354 
355  gsl_monte_plain_integrate(&monte_fun, lower, upper, 2, number_of_calls_,
356  rng_, state_, &result.first, &result.second);
357 
358  return result;
359  }
360 
361  private:
363  gsl_monte_plain_state *state_;
364 
366  gsl_rng *rng_;
367 
369  const std::size_t number_of_calls_;
370 };
371 
378 template <typename F>
379 struct Integrand2d {
381  double min1;
383  double diff1;
385  double min2;
387  double diff2;
389  F f;
390 };
391 
407  public:
419  explicit Integrator2dCuhre(int num_calls = 1e6, double epsrel = 5e-4,
420  double epsabs = 1e-9)
421  : maxeval_(num_calls), epsrel_(epsrel), epsabs_(epsabs) {}
422 
439  template <typename F>
440  Result operator()(double min1, double max1, double min2, double max2, F fun) {
441  Result result = {0., 0.};
442 
443  if (max1 < min1 || max2 < min2) {
444  bool tolerable = (max1 - min1 > -1.e-16) && (max2 - min2 > -1.e-16);
445  if (tolerable) {
446  return result;
447  }
448  std::stringstream err;
449  err << "Integrator2dCuhre got wrong integration limits: [" << min1 << ", "
450  << max1 << "], [" << min2 << ", " << max2 << "]";
451  throw std::invalid_argument(err.str());
452  }
453 
454  Integrand2d<F> f_with_limits = {min1, max1 - min1, min2, max2 - min2, fun};
455 
456  const integrand_t cuhre_fun{[](const int * /* ndim */, const cubareal xx[],
457  const int * /* ncomp */, cubareal ff[],
458  void *userdata) -> int {
459  auto i = static_cast<Integrand2d<F> *>(userdata);
460  /* We have to transform the integrand to the unit cube.
461  * This is what Cuba expects. */
462  ff[0] = (i->f)(i->min1 + i->diff1 * xx[0], i->min2 + i->diff2 * xx[1]) *
463  i->diff1 * i->diff2;
464  return 0;
465  }};
466 
467  const int ndim = 2;
468  const int ncomp = 1;
469  void *userdata = &f_with_limits;
470  const int nvec = 1;
471  const int flags = 0; // Use the defaults.
472  const int mineval = 0;
473  const int maxeval = maxeval_;
474  const int key = -1; // Use the default.
475  const char *statefile = nullptr;
476  void *spin = nullptr;
477 
478  Cuhre(ndim, ncomp, cuhre_fun, userdata, nvec, epsrel_, epsabs_, flags,
479  mineval, maxeval, key, statefile, spin, &nregions_, &neval_, &fail_,
480  &result.first, &result.second, &prob_);
481 
482  if (fail_) {
483  std::stringstream err;
484  err << "After " << neval_ << " evaluations "
485  << "Cuhre integration from Cuba reports error code " << fail_;
486  throw std::runtime_error(err.str());
487  }
488  result.check_error("Cuba integration ", epsrel_, epsabs_);
489 
490  return result;
491  }
492 
493  private:
495  int maxeval_;
497  double epsrel_;
499  double epsabs_;
503  int neval_;
511  int fail_;
516  double prob_;
517 };
518 
519 } // namespace smash
520 
521 #endif // SRC_INCLUDE_INTEGRATE_H_
int maxeval_
The (approximate) maximum number of integrand evaluations allowed.
Definition: integrate.h:495
double min2
the lower bound of the second integrated variable
Definition: integrate.h:385
double value() const
Access the first entry in the pair as the value.
Definition: integrate.h:66
gsl_monte_plain_state * state_
internal state of the Monte-Carlo integrator
Definition: integrate.h:268
This is a wrapper for the integrand, so we can pass the limits as well for renormalizing to the unit ...
Definition: integrate.h:379
Result operator()(double a, double b, F &&fun)
The function call operator implements the integration functionality.
Definition: integrate.h:139
constexpr GslWorkspaceDeleter()=default
The class has no members, so this is a noop.
A deleter type for std::unique_ptr to be used with gsl_integration_workspace pointers.
Definition: integrate.h:37
~Integrator1dMonte()
Destructor: Clean up internal state and RNG.
Definition: integrate.h:215
Guard type that safely disables floating point traps for the scope in which it is placed...
Definition: fpenvironment.h:79
const std::size_t number_of_calls_
number of calls to the integrand
Definition: integrate.h:369
A C++ interface for numerical integration in two dimensions with the GSL Monte-Carlo integration func...
Definition: integrate.h:288
Integrator1dMonte(size_t num_calls=1E6)
Construct an integration functor.
Definition: integrate.h:204
std::pair< double, double > Base
The data type to store the value and the error of the integration.
Definition: integrate.h:56
std::unique_ptr< gsl_integration_cquad_workspace, GslWorkspaceDeleter > workspace_
Holds the workspace pointer.
Definition: integrate.h:168
double min1
the lower bound of the first integrated variable
Definition: integrate.h:381
int neval_
Actual number of integrand evaluations needed.
Definition: integrate.h:503
double error() const
Access the second entry in the pair as the absolute error.
Definition: integrate.h:69
A C++ interface for numerical integration in two dimensions with the Cuba Cuhre integration function...
Definition: integrate.h:406
void check_error(const std::string &integration_name, double relative_tolerance=5e-4, double absolute_tolerance=1e-9) const
Check whether the error is small and alert if it is not.
Definition: integrate.h:79
A C++ interface for numerical integration in one dimension with the GSL Monte-Carlo integration funct...
Definition: integrate.h:188
The result type returned from integrations, containing the value and an error.
Definition: integrate.h:54
double diff2
the integration range of the second integrated variable
Definition: integrate.h:387
T uniform_int(T min, T max)
Definition: random.h:97
int fail_
An error flag.
Definition: integrate.h:511
Integrator2d(size_t num_calls=1E6)
Construct an integration functor.
Definition: integrate.h:304
const double accuracy_relative_
Parameter to the GSL integration function: desired relative error limit.
Definition: integrate.h:174
Integrator2dCuhre(int num_calls=1e6, double epsrel=5e-4, double epsabs=1e-9)
Construct an integration functor.
Definition: integrate.h:419
gsl_monte_plain_state * state_
internal state of the Monte-Carlo integrator
Definition: integrate.h:363
double diff1
the integration range of the first integrated variable
Definition: integrate.h:383
gsl_rng * rng_
random number generator
Definition: integrate.h:366
Result operator()(double min1, double max1, double min2, double max2, F &&fun)
The function call operator implements the integration functionality.
Definition: integrate.h:337
const double accuracy_absolute_
Parameter to the GSL integration function: desired absolute error limit.
Definition: integrate.h:171
double prob_
The chi^2 probability that the error is not a reliable estimate of the true integration error...
Definition: integrate.h:516
F f
the integrated function
Definition: integrate.h:389
void operator()(gsl_integration_cquad_workspace *ptr) const
Frees the gsl_integration_cquad_workspace resource if it is non-zero.
Definition: integrate.h:42
double epsabs_
Requested absolute accuracy.
Definition: integrate.h:499
gsl_rng * rng_
random number generator
Definition: integrate.h:271
Result operator()(double min, double max, F &&fun)
The function call operator implements the integration functionality.
Definition: integrate.h:235
const std::size_t number_of_calls_
number of calls to the integrand
Definition: integrate.h:274
A C++ interface for numerical integration in one dimension with the GSL CQUAD integration functions...
Definition: integrate.h:106
Result operator()(double min1, double max1, double min2, double max2, F fun)
The function call operator implements the integration functionality.
Definition: integrate.h:440
int nregions_
Actual number of subregions needed.
Definition: integrate.h:501
~Integrator2d()
Destructor: Clean up internal state and RNG.
Definition: integrate.h:315
Integrator(size_t workspace_size=1000)
Construct an integration functor with the given workspace_size.
Definition: integrate.h:122
double epsrel_
Requested relative accuracy.
Definition: integrate.h:497
Definition: action.h:24