R: Add user-supplied distributions for use in NIMBLE BUGS models

registerDistributions {nimble} | R Documentation

## Add user-supplied distributions for use in NIMBLE BUGS models

### Description

Register distributional information so that NIMBLE can process user-supplied distributions in BUGS model code

### Usage

    registerDistributions(distributionsInput, userEnv = parent.frame(),
      verbose = nimbleOptions("verbose"))

### Arguments

`distributionsInput` | either a list or character vector specifying the user-supplied distributions. If a list, it should be a named list of lists in the form of that shown in `nimble:::distributionsInputList` with each list having required field `BUGSdist` and optional fields `Rdist`, `altParams`, `discrete`, `pqAvail`, `types`, and with the name of the list the same as that of the density function. Alternatively, simply a character vector providing the names of the density functions for the user-supplied distributions.
`userEnv` | environment in which to look for the nimbleFunctions that provide the distribution; this will generally not need to be set by the user as it will default to the environment from which this function was called.
`verbose` | logical indicating whether to print additional logging information

### Details

When `distributionsInput` is a list of lists, see below for more information on the structure of the list. When `distributionsInput` is a character vector, the distribution is assumed to be of standard form, with parameters assumed to be the arguments provided in the density nimbleFunction, no alternative parameterizations, and the distribution assumed to be continuous with range from minus infinity to infinity. The availability of distribution and quantile functions is inferred from whether appropriately-named functions exist in the global environment.

Finally, note that one no longer needs to explicitly call `registerDistributions` as it will be called automatically when the user-supplied distribution is used for the first time in BUGS code. However, if one wishes to provide alternative parameterizations, to provide a range, or to indicate a distribution is discrete, then one still must explicitly register the distribution using `registerDistributions` with the argument in the list format.

Format of the component lists when `distributionsInput` is a list of lists:

- `BUGSdist` a character string in the form of the density name (starting with 'd') followed by the names of the parameters in parentheses. When alternative parameterizations are given in `Rdist`, this should be an exhaustive list of the unique parameter names from all possible parameterizations, with the default parameters specified first.

- `Rdist` an optional character vector with one or more alternative specifications of the density; each alternative specification can be an alternative name for the density, a different ordering of the parameters, different parameter name(s), or an alternative parameterization. In the latter case, the character string in parentheses should provide a given reparameterization as comma-separated name = value pairs, one for each default parameter, where name is the name of the default parameter and value is a mathematical expression relating the default parameter to the alternative parameters or other default parameters. The default parameters should correspond to the input arguments of the nimbleFunctions provided as the density and random generation functions. The mathematical expression can use any of the math functions allowed in NIMBLE (see the User Manual) as well as user-supplied nimbleFunctions (which must have no setup code). The names of your nimbleFunctions for the distribution functions must match the function name in the `Rdist` entry (or if missing, the function name in the `BUGSdist` entry

- `discrete` a optional logical indicating if the distribution is that of a discrete random variable. If not supplied, distribution is assumed to be for a continuous random variable.

- `pqAvail` an optional logical indicating if distribution (CDF) and quantile (inverse CDF) functions are provided as nimbleFunctions. These are required for one to be able to use truncated versions of the distribution. Only applicable for univariate distributions. If not supplied, assumed to be FALSE.

- `altParams` a character vector of comma-separated 'name = value' pairs that provide the mathematical expressions relating non-canonical parameters to canonical parameters (canonical parameters are those passed as arguments to your distribution functions). These inverse functions are used for MCMC conjugacy calculations when a conjugate relationship is expressed in terms of non-default parameters (such as the precision for normal-normal conjugacy). If not supplied, the system will still function but with a possible loss of efficiency in certain algorithms.

- `types` a character vector of comma-separated 'name = input' pairs indicating the type and dimension of the random variable and parameters (including default and alternative parameters). 'input' should take the form 'double(d)' or 'integer(d)', where 'd' is 0 for scalars, 1 for vectors, 2 for matrices. Note that since NIMBLE uses doubles for numerical calculations and the default type is `double(0)`, one should generally use 'double' and one need only specify the type for non-scalars. 'name' should be either 'value' to indicate the random variable itself or the parameter name to indicate a given parameter.

- `range` a vector of two values giving the range of the distribution for possible use in future algorithms (not used currently). When the lower or upper limit involves a strict inequality (e.g., $x>0$), you should simply treat it as a non-strict inequality ($x>=0$, and set the lower value to 0). Also we do not handle ranges that are functions of parameters, so simply use the smallest/largest possible values given the possible parameter values. If not supplied this is taken to be `(-Inf, Inf)`.

### Author(s)

Christopher Paciorek

### Examples

    dmyexp <- nimbleFunction(
       run = function(x = double(0), rate = double(0), log = integer(0)) {
           returnType(double(0))
           logProb <- log(rate) - x*rate
           if(log) {
               return(logProb)
           } else {
               return(exp(logProb))
           }
       })
    rmyexp <- nimbleFunction(
       run = function(n = integer(0), rate = double(0)) {
           returnType(double(0))
           if(n != 1) nimPrint("rmyexp only allows n = 1; using n = 1.")
           dev <- runif(1, 0, 1)
           return(-log(1-dev) / rate)
       }
       )
    registerDistributions(list(
        dmyexp = list(
                  BUGSdist = "dmyexp(rate, scale)",
                  Rdist = "dmyexp(rate = 1/scale)",
                  altParams = "scale = 1/rate",
                  pqAvail = FALSE)))
    code <- nimbleCode({
        y ~ dmyexp(rate = r)
        r ~ dunif(0, 100)
    })
    m <- nimbleModel(code, inits = list(r = 1), data = list(y = 2))
    calculate(m, 'y')
    m$r <- 2
    calculate(m, 'y')
    m$resetData()
    simulate(m, 'y')
    m$y

    # alternatively, simply specify a character vector with the
    # name of one or more 'd' functions
    deregisterDistributions('dmyexp')
    registerDistributions('dmyexp')

    # or simply use in BUGS code without registration
    deregisterDistributions('dmyexp')
    m <- nimbleModel(code, inits = list(r = 1), data = list(y = 2))

    # example of Dirichlet-multinomial registration to illustrate
    # use of 'types' (note that registration is not actually needed
    # in this case)
    ddirchmulti <- nimbleFunction(
        run = function(x = double(1), alpha = double(1), size = double(0),
                       log = integer(0, default = 0)) {
            returnType(double(0))
            logProb <- lgamma(size) - sum(lgamma(x)) + lgamma(sum(alpha)) -
                sum(lgamma(alpha)) + sum(lgamma(alpha + x)) - lgamma(sum(alpha) +
                                                                     size)
            if(log) return(logProb)
            else return(exp(logProb))
        })

    rdirchmulti <- nimbleFunction(
        run = function(n = integer(0), alpha = double(1), size = double(0)) {
            returnType(double(1))
            if(n != 1) print("rdirchmulti only allows n = 1; using n = 1.")
            p <- rdirch(1, alpha)
            return(rmulti(1, size = size, prob = p))
        })

    registerDistributions(list(
        ddirchmulti = list(
            BUGSdist = "ddirchmulti(alpha, size)",
            types = c('value = double(1)', 'alpha = double(1)')
            )
        ))

[Package nimble version 0.9.1 Index]