Chapter 2: C++ Class

2.1: Constructor and main member functions

2.1.1: Constructor

Vegas(int cdim, int nsdim, int cnbin, int ddim, int adim,
      const int nvals[], int fn, Integrand fxn = 0, int ncpu = 1);

cdim: number of continuous dimensions
nsdim: number of non-separable continuous dimensions (first nsdim dimensions)
cnbin: number of bins for each continuous dimension
ddim: number of discrete dimensions
adim: number of auxilliary dimensions (no adaptation)
nvals[]: number of values for each discrete dimension (see below)
fn: number of functions to integrate, (weights are adapted for first function)
fxn: pointer to function (see next section)
ncpu: number of CPUs, default: 1 (see section 5.1)

The default integration/summation range is

  • for continuous dimensions: (0, 1)
  • for discrete dimensions: {0, 1, 2, ..., nvals[dim]-1}
  • Random numbers in (0, 1) are provided to facilitate the addition of auxilliary dimensions that are not VEGAS driven.

    The parameters ddim or adim can be set to zero if no discrete or auxilliary dimensions are needed.

    The parameters of a constructed Vegas object can be modified with two member functions:

    void set_parameters(int cdim, int cnbin, int ddim, int adim, const int nvals[], int fn);
    
    void set_integrand(Integrand fxn);
    

    Note that these functions are questionable from a design perspective and may not be supported in the next major version, i.e. versions 2.0.0 and higher.

    If you plan to use the get and put methods (see section 2.6) directly rather than the loop method, just pass 0 for fxn.

    2.1.2: pl_vegas

    If a function pointer fxn of type Integrand:

    typedef void (*Integrand)(const double x[], const int k[], const double& wgt,
                              const double aux[], double f[]);
    

    is passed to the constructor or set_integrand then the function pl_vegas runs through iterats iterations with ncall shots each when called, modeling the behavior of Peter Lepage's VEGAS:

    void pl_vegas(Vegas& vegas, int64 ncall, int iterats, int init = 0);
    

    The array x[] contains the random numbers for the continuous dimensions, the array k[] contains the random numbers for the discrete dimensions, the array aux[] contains the auxilliary random numbers. The VEGAS weight (For each iteration the sum of the weights is normalized to 1.) is passed in variable wgt, so that it can be passed on to functions that fill histograms, for example. The function *fxn is expected to fill the array f[] with the function values that correspond to the arguments x[0], x[1], ..., k[0], k[1], ... and aux[0], aux[1], ... The value of the first function is assigned to f[0]. The integration is optimized for this function according to the VEGAS algorithm. Secondary functions can simultaneously be integrated and their values are expected to be returned as f[1], f[2], etc.

    The parameter init has the same meaning as in the original VEGAS program:

    init = 0: fresh start (no prior information)
    init = 1: use previous adaptation info, but discard shot data
    init = 2: use previous adaptation info and build on previous shots data

    The generic action for a single iteration is encapsulated in the member function:

    void loop(int64 ncall_);
    

    Note that ncall's type is int64, corresponding to a 64-bit integer. int64 might be a typedef to long int or long long int depending on your system. You can find that out by looking at the values of the macros SIZE_LONG and SIZE_LONG_LONG.

    Both, class Vegas and the associated function pl_vegas are enclosed in namespace MonteCarlo.

    2.1.3: Accessing results

    Methods are provided to obtain the results/statistics for each integrated function fn (since the last adaptation):
    double get_integral(int fn) const ;
    
    double get_error(int fn) const ;
    
    double get_redchi2(int fn) const ; // "reduced chi^2" = chi^2/(nr of estimates-1)
    
    int get_niter() const ; // number of iterations (estimates)
    

    or accumulated over all iterations:

    double get_acc_integral(int fn) const ;
    
    double get_acc_error(int fn) const ;
    
    double get_acc_redchi2(int fn) const ;
    
    int get_acc_niter() const ;
    
    

    2.1.4: Saving and restoring the state of a Vegas object

    The state of a Vegas object can be saved to file and restored later with the following methods:

    void save(const char* filename = "vegas.dat") const ;
    
    void save(std::ofstream& fout) const ;
    
    void restore(Integrand fxn, const char* filename = "vegas.dat");
    
    void restore(Integrand fxn, std::ifstream& fin);
    
    void restore(std::ifstream& fin); // integrand set with set_integrand()
    

    Saved are the parameters that can be set with set_parameters and all weights. This does not include the function pointer of type Integrand and the number of CPUs. The restore method does not check if the function to be integrated is the same as when save was called.

    2.2: Sample program

    #ifdef NO_CXX_HEADERS_FOR_C_LIB
    #include <math.h>
    #else
    #include <cmath>
    #endif
    #include "dvegas.h"
    
    #ifndef NO_CXX_HEADERS_FOR_C_LIB
    // <cmath>
    using std::pow ;
    using std::sqrt ;
    using std::exp ;
    #endif
    
    void fxn(const double x[], const int k[], const double& wgt, const double aux[], double f[])
    {
      f[0] = x[0]*x[1]*x[2]*x[3]*x[4]*pow(2.0,5);
    }
    
    void fxn1(const double x[], const int k[], const double& wgt, const double aux[], double f[])
    {
      if (k[0] < 99)
        f[0] =   1 * x[0]*x[1]*x[2]*x[3]*x[4]*pow(2.0,5);
      else if (k[0] == 99)
        f[0] = 100 * x[0]*x[1]*x[2]*x[3]*x[4]*pow(2.0,5);  
    }
    
    int main()
    {
      using MonteCarlo::Vegas ;
      using MonteCarlo::pl_vegas ;
    
      int nvals[2];
      Vegas vegas(5, 50, 0, 0, nvals, 1, &fxn);
      pl_vegas(vegas, 100000, 2); // result: 1.0
    
      nvals[0] = 100 ;
      vegas.set_parameters(5, 50, 1, 0, nvals, 1);
      vegas.set_integrand(&fxn1);
      pl_vegas(vegas, 100000, 1); // result: 199.0, factor sqrt(100)=10 error reduction with discrete weights
      vegas.save();
      pl_vegas(vegas, 100000, 1, 1);
    
      nvals[1] = 3 ;
      vegas.set_parameters(5, 50, 2, 0, nvals, 1);
      pl_vegas(vegas, 100000, 2); // result: 597.0
    
      vegas.restore(&fxn1);
      vegas.save("same.dat");
      pl_vegas(vegas, 100000, 1, 1); // result: 199.0
    }
    
    

    2.3: Limit macros

    VEGAS_CONT_DIMMX
    maximum number of continuous dimensions, default: 20

    VEGAS_CONT_NBINMX
    maximum number of bins for continuous dimensions, default: 50

    VEGAS_DISC_DIMMX
    maximum number of discrete dimensions, default: 5

    VEGAS_DISC_NBINMX
    maximum number of values for discrete dimensions, default: 100

    VEGAS_AUX_DIMMX
    maximum number of auxilliary dimensions (no adaptation), default: 5

    VEGAS_FNMX
    maximum number of functions to integrate, default: 5

    2.4: Numeric types

    The following type definitions are used in the class and function definitions:
    #if SIZEOF_LONG == 8
    typedef long int    int64 ;
    #elif SIZEOF_LONG_LONG == 8
    typedef long long int int64 ;
    #endif
    
    typedef double      float64 ;
    
    

    The type int64 is a 64-bit integer and is used to allow a maximum number of 9,223,372,036,854,755,807. (The typical 32-bit integer has a maximum of about 2 billion, which I found to be too small in some cases to achieve the desired precision.) The type float64 is a 64-bit floating point number and is used in some member functions for variables that accumulate a large number of temporary results (of the order of ncall). 32-bit floating point numbers provide only a precision of typically 7 decimal digits, which can be insufficient when adding about 100 million numbers or more. For 64-bit floating point numbers with a precision of typically 15 decimal digits this problem occurs roughly at 10^16 additions. On most systems the type double corresponds to 64-bit floating point numbers. In rare cases float64 might correspond to long double. (Note that these problems can also occur in histogram packages.)

    In a future dVegas release the issues addressed above will be double-checked in the pre_loop member function, and a warning or error will be issued when problems could arise. This kind of checking is facilitated in C++ by specializations of the numeric_limits template presented in <limits>. See section 22.2, Numeric Limits, in Bjarne Stroustrup's The C++ programming language, 3rd ed.

    2.5: Enumerations

    The enumeration Sampling specifies sampling techniques:
    enum Sampling { importance, stratified } ;
    

    Currently only importance sampling is implemented, since I use dVegas for calculations with high numbers of dimensions. Additional stratified sampling can lead to a substantially improved efficiency in low dimensions (cdim = 1, ..., 4) and will be implemented if the need arises.

    The enumerations Reset and Info hold sets of options. They allow to specify elementary or composite options by name rather than a crypic (hexadecimal) number. The state of each elementary option is stored in a bit that is part of a bit set of related options.

    The enumeration Reset specifies reset options:

    enum Reset { r_data=0x1, r_weights=0x2, r_none=0x0, r_all=0x3 };
    

    The enumeration Info holds a set of options for the info member function, which prints out information about the most recent iteration, as well as accumulated results and statistics. The info member function is a primary target for customization and the options listed below are intended to give an idea of the possibilities:

    enum Info  { i_spec     =0x0001, i_int     =0x0002, i_adapt     =0x0004, i_grid     =0x0008,
                 i_spec_more=0x0010, i_int_more=0x0020, i_adapt_more=0x0040, i_grid_more=0x0080,
                 i_spec_all =0x0011, i_int_all =0x0022, i_adapt_all =0x0044, i_grid_all =0x0088,
                 i_all_short=0x000f, i_all_long=0x00ff };
    

    Here, spec relates to information like parameters and settings, int to information about the integration itself like results, errors and statistics, adapt to information about the progression and efficiency of the adaptation cycles, and grid options would print out weight data (grid data for continuous dimensions). In each category the amount of information displayed can be regulated, e.g. by appending _more and _all.

    In this release the Info argument of the info member function is just a dummy argument and the function outputs information in a style that I found useful. Based on feedback from other users a variety of output options will be implemented in future dVegas releases.

    2.6: Vegas interface

    class Vegas 
    {
    public:
      void loop(int64 ncall_);  // accumulate data
      void* loop_worker() const ;  // thread function for parallel mode
      void adapt(); // adapt weights
      void reset(Reset flag); // discard data
      void info(Info flag) const ;  // print info
    
      void get(double x[], int k[], double* wgt_, double aux[], int icpu = 0);
      void put(const double f[], int icpu = 0);
      void pre_loop(int64 ncall_);
      void post_loop();
    
      Vegas(int cdim, int cnbin, int ddim, int adim, const int nvals[], int fn_, Integrand fxn = 0, int ncpu_ = 1);
      virtual ~Vegas() { }
      // the following two functions are questionable from a design perspective and may not 
      // be available in the next major version (i.e. versions 2.0.0 and higher)
      void set_parameters(int cdim, int cnbin, int ddim, int adim, const int nvals[], int fn_);
      void set_integrand(Integrand fxn);
    
      // results since last adaptation
      double get_integral(int fn_) const ;
      double get_error(int fn_) const ;
      double get_redchi2(int fn_) const ; // "reduced chi^2" = chi^2/(nr of estimates-1)
      int get_niter() const ; // nr of estimates/iterations
    
      // accumulated results
      double get_acc_integral(int fn_) const ;
      double get_acc_error(int fn_) const ;
      double get_acc_redchi2(int fn_) const ; // "reduced chi^2" = chi^2/(nr of estimates-1)
      int get_acc_niter() const ; // nr of estimates/iterations
    
      // save and read weights information and configuration (possibly multiple times)
      void save(const char* filename = "vegas.dat") const ;
      void save(std::ofstream& fout) const ;
      void restore(Integrand fxn, const char* filename = "vegas.dat");
      void restore(Integrand fxn, std::ifstream& fin);
      void restore(std::ifstream& fin); // integrand set with set_integrand()
    
      friend std::ostream& operator<<(std::ostream& s, const Vegas& v);
      friend std::istream& operator>>(std::istream& s,       Vegas& v);
    
      // digital watermark with version information
      void print_version() const ;
    
      // use with caution
      void set_niter(int i);
    
    private:
      // ...
    };
    

    2.7: Vegas implementation

    The complete C++ source code can be found in file src/dvegas.cpp.