Differences From Other BUGS Implementations

There exist many implementations of BUGS, notably WinBUGS, OpenBUGS, MultiBUGS, JAGS, and nimble.

This section aims to outline some differences between JuliaBUGS and other BUGS implementations. This comparison is not exhaustive, and we welcome any further discussion and reports on the matter.

Use of generaic function in distribution functions

In WinBUGS, OpenBUGS, and MultiBUGS, the arguments to distribution functions are typically restricted to variables or constants, not general expressions. JuliaBUGS, however, allows for more flexibility in these arguments.

For example, the following expressions are allowed in all BUGS implementations, including JuliaBUGS (assuming y = [1, 2, 3]):

model {
 x ~ dnorm(y[y[2]], 1)
}

model {
  x ~ dnorm(y[y[2]+1], 1)
}

However, JuliaBUGS allows more flexibility in these arguments. The following expressions, which are not allowed in traditional BUGS implementations, are permitted in JuliaBUGS:

model {
 x ~ dnorm(y[1] + 1, 1)
}

model {
 x ~ dnorm(sum(y[1:2]), 1)
}

model {
 x ~ dnorm(y[sum(y[1:2])], 1)
}

cumulative, density, and deviance Functions

In OpenBUGS, there are several functions for working with distributions:

  • cumulative(s1, s2): Computes the tail area (cumulative distribution function) of the distribution of s1 up to the value of s2. s1 must be a stochastic node, and s1 and s2 can be the same.
  • density(s1, s2): Computes the density function of the distribution of s1 at the value of s2. s1 must be a stochastic node supplied as data, and s1 and s2 can be the same.
  • deviance(s1, s2): Computes the deviance of the distribution of s1 at the value of s2. s1 must be a stochastic node supplied as data, and s1 and s2 can be the same.

In MultiBUGS, these functions have been replaced with the cdf.dist, pdf.dist, and dev.dist family of functions.

In JuliaBUGS, we don't have these functions directly, but similar functionality can be achieved using the Distributions.jl package:

  • The cdf function computes the cumulative distribution function of a given univariate distribution at a specified value.
  • The pdf function computes the probability density function of a given univariate distribution at a specified value.
  • JuliaBUGS does not currently support a deviance function equivalent to the one in OpenBUGS.

Example

The cdf and pdf functions from the Distributions.jl are simple to use: the first argument is the distribution, and the second argument is the value at which to evaluate the function.

An OpenBUGS program like

model {
    x ~ dnorm(0, 1)
    cumulative.x = cumulative(x, x)
}

will need to be rewritten to:

@bugs begin
    x ~ Normal(0, 1)
    cumulative_x = cdf(Normal(0, 1), x)
end

Use : for slicing when using Julia Syntax

In the original BUGS language, slicing is performed using syntax like x[, ], which selects all elements from both the first and second dimensions.

The @bugs macro will automatically insert a : when given x[], however, Julia parser will throw an error if it encounters x[, ], so when using the @bugs macro in JuliaBUGS, users must explicitly use the Colon (:) operator for slicing. For example, to select all elements from both dimensions of an array x, you would write x[:, :].

BUGS supports four link functions: log, logit, cloglog, and probit. These functions are used to support Generalized Linear Models and, in some cases, to transform random variables with constrained support to the real line.

For instance, the Seeds example features logistic regression, and the model definition is

model
{
    for( i in 1 : N ) {
        r[i] ~ dbin(p[i],n[i])
        beta[i] ~ dnorm(0.0,tau)
        logit(p[i]) <- alpha0 + alpha1 * x1[i] + alpha2 * x2[i] + alpha12 * x1[i] * x2[i] + beta[i]
    }
    alpha0 ~ dnorm(0.0,1.0E-6)
    alpha1 ~ dnorm(0.0,1.0E-6)
    alpha2 ~ dnorm(0.0,1.0E-6)
    alpha12 ~ dnorm(0.0,1.0E-6)
    sigma ~ dunif(0,10)
    tau <- 1 / pow(sigma, 2)
}

JuliaBUGS inherits these functions, but it's important to note that the link function syntax is not supported when using the Julia-like syntax. The reason for this is that Julia uses the syntax f(...) = ... to define functions, and the link function syntax can be confusing in the Julia context.

Instead, users are advised to use the inverse functions of these link functions by calling them on the right-hand side (RHS) of the statement. The inverse functions are:

  • logexp
  • logitlogistic
  • cloglogcloglog
  • probitprobit

So the above model should be rewritten as

@bugs begin
    for i in 1:N
        r[i] ~ dbin(p[i], n[i])
        b[i] ~ dnorm(0.0, tau)
        p[i] = logistic(alpha0 + alpha1 * x1[i] + alpha2 * x2[i] + alpha12 * x1[i] * x2[i] + b[i])
    end
    alpha0 ~ dnorm(0.0, 1.0e-6)
    alpha1 ~ dnorm(0.0, 1.0e-6)
    alpha2 ~ dnorm(0.0, 1.0e-6)
    alpha12 ~ dnorm(0.0, 1.0e-6)
    tau ~ dgamma(0.001, 0.001)
    sigma = 1 / sqrt(tau)
end

(When the program is in the original BUGS syntax, the link function syntax is supported.)

It's also worth noting that JuliaBUGS uses Bijectors.jl to handle constrained parameters.

Compare with nimble

In the BUGS language, link functions are only supported in logical assignments. However, nimble extends this functionality by allowing link functions to be used in stochastic assignments as well. nimble will creates new node as intermediate variables. JuliaBUGS doesn't currently support this syntax.