vegas Package

Introduction

This package provides tools for estimating multidimensional integrals numerically using an enhanced version of the adaptive Monte Carlo vegas algorithm (G. P. Lepage, J. Comput. Phys. 27(1978) 192).

A vegas code generally involves two objects, one representing the integrand and the other representing an integration operator for a particular multidimensional volume. A typical code sequence for a D-dimensional integral has the structure:

# create the integrand
def f(x):
    ... compute the integrand at point x[d] d=0,1...D-1 
    ...

# create an integrator for volume with 
# xl0 <= x[0] <= xu0, xl1 <= x[1] <= xu1 ...
integration_region = [[xl0, xu0], [xl1, xu1], ...]
integrator = vegas.Integrator(integration_region)

# do the integral and print out the result
result = integrator(f, nitn=10, neval=10000)
print(result)

The algorithm iteratively adapts to the integrand over nitn iterations, each of which uses at most neval integrand samples to generate a Monte Carlo estimate of the integral. The final result is the weighted average of the results fom all iterations.

The integrator remembers how it adapted to f(x) and uses this information as its starting point if it is reapplied to f(x) or applied to some other function g(x). An integrator’s state can be archived for future applications using Python’s pickle module.

See the extensive Tutorial in the first section of the vegas documentation.

Integrator Objects

The central component of the vegas package is the integrator class:

class vegas.Integrator

Adaptive multidimensional Monte Carlo integration.

vegas.Integrator objects make Monte Carlo estimates of multidimensional functions f(x) where x[d] is a point in the integration volume:

integ = vegas.Integrator(integration_region)

result = integ(f, nitn=10, neval=10000)

The integator makes nitn estimates of the integral, each using at most neval samples of the integrand, as it adapts to the specific features of the integrand. Successive estimates typically improve in accuracy until the integrator has fully adapted. The integrator returns the weighted average of all nitn estimates, together with an estimate of the statistical (Monte Carlo) uncertainty in that estimate of the integral. The result is an object of type RunningWAvg (which is derived from gvar.GVar).

Parameters:
  • map (array or vegas.AdaptiveMap or vegas.Integrator) –

    The integration region as specified by an array xlimit[d, i] where d is the direction and i=0,1 specify the lower and upper limits of integration in direction d.

    map could also be the integration map from another vegas.Integrator, or that vegas.Integrator itself. In this case the grid is copied from the existing integrator.

  • nitn (positive int) – The maximum number of iterations used to adapt to the integrand and estimate its value. The default value is 10.
  • neval (positive int) – The maximum number of integrand evaluations in each iteration of the vegas algorithm. Increasing neval increases the precision: statistical errors should fall at least as fast as sqrt(1./neval) and often fall much faster. The default value is 1000; realistic problems often require 10–100 times more evaluations than this.
  • alpha (float) – Damping parameter controlling the remapping of the integration variables as vegas adapts to the integrand. Smaller values slow adaptation, which may be desirable for difficult integrands. Small alphas are also sometimes useful after the grid has adapted, to minimize fluctuations away from the optimal grid. The default value is 0.5.
  • beta (float) – Damping parameter controlling the redistribution of integrand evaluations across hypercubes in the stratified sampling of the integrand (over transformed variables). Smaller values limit the amount of redistribution. The theoretically optimal value is 1; setting beta=0 prevents any redistribution of evaluations. The default value is 0.75.
  • nhcube_vec (positive int) – The number of hypercubes (in y space) whose integration points are combined into a single vector to be passed to the integrand, in a single batch, when using vegas in vector mode (see fcntype='vector' below). The default value is 100. Larger values may be lead to faster evaluations, but at the cost of more memory for internal work areas.
  • maxinc_axis (positive int) – The maximum number of increments per axis allowed for the x-space grid. The default value is 1000; there is probably little need to use other values.
  • adapt_to_errors

    adapt_to_errors=False causes vegas to remap the integration variables to emphasize regions where |f(x)| is largest. This is the default mode.

    adapt_to_errors=True causes vegas to remap variables to emphasize regions where the Monte Carlo error is largest. This might be superior when the number of the number of stratifications in the y grid is large (> 50?). It is typically useful only in one or two dimensions.

  • max_nhcube (positive int) – Maximum number of hypercubes allowed for stratification. The default value is 5e8. Larger values can allow for more adaptation (when neval is larger than 2 * max_nhcube), but also can result in very large internal work arrays. The maximum setting is a function of the RAM available to the processor used.
  • max_neval_hcube (positive int) – Maximum number of integrand evaluations per hypercube in the stratification. The default value is 1e7. Larger values might allow for more adaptation (when neval is larger than 2 * max_neval_hcube), but also can result in very large internal work arrays.
  • fcntype

    Specifies the default type of integrand.

    fcntype='scalar' imples that the integrand is a function f(x) of a single integration point x[d].

    fcntype='vector' implies that the integrand function takes three arguments: a list of integration points x[i, d], where i=0...nx-1 labels the integration point and d labels the direction; a buffer f[i] into which the corresponding integrand values are written; and the number nx of integration points provided.

    The default is fcntype=scalar, but this is overridden if the integrand has a fcntype attribute. It is also overridden for classes derived from VecIntegrand, which are treated as fcntype='vector' integrands.

  • rtol (float less than 1) – Relative error in the integral estimate at which point the integrator can stop. The default value is 0.0 which means that the integrator will complete all iterations specified by nitn.
  • atol (float) – Absolute error in the integral estimate at which point the integrator can stop. The default value is 0.0 which means that the integrator will complete all iterations specified by nitn.
  • analyzer

    An object with methods

    analyzer.begin(itn, integrator)

    analyzer.end(itn_result, result)

    where: begin(itn, integrator) is called at the start of each vegas iteration with itn equal to the iteration number and integrator equal to the integrator itself; and end(itn_result, result) is called at the end of each iteration with itn_result equal to the result for that iteration and result equal to the cummulative result of all iterations so far. Setting analyzer=vegas.reporter(), for example, causes vegas to print out a running report of its results as they are produced. The default is analyzer=None.

vegas.Integrator objects have attributes for each of these parameters. In addition they have the following methods:

set(ka={}, **kargs)

Reset default parameters in integrator.

Usage is analogous to the constructor for vegas.Integrator: for example,

old_defaults = integ.set(neval=1e6, nitn=20)

resets the default values for neval and nitn in vegas.Integrator integ. A dictionary, here old_defaults, is returned. It can be used to restore the old defaults using, for example:

integ.set(old_defaults)
settings(ngrid=0)

Assemble summary of integrator settings into string.

Parameters:ngrid (int) – Number of grid nodes in each direction to include in summary. The default is 0.
Returns:String containing the settings.

AdaptiveMap Objects

vegas’s remapping of the integration variables is handled by a vegas.AdaptiveMap object, which maps the original integration variables x into new variables y in a unit hypercube. Each direction has its own map specified by a grid in x space:

x_0 &= a \\
x_1 &= x_0 + \Delta x_0 \\
x_2 &= x_1 + \Delta x_1 \\
\cdots \\
x_N &= x_{N-1} + \Delta x_{N-1} = b

where a and b are the limits of integration. The grid specifies the transformation function at the points y=i/N for i=0,1\ldots N:

x(y\!=\!i/N) = x_i

Linear interpolation is used between those points. The Jacobian for this transformation is:

J(y) = J_i = N \Delta x_i

vegas adjusts the increments sizes to optimize its Monte Carlo estimates of the integral. This involves training the grid. To illustrate how this is done with vegas.AdaptiveMaps consider a simple two dimensional integral over a unit hypercube with integrand:

def f(x):
   return x[0] * x[1] ** 2

We want to create a grid that optimizes uniform Monte Carlo estimates of the integral in y space. We do this by sampling the integrand at a large number ny of random points y[j, d], where j=0...ny-1 and d=0,1, uniformly distributed throughout the integration volume in y space. These samples be used to train the grid using the following code:

import vegas
import numpy as np

def f(x):
   return x[0] * x[1] ** 2

m = vegas.AdaptiveMap([[0, 1], [0, 1]], ninc=5)

ny = 1000
y = np.random.uniform(0., 1., (ny, 2))  # 1000 random y's

x = np.empty(y.shape, float)            # work space
jac = np.empty(y.shape[0], float)
f2 = np.empty(y.shape[0], float)

print('intial grid:')
print(m.settings())

for itn in range(5):                    # 5 iterations to adapt
   m.map(y, x, jac)                     # compute x's and jac

   for j in range(ny):                  # compute training data
      f2[j] = (jac[j] * f(x[j])) ** 2

   m.add_training_data(y, f2)           # adapt
   m.adapt(alpha=1.5)

   print('iteration %d:' % itn)
   print(m.settings())

In each of the 5 iterations, the vegas.AdaptiveMap adjusts the map, making increments smaller where f2 is larger and larger where f2 is smaller. The map converges after only 2 or 3 iterations, as is clear from the output:

initial grid:
    grid[ 0] = [ 0.   0.2  0.4  0.6  0.8  1. ]
    grid[ 1] = [ 0.   0.2  0.4  0.6  0.8  1. ]

iteration 0:
    grid[ 0] = [ 0.     0.411  0.618  0.772  0.89   1.   ]
    grid[ 1] = [ 0.     0.508  0.694  0.822  0.911  1.   ]

iteration 1:
    grid[ 0] = [ 0.     0.408  0.611  0.76   0.887  1.   ]
    grid[ 1] = [ 0.     0.542  0.718  0.835  0.922  1.   ]

iteration 2:
    grid[ 0] = [ 0.     0.411  0.612  0.76   0.887  1.   ]
    grid[ 1] = [ 0.     0.551  0.721  0.835  0.924  1.   ]

iteration 3:
    grid[ 0] = [ 0.     0.411  0.612  0.76   0.887  1.   ]
    grid[ 1] = [ 0.     0.554  0.721  0.836  0.924  1.   ]

iteration 4:
    grid[ 0] = [ 0.     0.411  0.612  0.76   0.887  1.   ]
    grid[ 1] = [ 0.     0.555  0.722  0.836  0.925  1.   ]

The grid increments along direction 0 shrink at larger values x[0], varying as 1/x[0]. Along direction 1 the increments shrink more quickly varying like 1/x[1]**2.

vegas samples the integrand in order to estimate the integral. It uses those same samples to train its vegas.AdaptiveMap in this fashion, for use in subsequent iterations of the algorithm.

class vegas.AdaptiveMap

Adaptive map y->x(y) for multidimensional y and x.

An AdaptiveMap defines a multidimensional map y -> x(y) from the unit hypercube, with 0 <= y[d] <= 1, to an arbitrary hypercube in x space. Each direction is mapped independently with a Jacobian that is tunable (i.e., “adaptive”).

The map is specified by a grid in x-space that, by definition, maps into a uniformly spaced grid in y-space. The nodes of the grid are specified by grid[d, i] where d is the direction (d=0,1...dim-1) and i labels the grid point (i=0,1...N). The mapping for a specific point y into x space is:

y[d] -> x[d] = grid[d, i(y[d])] + inc[d, i(y[d])] * delta(y[d])

where i(y)=floor(y*N), delta(y)=y*N - i(y), and inc[d, i] = grid[d, i+1] - grid[d, i]. The Jacobian for this map,

dx[d]/dy[d] = inc[d, i(y[d])] * N,

is piece-wise constant and proportional to the x-space grid spacing. Each increment in the x-space grid maps into an increment of size 1/N in the corresponding y space. So regions in x space where inc[d, i] is small are stretched out in y space, while larger increments are compressed.

The x grid for an AdaptiveMap can be specified explicitly when the map is created: for example,

m = AdaptiveMap([[0, 0.1, 1], [-1, 0, 1]])

creates a two-dimensional map where the x[0] interval (0,0.1) and (0.1,1) map into the y[0] intervals (0,0.5) and (0.5,1) respectively, while x[1] intervals (-1,0) and (0,1) map into y[1] intervals (0,0.5) and (0.5,1).

More typically an initially uniform map is trained with data f[j] corresponding to ny points y[j, d], with j=0...ny-1, uniformly distributed in y space: for example,

m.add_training_data(y, f)
m.adapt(alpha=1.5)

m.adapt(alpha=1.5) shrinks grid increments where f[j] is large, and expands them where f[j] is small. Typically one has to iterate over several sets of ys and fs before the grid has fully adapted.

The speed with which the grid adapts is determined by parameter alpha. Large (positive) values imply rapid adaptation, while small values (much less than one) imply slow adaptation. As in any iterative process, it is usually a good idea to slow adaptation down in order to avoid instabilities.

Parameters:
  • grid – Initial x grid, where grid[d, i] is the i-th node in direction d.
  • ninc (int or None) – Number of increments along each axis of the x grid. A new grid is generated if ninc differs from grid.shape[1]. The new grid is designed to give the same Jacobian dx(y)/dy as the original grid. The default value, ninc=None, leaves the grid unchanged.
dim

Number of dimensions.

ninc

Number of increments along each grid axis.

grid

The nodes of the grid defining the maps are self.grid[d, i] where d=0... specifies the direction and i=0...self.ninc the node.

inc

The increment widths of the grid:

self.inc[d, i] = self.grid[d, i + 1] - self.grid[d, i]
adapt(alpha=0.0, ninc=None)

Adapt grid to accumulated training data.

self.adapt(...) projects the training data onto each axis independently and maps it into x space. It shrinks x-grid increments in regions where the projected training data is large, and grows increments where the projected data is small. The grid along any direction is unchanged if the training data is constant along that direction.

The number of increments along a direction can be changed by setting parameter ninc.

The grid does not change if no training data has been accumulated, unless ninc is specified, in which case the number of increments is adjusted while preserving the relative density of increments at different values of x.

Parameters:
  • alpha (double or None) – Determines the speed with which the grid adapts to training data. Large (postive) values imply rapid evolution; small values (much less than one) imply slow evolution. Typical values are of order one. Choosing alpha<0 causes adaptation to the unmodified training data (usually not a good idea).
  • ninc (int or None) – Number of increments along each direction in the new grid. The number is unchanged from the old grid if ninc is omitted (or equals None).
add_training_data(y, f, ny=-1)

Add training data f for y-space points y.

Accumulates training data for later use by self.adapt(). Grid increments will be made smaller in regions where f is larger than average, and larger where f is smaller than average. The grid is unchanged (converged?) when f is constant across the grid.

Parameters:
  • y (contiguous 2-d array of floats) – y values corresponding to the training data. y is a contiguous 2-d array, where y[j, d] is for points along direction d.
  • f (contiguous 2-d array of floats) – Training function values. f[j] corresponds to point y[j, d] in y-space.
  • ny (int) – Number of y points: y[j, d] for d=0...dim-1 and j=0...ny-1. ny is set to y.shape[0] if it is omitted (or negative).
__call__(y)

Return x values corresponding to y.

y can be a single dim-dimensional point, or it can be an array y[i,j, ..., d] of such points (d=0..dim-1).

jac(y)

Return the map’s Jacobian at y.

y can be a single dim-dimensional point, or it can be an array y[d,i,j,...] of such points (d=0..dim-1).

make_uniform(ninc=None)

Replace the grid with a uniform grid.

The new grid has ninc increments along each direction if ninc is specified. Otherwise it has the same number of increments as the old grid.

map(y, x, jac, ny=-1)

Map y to x, where jac is the Jacobian.

y[j, d] is an array of ny y-values for direction d. x[j, d] is filled with the corresponding x values, and jac[j] is filled with the corresponding Jacobian values. x and jac must be preallocated: for example,

x = numpy.empty(y.shape, float)
jac = numpy.empty(y.shape[0], float)
Parameters:
  • y (contiguous 2-d array of floats) – y values to be mapped. y is a contiguous 2-d array, where y[j, d] contains values for points along direction d.
  • x (contiguous 2-d array of floats) – Container for x values corresponding to y.
  • jac (contiguous 1-d array of floats) – Container for Jacobian values corresponding to y.
  • ny (int) – Number of y points: y[j, d] for d=0...dim-1 and j=0...ny-1. ny is set to y.shape[0] if it is omitted (or negative).
show_grid(ngrid=40, shrink=False)

Display plots showing the current grid.

Parameters:
  • ngrid (int) – The number of grid nodes in each direction to include in the plot. The default is 40.
  • shrink – Display entire range of each axis if False; otherwise shrink range to include just the nodes being displayed. The default is False.
Nparam axes:

List of pairs of directions to use in different views of the grid. Using None in place of a direction plots the grid for only one direction. Omitting axes causes a default set of pairings to be used.

settings(ngrid=5)

Create string with information about grid nodes.

Creates a string containing the locations of the nodes in the map grid for each direction. Parameter ngrid specifies the maximum number of nodes to print (spread evenly over the grid).

Other Objects

class vegas.RunningWAvg

Running weighted average of Monte Carlo estimates.

This class accumulates independent Monte Carlo estimates (e.g., of an integral) and combines them into a single weighted average. It is derived from gvar.GVar (from the lsqfit module if it is present) and represents a Gaussian random variable.

mean

The mean value of the weighted average.

sdev

The standard deviation of the weighted average.

chi2

chi**2 of weighted average.

dof

Number of degrees of freedom in weighted average.

Q

Q or p-value of weighted average’s chi**2.

itn_results

A list of the results from each iteration.

add(g)

Add estimate g to the running average.

summary()

Assemble summary of independent results into a string.

class vegas.VecIntegrand

Base class for classes providing vectorized integrands.

A class derived from vegas.VecInterand should provide a __call__(x, f, nx) member where:

x[i, d] is a contiguous array where i=0...nx-1 labels different integrtion points and d=0... labels different directions in the integration space.

f[i] is a buffer that is filled with the integrand values for points i=0...nx-1.

nx is the number of integration points.

Deriving from vegas.VecIntegrand is the easiest way to construct integrands in Cython, and gives the fastest results.

Table Of Contents

Previous topic

How vegas Works