clProbDist
An OpenCL library for probability distributions
Introduction

We introduce clProbDist, an OpenCL library for probability distributions. It provides facilities for evaluating probability density or mass functions, distribution functions and their inverses, and reliability functions, currently for five distributions: normal, lognormal, exponential, gamma and Poisson. Applications include nonuniform variate generation, computing confidence intervals, computing \(p\)-values for statistical tests, computing densities to evaluate the likelihood function of a sample, etc.

This library, in its current state, defines a framework for working with probability distributions in OpenCL, and to show some working examples. Dozens of additional distributions need to be contributed to clProbDist to make it a mature library.

clProbDist allows users to generate nonuniform variates (see Example 2: non-uniform variate generation for illustration) through inversion only. Other (possibly faster) methods, including acceptance-rejection based approaches, need to be implemented somewhere else. The inversion method relies on evaluating the inverse cumulative distribution function (CDF) of a given distribution. For certain distributions, it may be profitable to compute lookup arrays in advance and use them to evaluate the inverse CDF repeatedly with the same parameters, as when generating many random variates from the same distribution. clProbDist provides facilities to do so (see Example 3: non-uniform variate generation revisited for an example).

What to read next?

Basic usage

Distributions, prefixes and headers

The API is almost the same for every distribution. It is described in detail in clProbDist_template.h. Each distribution has its own header file (which is normally the lowercase name of the distribution, with a .h or .clh extension on the host or device, respectively), and each function contains the name of the distribution in its prefix (listed in Implemented distributions). In the generic probability distribution API reference, the distribution-specific part of the prefix is not shown.

For example, functions from the Normal distribution API have names that begin with clprobdistNormal and are declared in the normal.h header file. In particular, the probability density function clprobdistDensity(), the cumulative distribution function clprobdistCDF() and its inverse clprobdistInverseCDF() from the generic API, expand to clprobdistNormalDensity(), clprobdistNormalCDF() and clprobdistNormalInverseCDF(), respectively, for the normal distribution. More concretely, to store in p the probability density of x in a normal distribution of mean \(\mu\) = mu and standard deviation \(\sigma\) = sigma, one would invoke

cl_double p = clprobdistNormalDensity(mu, sigma, x, NULL);

Note that x, mu and sigma in the above are all floating-point values of type cl_double. The last argument can be used for reporting errors, but errors are ignored here by passing NULL.

Example 1: computing a confidence interval

The following code defines a function (on the host) that computes a normal confidence interval of level level on the mean of a set of n observations of type cl_double stored in the array observations. This assumes, of course, that the mean is normally distributed. The lower and upper bounds are returned in the lower and upper variables, respectively:

#include <math.h> // for sqrt()
void normalConfidenceInterval(cl_int n, cl_double* observations, cl_double level,
cl_double* lower, cl_double* upper)
{
// compute sample average and variance
cl_double sum = 0.0;
cl_double sum_squares = 0.0;
for (size_t i = 0; i < n; i++) {
sum += observations[i];
sum_squares += observations[i] * observations[i];
}
cl_double average = sum / n;
cl_double variance = (sum_squares - average * sum) / (n - 1);
// compute normal distribution parameters
cl_double mu = average;
cl_double sigma = sqrt(variance / n);
// compute confidence interval
*lower = clprobdistNormalInverseCDF(mu, sigma, 0.5 * (1.0 - level), NULL);
*upper = clprobdistNormalInverseCDF(mu, sigma, 0.5 * (1.0 + level), NULL);
}
[note] The first part of normalConfidenceInterval() simply computes the sample average and variance of the observations. The second part illustrates how to evaluate the inverse normal distribution function by invoking clprobdistNormalInverseCDF() with the normal distribution parameters \(\mu\)=mu and \(\sigma\)=sigma (the mean and variance) as its first arguments, for two different quantiles. The last argument is used for reporting errors, which are ignored here by setting it to NULL.

A more efficient implementation would compute the half width of the interval using a single call to clprobdistStdNormalInverseCDF() from the Standard normal distribution API. The standard normal distribution always has zero mean and a unit variance, so functions from its API do not take any distribution parameters. With this, we could replace the last four instructions of the normalConfidenceInterval defined above with:

cl_double half_width = sqrt(variance / n) *
clprobdistStdNormalInverseCDF(0.5 * (1.0 + level), NULL);
*lower = average - half_width;
*upper = average + half_width;

The complete code for this example can be found in DocsTutorial/example1.c.

Example 2: non-uniform variate generation

In this example, we want to simulate a stochastic system (not completely specified as our focus here is the usage of clProbDist) that uses a number, say 100, of Poisson variates, all with the same mean lambda, as its input. To generate a Poisson variate with the inversion method, we first generate a pseudorandom number uniformly distributed in (0,1) using the MRG31k3p generator from the clRNG library, and we map the output through clprobdistPoissonInverseCDF(). (The user is referred to the clRNG documentation and clRNG tutorial document for instructions on random streams usage.) For simplicity, we assume that the Poisson variates are used sequentially, one at a time, so we do not need to store them all at once in advance. Again, we run everything on the host. The code to average 1024 realizations of the output of this stochastic system would look like this:

#include <clRNG/mrg31k3p.h>
cl_double simulateOneRun(cl_double lambda, clrngMrg31k3pStream* stream)
{
cl_double output = 0.0;
for (int i = 0; i < 100; i++) {
cl_double u = clrngMrg31k3pRandomU01(stream);
cl_int poisson_variate = clprobdistPoissonInverseCDF(lambda, u, NULL);
// modify output using poisson_variate
output += poisson_variate;
}
return output;
}
int main()
{
// prepare
int replications = 1024;
cl_double lambda = 50.0;
clrngMrg31k3pStream* stream = clrngMrg31k3pCreateStreams(NULL, 1, NULL, NULL);
// simulate
cl_double sum = 0.0;
for (int i = 0; i < replications; i++)
sum += simulateOneRun(lambda, stream);
printf("The average output is %.3f\n", sum / replications);
// clean up
clrngMrg31k3pDestroyStreams(stream);
return 0;
}

Here, the output of simulateOneRun() is simply the sum of the 100 Poisson variates. In practice, it would be more elaborate, but the purpose of this example is to show how to generate nonuniform variates. The call to clprobdistPoissonInverseCDF(lambda, u, NULL) evaluates the inverse Poisson distribution function with mean lambda at quantile u, ignoring errors.

The complete code for this example can be found in DocsTutorial/example2.c.

The above code generates 1024 × 100 Poisson variates with the same mean, and can be very inefficient when lambda is large. A faster approach makes use of a lookup array to avoid repeating costly computations. Such facility is provided by a selection of functions of the Poisson distribution API, illustrated in Example 3: non-uniform variate generation revisited below. For this purpose, we introduce Distribution objects.

Distribution objects

The API of each distribution comes in two variants. One uses distribution parameters as the first arguments of the functions; the other replaces the distribution parameters with a distribution object, and appends WithObject to the function names. [note]

The type of a distribution object is specific to the distribution it represents, e.g., the Poisson distribution has objects of type clprobdistPoisson. A distribution object stores the parameters of the distribution it represents, and, for some distributions, it may also store additional data.

In particular, for discrete distributions such as the Poisson one, the distribution object also stores lookup arrays to speed up the evaluation of the probability, of the distribution function and its inverse, and of the reliability function. Building the lookup arrays cost some time, so it is advisable to use a distribution object especially when these functions need to be evaluated many times. Before before using a distribution object with these functions, it must be created using clprobdistCreate(); it can be destroyed with clprobdistDestroy().

For other distributions, it should make very little difference in terms of performance which variant of the distribution API is used (with distribution objects or distribution parameters).

In the generic probability distribution API reference, the generic type name for a distribution object is clprobdistObject.

Example 3: non-uniform variate generation revisited

Here, we modify Example 2: non-uniform variate generation to use a distribution object in order to accelerate the multiple evaluations of the inverse distribution function. First, simulateOneRun() needs to receive a distribution object instead of the mean of the Poisson distribution, so its signature becomes

cl_double simulateOneRun(const clprobdistPoisson* dist, clrngMrg31k3pStream* stream)

Next, we replace the call to clprobdistPoissonInverseCDF() to its variant that takes a distribution object:

cl_int poisson_variate = clprobdistPoissonInverseCDFWithObject(dist, u, NULL);

Finally, we create the distribution object with:

clprobdistPoisson* dist = clprobdistPoissonCreate(50.0, NULL, NULL);

and pass it to simulateOneRun() instead of the value of the mean:

sum += simulateOneRun(dist, stream);

The first argument to clprobdistPoissonCreate() is the value of distribution parameter \(\lambda\). For distributions with multiple parameters, their values would be listed as the first arguments to clprobdistCreate() (expanded to the proper name for the distribution). For instance, to create a distribution object for the normal distribution with mean mu and standard deviation sigma, we would call:

clprobdistNormal* dist = clprobdistNormalCreate(mu, sigma, NULL, NULL);

The last two arguments to clprobdistPoissonCreate(), or to clprobdistCreate() in general, can be used to return the size of the created distribution object and the error status. Here, we ignore them by setting them to NULL.

The complete code for this example can be found in DocsTutorial/example3.c.

Usage from device code

Using clProbDist from device code is very similar to using it from host code. The device API variant that uses distribution parameters is the same as the host API. For example, to store in p the probability density of x in a normal distribution of mean \(\mu\) = mu and standard deviation \(\sigma\) = sigma, one would include the header file normal.clh and invoke:

double p = clprobdistNormalDensity(mu, sigma, x);

Here, x, mu and sigma are all floating-point values of type double.

The device API has no functions to create or destroy distribution objects. Besides that, it is almost the same as the host API. Here is an example kernel that receives a normal distribution object and uses it to invoke clprobdistNormalDensity():

__kernel void my_kernel(__global const clprobdistNormal* dist)
{
double p = clprobdistNormalDensity(dist, 8.0, NULL);
...
}

Again, the last argument to clprobdistNormalDensity(), that can be used for error reporting but is ignored here by setting it to NULL. It is possible to store the distribution objects into other memory types than global memory, but this requires additional instructions in the host and kernel codes. This is discussed in Device memory types.

Example 4: Poisson variate generation on the device

Here, we adapt Example 3: non-uniform variate generation revisited, which runs on the host, to run on the device. First, we translate the computation code into device code, by replacing the extensions of the header names with .clh and by replacing cl_double with double, and by writing a kernel to receive data from the host, call simulateOneRun() and send data back to the host:

#include <clRNG/mrg31k3p.clh>
double simulateOneRun(__global const clprobdistPoisson* dist, clrngMrg31k3pStream* stream)
{
double output = 0.0;
for (int i = 0; i < 100; i++) {
double u = clrngMrg31k3pRandomU01(stream);
int poisson_variate = clprobdistPoissonInverseCDFWithObject(dist, u, NULL);
// modify output using poisson_variate
...
}
return output;
}
__kernel void my_kernel(
__global const clprobdistPoisson* dist,
__global const clrngMrg31k3pHostStream* host_streams,
__global double* output)
{
size_t gid = get_global_id(0);
clrngMrg31k3pStream stream;
clrngMrg31k3pCopyOverStreamsFromGlobal(1, &stream, &host_streams[gid]);
output[gid] = simulateOneRun(dist, &stream);
}

The __global OpenCL C keyword in front of const clprobdistPoisson* indicates that the Poisson distribution object is stored in the device's global memory.

The host code is responsible for creating the distribution object and sending it into the device's memory. The latter requires the size of the object to be known, so we allow clprobdistPoissonCreate() to return it in its second argument (dist_buf_size):

size_t dist_buf_size;
clprobdistPoisson* dist = clprobdistPoissonCreate(50.0, &dist_buf_size, NULL);

After this instruction, dist_buf_size contains the size in bytes of the distribution object pointed to by dist. The size of the array is determined at runtime; it may vary depending on the value of the distribution parameter \(\lambda\), set to 50.0 here. An OpenCL buffer must be created to copy the distribution object into the device's memory:

cl_mem dist_buf = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
dist_buf_size, dist, NULL);

In the above code snippet, the context variable is an OpenCL context. Finally, the buffer must be set as the first argument of the kernel (stored in the kernel variable):

clSetKernelArg(kernel, 0, sizeof(dist_buf), &dist_buf);

The complete code for this example can be found in DocsTutorial/example4.c and DocsTutorial/example4_kernel.cl.

Configuration

Environment variables

For all features of the library to work properly, the CLPROBDIST_ROOT environment variable must be set to point to the installation path of the clProbDist package, that is, the directory under which lies the include/clProbDist subdirectory. Means of setting an environment variable depend on the operating system used.

Device memory types

The API of each distribution assumes that the distribution objects are stored in a specific type of memory. It defaults to global memory, but can be customized by the user by changing the value of the preprocessor symbol CLPROBDIST_<DIST>_OBJ_MEM, where <DIST> is the uppercase name of the distribution, before including the device header file of the distribution, to one of the following values:

For example, to store exponential distribution objects in constant memory, the device code should simply begin with:

#define CLPROBDIST_EXPONENTIAL_OBJ_MEM CLPROBDIST_MEM_TYPE_CONSTANT
#include <clProbDist/exponential.clh>

For other memory types than global and constant memory, the device API provides a clprobdistCopyOverFromGlobal() function, where the clprobdist prefix has to be expanded with the distribution name as explained in Distributions, prefixes and headers. We give examples below of how to use this function.

To store exponential distribution objects in private memory instead, in addition to replacing CLPROBDIST_MEM_TYPE_CONSTANT with CLPROBDIST_MEM_TYPE_PRIVATE, each work item needs to make a private copy of the global distribution object and use the address of that copy with API calls, e.g.,

#define CLPROBDIST_EXPONENTIAL_OBJ_MEM CLPROBDIST_MEM_TYPE_PRIVATE
__kernel void example(__global const clprobdistExponential* gdist)
{
// allocate private storage for distribution object
// copy distribution object from global memory to private memory
// following API calls should use &pdist instead of gdist
...
}

To store distribution objects in local memory, the user must first reserve local memory from the host code. For example, with a Poisson distribution object, the host code would include something like the following:

// create a Poisson distribution object
clprobdistPoisson* dist = clprobdistPoissonCreate(50.0, NULL, NULL);
// create OpenCL buffer for distribution object
size_t dist_size;
dist_buf = clCreateBuffer(context,
CL_MEM_HOST_WRITE_ONLY | CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
dist_size, dist, &err);
// send the distribution object in global memory
clSetKernelArg(kernel, 0, sizeof(dist_buf), &dist_buf);
// allocate local memory to allow the device to copy the distribution object
clSetKernelArg(kernel, 1, dist_size, NULL);

In the kernel, the distribution object must be copied from global memory to local memory, which is shared by all work items from the same work group. Perhaps the simplest way to do so is to have the first work item of each work group do the copy, e.g.,

#define CLPROBDIST_POISSON_OBJ_MEM CLPROBDIST_MEM_TYPE_LOCAL
__kernel void example(__global const clprobdistPoisson* gdist,
__local clprobdistPoisson* ldist)
{
int gid = get_global_id(0);
if (get_global_id(0) % get_local_size(0) == 0)
// copy distribution object from global memory to local memory
// wait for all work items of the work group to be ready
barrier(CLK_LOCAL_MEM_FENCE);
// following API calls should use ldist instead of gdist
...
}

This is substantially more complicated than using global memory. It may be faster or slower depending on the distribution and its parameters, the OpenCL hardware and the vendor implementation of OpenCL.

Implemented distributions

The following table lists the distributions that are currently implemented in clProbDist with the type of their support (continuous or discrete), the prefix for functions of the distribution API, and the name of the corresponding header file on the host and on the device.

Distribution Support Type Prefix Host Header File Device Header File
Normal continuous clprobdistNormal normal.h normal.clh
Standard Normal continuous clprobdistStdNormal normal.h normal.clh
Lognormal continuous clprobdistLognormal lognormal.h lognormal.clh
Exponential continuous clprobdistExponential exponential.h exponential.clh
Gamma continuous clprobdistGamma gamma.h gamma.clh
Poisson discrete clprobdistPoisson poisson.h poisson.clh

The API of continuous distributions provide a clprobdistDensity() function to compute the probability density; for discrete distributions, it is replaced by a clprobdistProb() function that computes the probability mass.

The common API for all distributions, in a generic form, is given in clProbDist_template.h. Some parts of the API vary across distributions, for instance, the type of return value of the inverse distribution function: cl_double for continuous distributions and cl_int for discrete distributions. This is in fact the type of the random variable described by the distribution, and its programmatic type is denoted by vartype in the generic API. Also, the number of distribution parameters vary, as well as their individual types and names. The token DIST_PARAMS substitutes for the list of distribution parameters in the generic API. The table below explicits the expansion of vartype and DIST_PARAMS for implemented distributions. The meaning of the distribution parameters is given below for each individual distribution.

Distribution vartype DIST_PARAMS
Normal cl_double cl_double \(\mu\), cl_double \(\sigma\)
Standard Normal cl_double none
Lognormal cl_double cl_double \(\mu\), cl_double \(\sigma\)
Exponential cl_double cl_double \(\lambda\) cl_double
Gamma cl_double cl_double \(\alpha\), cl_double \(\lambda\), cl_int \(d\)
Poisson cl_int cl_double \(\lambda\)

In addition to the functions listed with generic API names in clProbDist_template.h, each parameterized distribution exposes functions to retrieve the values of the parameters a distribution object was created with.

Normal distribution

With mean \(\mu\) and variance \(\sigma^2\), where \(\sigma > 0\), its density function is

\[ f(x) = \frac{1}{\sqrt{2 \pi \sigma^2}} \exp\left[ \frac{-(x-\mu)^2}{2\sigma^2} \right] \qquad (-\infty < x < \infty). \]

For the normal distribution, DIST_PARAMS expands to:

cl_double mu, cl_double sigma

where mu \(=\mu\) and sigma \(=\sigma\). The API of the normal distribution adds the functions clprobdistNormalGetMu() and clprobdistNormalGetSigma() to retrieve the value of these parameters.

The CDF is evaluated using Uses the Chebyshev approximation proposed in [6], which gives 16 decimals of precision. The inverse CDF is computed using different rational Chebyshev approximations [2], which give 16 decimal digits of precision for \(2.2 \times 10^{-308} < u < 1\).

The complete API of the normal distribution is described in normal.h.

Standard normal distribution

This special case of the normal distribution with \(\mu = 0\) and \(\sigma = 1\) has distribution function

\[ F(x) = \Phi(x) = \frac{1}{\sqrt{2 \pi}} \int_{-\infty}^x e^{-t^2/2} \,dt \qquad (-\infty < x < \infty). \]

For the standard normal distribution, there are no free parameters, so DIST_PARAMS expands to nothing.

The complete API of the standard normal distribution is also described in normal.h.

Lognormal distribution

With scale parameter \(\mu\) and shape parameter \(\sigma^2\), where \(\sigma > 0\), its density function is

\[ f(x) = \frac{1}{\sqrt{2 \pi} \sigma x} \exp\left[ \frac{-(\ln x - \mu)^2}{2\sigma^2} \right] \qquad (0 < x < \infty). \]

Its distribution function is

\[ F(x) = \Phi((\ln x - \mu) / \sigma) \qquad (0 < x < \infty), \]

where \(\Phi\) is the distribution function for the Standard normal distribution. This inverse distribution function is given by:

\[ F^{-1}(u) = \exp\left[ \mu + \sigma\Phi^{-1}(u) \right] \qquad (0 \leq u < 1). \]

If \(\ln Y\) has a normal distribution, then \(Y\) has a lognormal distribution with the same parameters.

The CDF and inverse CDF are evaluated using the implementations for the normal distribution.

For the lognormal distribution, DIST_PARAMS expands to:

cl_double mu, cl_double sigma

where mu \(=\mu\) and sigma \(=\sigma\). The API of the normal distribution adds the functions clprobdistLognormalGetMu() and clprobdistLognormalGetSigma() to retrieve the value of these parameters.

The complete API of the normal distribution is described in lognormal.h.

Exponential distribution

With mean \(1/\lambda\), where \(\lambda > 0\), its density, distribution and inverse distribution functions are, respectively,

\begin{align} f(x) &= \lambda e^{-\lambda x} & (x \geq 0) \\ F(x) &= 1 - e^{-\lambda x} & (x \geq 0) \\ F^{-1}(x) &= -\lambda^{-1} \ln(1 - u) & (0 \leq u < 1). \end{align}

The inverse CDF is computed exactly with the above formula.

For the exponential distribution, DIST_PARAMS expands to:

cl_double lambda

where lambda \(=\lambda\). The API of the exponential distribution adds the function clprobdistExponentialGetLambda() retrieve the value of this parameter.

The complete API of the exponential distribution is described in exponential.h.

Gamma distribution

With shape parameter \(\alpha > 0\) and scale parameter \(\lambda > 0\), its density function is

\[ f(x) = \frac{\lambda^\alpha x^{\alpha - 1} e^{\lambda x}}{\Gamma(\alpha)} \qquad (x > 0), \]

where

\[ \Gamma(\alpha) = \int_0^\infty x^{\alpha - 1} e^{-x} \,dx \]

is the usual gamma function. In particular, \(\Gamma(n) = (n - 1)!\) when \(n\) is a positive integer.

Several functions of the API for the gamma distribution are evaluated using an approximation with a precision of roughly \(d\) decimal digits, where \(d\) can be specified by the user. For consistency, \(d\) must always be specified, but is ignored by clprobdistGammaDensity(), clprobdistGammaMean(), clprobdistGammaStdDeviation() and clprobdistGammaVariance().

The CDF is approximated with an improved version of the algorithm in [1] . The functions clprobdistGammaCDF() and clprobdistGammaCDFWithObject() try to return \(d\) decimals digits of precision. For \(\alpha\) not too large (e.g., \(\alpha \leq 1000\)), \(d\) gives a good idea of the precision attained. The inverse CDF \(F^{-1}(u)\) is approximated by solving \(F(x) - u = 0\) for \(x\) with the bisection method when \(u \leq 10^{-8}\) or \(\alpha \leq 1.5\), or with the Brent-Dekker method (see [3] and [4]) otherwise.

For the gamma distribution, DIST_PARAMS expands to:

cl_double alpha, cl_double lambda, cl_int decprec

where alpha \(=\alpha\), lambda \(=\lambda\) and decprec \(=d\). The API of the gamma distribution adds the functions clprobdistGammaGetAlpha() and clprobdistGammaGetLambda() to retrieve the value of the first two parameters.

The complete API of the gamma distribution is described in gamma.h.

Poisson distribution

With mean \(\lambda \geq 0\), its mass and distribution functions are, respectively,

\begin{align} p(x) &= \frac{e^{-\lambda} \lambda^x}{x!} & (x = 0, 1, \ldots) \\ F(x) &= e^{-\lambda} \sum_{j=0}^x \frac{\lambda^j}{j!} & (x = 0, 1, \ldots) \end{align}

When \(\lambda > 200\), to compute \(F(x)\) and \(\bar F(x)\), clprobdistPoissonCDF() and clprobdistPoissonComplCDF() exploit the relationship \(F(x) = 1 - G_{x+1}(\lambda)\), where \(G_{x+1}\) is the gamma distribution function with parameters \((\alpha, \lambda) = (x + 1, 1)\). Otherwise, the probabilities are summed term by term. To evaluate the inverse CDF \(F^{-1}(u)\), clprobdistPoissonInverseCDF() performs a sequential search for the smallest value of \(x\) such that \(F(x) \geq u\), by incrementing \(x\) starting from \(x = 0\). When \(\lambda\) is very large, the probabilities \(p(x)\) are empirically negligible when \(x\) is far from \(\lambda\). So, when \(\lambda \geq 700\), the implementation of clprobdistPoissonInverseCDF() starts with a binary search for the lower boundary \(x_{\mathrm{min}} \in \{0,\dots,\lambda\}\) of an interval that contains the non-negligible probability values. Then, the sequential search begins at \(x = x_{\mathrm{min}}\).

If one has to compute \(p(x)\) or \(F(x)\) for several values of \(x\) with the same \(\lambda\), where \(\lambda\) is not too large, then it is more efficient to instantiate an object and use the API variant that uses distribution objects of type clprobdistPoisson, e.g., clprobdistPoissonProbWithObject() and clprobdistPoissonCDFWithObject(), for which return values are computed in advance and stored in lookup arrays. Probabilities smaller than \(10^{-16}\) are not stored in the Poisson distribution object, but are computed directly each time they are needed (which should be very seldom). The inverse CDF is evaluated by clprobdistPoissonInverseCDFWithObject() with a binary search.

For the Poisson distribution, DIST_PARAMS expands to:

cl_double lambda

where lambda \(=\lambda\). The API of the exponential distribution adds the function clprobdistPoissonGetLambda() retrieve the value of this parameter.

The complete API of the Poisson distribution is described in poisson.h. The device API expects Poisson distribution objects to be stored into global memory by default. See Device memory types for information on using other types of memory.

WARNING: The complementary distribution function, which can be evaluated with clprobdistPoissonComplCDF() and clprobdistPoissonComplCDFWithObject(), is defined as \(\bar F(j) = \mathbb P[X \geq j]\) (for integers \(j\)), so that for the Poisson distribution clProbDist, \(F(j) + \bar F(j) \neq 1\) since both include the term \(\mathbb P[X = j]\).