DensityGrid#

class lintsampler.DensityGrid(edges, pdf, vectorizedpdf=False, pdf_args=(), pdf_kwargs={})#

Grid-like object over which density function is evaluated.

DensityGrid uses the parameter edges to construct a rectilinear grid. edges should contain one or k sequences of numbers representing the edges of a one- or k-dimensional grid. The given edges need not be evenly spaced, but should be monotonically increasing. After the grid is constructed, the given PDF function is evaluated on the vertices of the grid.

See the examples below for the various usage patterns.

Parameters:
edges1D iterable or tuple of 1D iterables

If a single 1D iterable (i.e., array, tuple, list of numbers), then this represents the cell edges of a 1D grid. So a 1D grid with N cells should have edges parameter as a 1D array with N+1 elements. If a tuple of 1D iterables, then this represents the edge values for a k-dimensional grid with dimensionality equal to the length of the tuple. So, a kD grid with (N1 x N2 x … x Nk) cells should take a length-k tuple, with arrays length N1+1, N2+1 etc.

pdffunction

Probability density function to evaluate on grid. Function should take coordinate vector (or batch of vectors if vectorized; see vectorizedpdf parameter) and return (unnormalised) density (or batch of densities). Additional arguments can be passed to the function via pdf_args and pdf_kwargs parameters.

vectorizedpdfbool, optional

if True, assumes that the pdf function is vectorized, i.e., it accepts (…, k)-shaped batches of coordinate vectors and returns (…)-shaped batches of densities. If False, assumes that the pdf function simply accepts (k,)-shaped coordinate vectors and returns single densities. Default is False.

pdf_argstuple, optional

Additional positional arguments to pass to pdf function; function call is pdf(position, *pdf_args, **pdf_kwargs). Default is empty tuple (no additional positional arguments).

pdf_kwargsdict, optional

Additional keyword arguments to pass to pdf function; function call is pdf(position, *pdf_args, **pdf_kwargs). Default is empty dict (no additional keyword arguments).

Examples

These examples demonstrate the various ways to set up and use an instance of DensityGrid.

  • A one-dimensional grid, spanning x=0 to x=10 with 32 cells (so 33 edges). As an example PDF we can take an unnormalised Gaussian:

    >>> pdf = lambda x: np.exp(-x**2)
    >>> g = DensityGrid(np.linspace(0, 30, 33), pdf)
    

    At this point, the object g has various attributes set up describing the grid and its geometry. We’ll save a demonstration of these for the next example, where they will be more interesting.

  • A two-dimensional grid, with 32 x 64 cells (so 33 gridlines along one axis and 65 along the other). For the PDF function, rather than writing our own we’ll use the bivariate standard normal PDF from scipy.stats.multivariate_normal:

    >>> pdf = multivariate_normal(mean=np.zeros(2), cov=np.eye(2)).pdf
    >>> x = np.linspace(-2, 2, 33)
    >>> y = np.linspace(-4, 4, 65)
    >>> g = DensityGrid((x, y), pdf)
    

    Let’s explore some of the attributes of g. First, some basic descriptors of the grid geometry:

    >>> g.dim
    2
    >>> g.shape
    (32, 64)
    >>> g.ncells
    2048
    

    There are also array attributes called mins and maxs which give the coordinates of the ‘first’ and ‘last’ corners of the grid. In 2D, these are the bottom-left and top-right:

    >>> g.mins
    array([  0., 100.])
    >>> g.maxs
    array([ 10., 200.])
    

    Meanwhile, edgearrays gives a list of the input edge arrays:

    >>> len(g.edgearrays)
    2
    >>> all(g.edgearrays[0] == x)
    True
    >>> all(g.edgearrays[1] == y)
    True
    

    There are also various attributes which relate to the evaluated probability densities and masses:

    >>> g.vertex_densities
    array([[7.22562324e-06, 1.18203307e-05, 1.90369817e-05, ...,
            1.90369817e-05, 1.18203307e-05, 7.22562324e-06],
           [9.20568282e-06, 1.50594920e-05, 2.42537439e-05, ...,
            2.42537439e-05, 1.50594920e-05, 9.20568282e-06],
           [1.15465131e-05, 1.88888347e-05, 3.04210101e-05, ...,
            3.04210101e-05, 1.88888347e-05, 1.15465131e-05],
           ...,
           [1.15465131e-05, 1.88888347e-05, 3.04210101e-05, ...,
            3.04210101e-05, 1.88888347e-05, 1.15465131e-05],
           [9.20568282e-06, 1.50594920e-05, 2.42537439e-05, ...,
            2.42537439e-05, 1.50594920e-05, 9.20568282e-06],
           [7.22562324e-06, 1.18203307e-05, 1.90369817e-05, ...,
            1.90369817e-05, 1.18203307e-05, 7.22562324e-06]])
    >>> g.masses
    array([[1.69184097e-07, 2.74103704e-07, 4.37229522e-07, ...,
            4.37229522e-07, 2.74103704e-07, 1.69184097e-07],
           [2.13673916e-07, 3.46183909e-07, 5.52206419e-07, ...,
            5.52206419e-07, 3.46183909e-07, 2.13673916e-07],
           [2.65695257e-07, 4.30466311e-07, 6.86647340e-07, ...,
            6.86647340e-07, 4.30466311e-07, 2.65695257e-07],
            ...,
           [2.65695257e-07, 4.30466311e-07, 6.86647340e-07, ...,
            6.86647340e-07, 4.30466311e-07, 2.65695257e-07],
           [2.13673916e-07, 3.46183909e-07, 5.52206419e-07, ...,
            5.52206419e-07, 3.46183909e-07, 2.13673916e-07],
           [1.69184097e-07, 2.74103704e-07, 4.37229522e-07, ...,
            4.37229522e-07, 2.74103704e-07, 1.69184097e-07]])
    >>> g.total_mass
    0.9541568382986452
    

    vertex_densities is a 2D (33 x 65) array containing the densities on all of the grid vertices, while masses is a 2D (32 x 64) array giving the probability masses of grid cells, and total_mass gives the total probability mass across the grid.

  • Things are slightly more efficient when the PDF function is vectorized, i.e., the PDF function takes a batch of input positions and returns a corresponding batch of densities. As it happens, the PDF function we used above is already nicely vectorized, so we can let DensityGrid know about this with a simple Boolean flag:

    >>> pdf = multivariate_normal(mean=np.zeros(2), cov=np.eye(2)).pdf
    >>> x = np.linspace(0, 10, 33)
    >>> y = np.linspace(100, 200, 65)
    >>> g = DensityGrid((x, y), pdf, vectorizedpdf=True)
    
  • It is also possible to construct a grid with just a single cell.

    In one dimension:

    >>> pdf = lambda x: np.exp(-x**2)
    >>> g = DensityGrid([-5, 5], pdf)
    >>> g.ncells
    1
    

    In multiple dimensions:

    >>> pdf = multivariate_normal(mean=np.zeros(2), cov=np.eye(2)).pdf
    >>> g = DensityGrid(([0, 10], [100, 200]), pdf)
    >>> g.ncells
    1
    
  • Example usage of choose_cells method

    The choose_cells method can be used to randomly select grid cells (weighted by their masses), given a 1D array of uniform samples.

    >>> pdf = multivariate_normal(mean=np.zeros(2), cov=np.eye(2)).pdf
    >>> x = y = np.linspace(-3, 3, 129)
    >>> g = DensityGrid((x, y), pdf, vectorizedpdf=True)
    >>> u = np.random.default_rng().uniform(size=10)  
    >>> mins, maxs, corners = g.choose_cells(u)
    

    Let’s inspect the returned arrays:

    >>> mins
    array([[ 1.546875,  0.703125],
           [-0.234375, -0.234375],
           [ 0.      ,  0.84375 ],
           [ 1.03125 ,  1.359375],
           [-0.9375  ,  0.      ],
           [ 0.09375 , -0.9375  ],
           [-1.453125, -0.046875],
           [-0.890625, -0.84375 ],
           [-1.265625, -0.515625],
           [-1.3125  , -0.84375 ]])
    >>> maxs
    array([[ 1.59375 ,  0.75    ],
           [-0.1875  , -0.1875  ],
           [ 0.046875,  0.890625],
           [ 1.078125,  1.40625 ],
           [-0.890625,  0.046875],
           [ 0.140625, -0.890625],
           [-1.40625 ,  0.      ],
           [-0.84375 , -0.796875],
           [-1.21875 , -0.46875 ],
           [-1.265625, -0.796875]])
    >>> corners
    (array([0.03757259, 0.15064809, 0.11148847, 0.03712126, 0.10255765,
            0.10210795, 0.0553122 , 0.074987  , 0.06255463, 0.04711508]),
     array([0.0363145 , 0.15214504, 0.1070474 , 0.03479141, 0.10244504,
            0.10657801, 0.055373  , 0.07792656, 0.06401463, 0.04896204]),
     array([0.03490626, 0.15214504, 0.11136605, 0.03533066, 0.1070474 ,
            0.10154859, 0.05914606, 0.07809798, 0.06630517, 0.05004977]),
     array([0.03373746, 0.15365686, 0.10692986, 0.0331132 , 0.10692986,
            0.10599417, 0.05921108, 0.0811595 , 0.0678527 , 0.05201177]))
    

    mins and maxs are both arrays shaped (10, 2), 10 because we fed in 10 uniform samples and 2 because we have a 2D grid (and bivariate PDF). corners is a length-4 tuple of length-10 arrays. 4 because each of the 10 cells has 4 corners. So, the first array contains the densities at the 00-corner of each cell, the second array contains the 01-densities, the third array gives the 10-densities, and the fourth array gives the 11-densities.

Attributes:
minsnumpy array

Minimum boundary values (i.e., first corner) of the structure.

maxsnumpy array

Maximum boundary values (i.e., last corner) of the structure.

dimint

Dimension of the density structure.

total_massfloat

Total probability mass of the structure.

shapetuple

dim-length tuple giving grid shape. For example, if edgearrays have lengths N1+1, N2+1, … Nk+1, then shape is (N1, N2, …, Nk).

ncellsint

Total number of cells in grid, i.e., the product over the shape tuple.

edgearrayslist

Length dim list of arrays of grid edges along each dimension.

vertex_densitiesnumpy array

k-dimensional array giving densities at vertices of grid. Shape is (N1+1, N2+1, …) if grid has (N1, N2, …) cells along each dimension.

massesnumpy array

k-dimensional array of probability masses of grid cells. Shape is equal to grid shape (shape attribute). Masses are calculated according to trapezoid rule, i.e., cell volumes multiplied by average vertex densities.

Methods

choose_cells(u)

Choose cells given 1D array of uniform samples.

choose_cells(u)#

Choose cells given 1D array of uniform samples.

Method enforced by base class DensityStructure.

Parameters:
u1D array of floats, shape (N,)

Array of uniform samples ~ U(0, 1).

Returns:
mins2D array of floats, shape (N, k)

Coordinate vector of first corner of each cell.

maxs2D array of floats, shape (N, k)

Coordinate vector of last corner of each cell.

corners2^k-tuple of 1D arrays, each length N

Densities at corners of given cells. Conventional ordering applies, e.g., in 3D: (f000, f001, f010, f011, f100, f101, f110, f111)

property dim#

Dimension of the density structure.

Returns:
int

The dimension of the structure.

property maxs#

Maximum boundary values (i.e., last corner) of the structure.

Returns:
array_like

1D array, length-k, giving the coordinates of the last corner of the k-dimensional structure.

property mins#

Minimum boundary values (i.e., first corner) of the structure.

Returns:
array_like

1D array, length-k, giving the coordinates of the first corner of the k-dimensional structure.

property total_mass#

Total probability mass of the structure.

Returns:
float

The total probability mass, summed over all the cells of the structure.