Pumps: Conjugate Gamma-Poisson Hierarchical Model
This example concerns failure records for 10 power plant pumps. For each pump we know how long it operated (in thousands of hours) and how many failures it experienced over that period. The question is how to estimate a failure rate for each individual pump: some pumps ran for only about a thousand hours, so their raw failure counts are very noisy on their own. A hierarchical model answers this by assuming the pump-specific failure rates are drawn from a common gamma distribution, so that every pump's estimate borrows strength from the others.
The failure counts are modeled as Poisson, and because the gamma prior on the rates is conjugate to the Poisson likelihood, this is the classic conjugate gamma-Poisson hierarchical model. It is one of the examples from Volume 1 of the classic BUGS examples; the original write-up is also available in the OpenBUGS documentation.
Model
For pump $i$ with operation time $t_i$ and failure count $x_i$:
\[\begin{aligned} x_i &\sim \text{Poisson}(\theta_i \, t_i), \qquad i = 1, \ldots, N \\ \theta_i &\sim \text{Gamma}(\alpha, \beta) \\ \alpha &\sim \text{Exponential}(1) \\ \beta &\sim \text{Gamma}(0.1, 1.0) \end{aligned}\]
In JuliaBUGS the model is written with the @bugs macro, which accepts the BUGS program almost unchanged:
using JuliaBUGS
pumps = @bugs begin
for i in 1:N
theta[i] ~ dgamma(alpha, beta)
lambda[i] = theta[i] * t[i]
x[i] ~ dpois(lambda[i])
end
alpha ~ dexp(1)
beta ~ dgamma(0.1, 1.0)
endBUGSModelDef:
begin
for i = 1:N
theta[i] ~ dgamma(alpha, beta)
lambda[i] = theta[i] * t[i]
x[i] ~ dpois(lambda[i])
end
alpha ~ dexp(1)
beta ~ dgamma(0.1, 1.0)
endData
The data are a NamedTuple with the operation times t (thousands of hours), the observed failure counts x, and the number of pumps N. Calling the model definition with the data builds the model:
data = (
t = [94.3, 15.7, 62.9, 126, 5.24, 31.4, 1.05, 1.05, 2.1, 10.5],
x = [5, 1, 5, 14, 3, 19, 1, 1, 4, 22],
N = 10
)
model = pumps(data)BUGSModel (parameters are in transformed (unconstrained) space, with dimension 12):
Model parameters:
alpha
beta
theta[10], theta[9], theta[8], theta[7], theta[6], theta[5], theta[4], theta[3], theta[2], theta[1]
Variable sizes and types:
lambda: size = (10,), type = Vector{Float64}
alpha: type = Float64
N: type = Int64
beta: type = Float64
theta: size = (10,), type = Vector{Float64}
t: size = (10,), type = Vector{Float64}
x: size = (10,), type = Vector{Int64}
All of the classic Volume 1 examples ship with the package in JuliaBUGS.BUGSExamples — for each example you get the model definition, the data, the initial values, and (where recorded) the reference results; this one is JuliaBUGS.BUGSExamples.VOLUME_1.pumps.
Sampling
To draw posterior samples, rebuild the model with gradient support and run the NUTS sampler:
using AbstractMCMC, AdvancedHMC, ADTypes, Mooncake, FlexiChains
using LogDensityProblems
model = pumps(data; adtype=AutoMooncake(; config=nothing))
n_samples, n_adapts = 2000, 1000
D = LogDensityProblems.dimension(model)
chain = AbstractMCMC.sample(
model, NUTS(0.8), n_samples;
chain_type=VNChain, n_adapts=n_adapts,
init_params=rand(D), discard_initial=n_adapts,
)
summarystats(chain)BUGS-style initial values for this example are available as JuliaBUGS.BUGSExamples.VOLUME_1.pumps.inits and can be applied with initialize!(model, inits).
Results
Reference posterior summaries are not bundled with the package for this example; the published results table can be found on the OpenBUGS Pumps page. The quantities of interest are the hyperparameters alpha and beta and the pump-specific failure rates theta[1] through theta[10]. A correctly converged chain's summarystats(chain) should match the published values up to Monte Carlo error.
See also: the example gallery overview and the getting-started tutorial.