This vignette assumes you’ve already read the Get started vignette and you now have a specific performance question: is it worth vectorizing my R integrand?
cubature exposes two calling conventions for the same
underlying C routine:
fDim vector).vectorInterface = TRUE for
hcubature/pcubature or
nVec > 1 for Cuba methods) — your R function receives a
matrix of points and returns a matrix of values, amortizing R’s per-call
overhead over many integrand evaluations at once.The vectorized interface almost always wins for non-trivial problems, sometimes by a factor of 10–50×, because the dominant cost is R’s function-call overhead rather than the arithmetic inside the integrand. But the speedup depends on your specific integrand, the method you pick, and the dimension. This vignette runs ten representative integrands through every combination and reports timings, so you can make an informed decision rather than guess.
Our harness will provide timing results for hcubature,
pcubature (where appropriate), and Cuba cuhre
calls — scalar and vectorized variants of each.
library(bench)
library(cubature)
harness <- function(which = NULL,
f, fv, lowerLimit, upperLimit, tol = 1e-3, iterations = 20, ...) {
fns <- c(hc = "Non-vectorized Hcubature",
hc.v = "Vectorized Hcubature",
pc = "Non-vectorized Pcubature",
pc.v = "Vectorized Pcubature",
cc = "Non-vectorized cubature::cuhre",
cc_v = "Vectorized cubature::cuhre")
cc <- function() cubature::cuhre(f = f,
lowerLimit = lowerLimit, upperLimit = upperLimit,
relTol = tol,
...)
cc_v <- function() cubature::cuhre(f = fv,
lowerLimit = lowerLimit, upperLimit = upperLimit,
relTol = tol,
nVec = 1024L,
...)
hc <- function() cubature::hcubature(f = f,
lowerLimit = lowerLimit,
upperLimit = upperLimit,
tol = tol,
...)
hc.v <- function() cubature::hcubature(f = fv,
lowerLimit = lowerLimit,
upperLimit = upperLimit,
tol = tol,
vectorInterface = TRUE,
...)
pc <- function() cubature::pcubature(f = f,
lowerLimit = lowerLimit,
upperLimit = upperLimit,
tol = tol,
...)
pc.v <- function() cubature::pcubature(f = fv,
lowerLimit = lowerLimit,
upperLimit = upperLimit,
tol = tol,
vectorInterface = TRUE,
...)
ndim <- length(lowerLimit)
if (is.null(which)) {
fnIndices <- seq_along(fns)
} else {
fnIndices <- na.omit(match(which, names(fns)))
}
fnList <- lapply(names(fns)[fnIndices], function(x) call(x))
argList <- c(fnList, iterations = iterations, check = FALSE)
result <- do.call(bench::mark, args = argList)
d <- result[seq_along(fnIndices), 1:9]
d$expression <- fns[fnIndices]
d
}We reel off the timing runs.
func <- function(x) sin(x[1]) * cos(x[2]) * exp(x[3])
func.v <- function(x) {
matrix(apply(x, 2, function(z) sin(z[1]) * cos(z[2]) * exp(z[3])), ncol = ncol(x))
}
d <- harness(f = func, fv = func.v,
lowerLimit = rep(0, 3),
upperLimit = rep(1, 3),
tol = 1e-5,
iterations = 100)[, 1:9]
knitr::kable(d, digits = 3, row.names = FALSE)| expression | min | median | itr/sec | mem_alloc | gc/sec | n_itr | n_gc | total_time |
|---|---|---|---|---|---|---|---|---|
| Non-vectorized Hcubature | 272.12µs | 281.2µs | 3505.732 | 23.9KB | 71.546 | 98 | 2 | 28ms |
| Vectorized Hcubature | 394.99µs | 413.3µs | 2396.508 | 54.5KB | 24.207 | 99 | 1 | 41.3ms |
| Non-vectorized Pcubature | 853.27µs | 870.84µs | 1139.577 | 0B | 59.978 | 95 | 5 | 83.4ms |
| Vectorized Pcubature | 1.19ms | 1.22ms | 812.282 | 58.4KB | 42.752 | 95 | 5 | 117ms |
| Non-vectorized cubature::cuhre | 590.58µs | 608.69µs | 1627.072 | 0B | 33.206 | 98 | 2 | 60.2ms |
| Vectorized cubature::cuhre | 588.51µs | 614.27µs | 1614.600 | 39.8KB | 32.951 | 98 | 2 | 60.7ms |
Using cubature, we evaluate \[
\int_R\phi(x)dx
\] where \(\phi(x)\) is the
three-dimensional multivariate normal density with mean 0, and variance
\[
\Sigma = \left(\begin{array}{rrr}
1 &\frac{3}{5} &\frac{1}{3}\\
\frac{3}{5} &1 &\frac{11}{15}\\
\frac{1}{3} &\frac{11}{15} & 1
\end{array}
\right)
\] and \(R\) is \([-\frac{1}{2}, 1] \times [-\frac{1}{2}, 4] \times
[-\frac{1}{2}, 2].\)
We construct a scalar function (my_dmvnorm) and a vector
analogue (my_dmvnorm_v). First the functions.
m <- 3
sigma <- diag(3)
sigma[2,1] <- sigma[1, 2] <- 3/5 ; sigma[3,1] <- sigma[1, 3] <- 1/3
sigma[3,2] <- sigma[2, 3] <- 11/15
logdet <- sum(log(eigen(sigma, symmetric = TRUE, only.values = TRUE)$values))
my_dmvnorm <- function (x, mean, sigma, logdet) {
x <- matrix(x, ncol = length(x))
distval <- stats::mahalanobis(x, center = mean, cov = sigma)
exp(-(3 * log(2 * pi) + logdet + distval)/2)
}
my_dmvnorm_v <- function (x, mean, sigma, logdet) {
distval <- stats::mahalanobis(t(x), center = mean, cov = sigma)
exp(matrix(-(3 * log(2 * pi) + logdet + distval)/2, ncol = ncol(x)))
}Now the timing.
d <- harness(f = my_dmvnorm, fv = my_dmvnorm_v,
lowerLimit = rep(-0.5, 3),
upperLimit = c(1, 4, 2),
tol = 1e-5,
iterations = 10,
mean = rep(0, m), sigma = sigma, logdet = logdet)
knitr::kable(d, digits = 3)| expression | min | median | itr/sec | mem_alloc | gc/sec | n_itr | n_gc | total_time |
|---|---|---|---|---|---|---|---|---|
| Non-vectorized Hcubature | 826.22ms | 830.43ms | 1.191 | 154.45KB | 13.577 | 10 | 114 | 8.4s |
| Vectorized Hcubature | 2.67ms | 2.75ms | 360.770 | 1.89MB | 0.000 | 10 | 0 | 27.72ms |
| Non-vectorized Pcubature | 355.42ms | 362.06ms | 2.722 | 0B | 12.248 | 10 | 45 | 3.67s |
| Vectorized Pcubature | 1.29ms | 1.31ms | 752.410 | 810.26KB | 0.000 | 10 | 0 | 13.29ms |
| Non-vectorized cubature::cuhre | 339.6ms | 342.88ms | 2.912 | 0B | 12.520 | 10 | 43 | 3.44s |
| Vectorized cubature::cuhre | 3.26ms | 3.34ms | 299.228 | 898.41KB | 0.000 | 10 | 0 | 33.42ms |
The effect of vectorization is huge. So it makes sense for users to vectorize their integrands as much as possible for efficiency.
Furthermore, for this particular example, we expect
mvtnorm::pmvnorm to do pretty well since it is specialized
for the multivariate normal. The vectorized versions of
hcubature and pcubature seem competitive and
in some cases better, as the table below shows.
library(mvtnorm)
g1 <- function() pmvnorm(lower = rep(-0.5, m),
upper = c(1, 4, 2), mean = rep(0, m), corr = sigma,
alg = Miwa(), abseps = 1e-5, releps = 1e-5)
g2 <- function() pmvnorm(lower = rep(-0.5, m),
upper = c(1, 4, 2), mean = rep(0, m), corr = sigma,
alg = GenzBretz(), abseps = 1e-5, releps = 1e-5)
g3 <- function() pmvnorm(lower = rep(-0.5, m),
upper = c(1, 4, 2), mean = rep(0, m), corr = sigma,
alg = TVPACK(), abseps = 1e-5, releps = 1e-5)
knitr::kable(bench::mark(g1(), g2(), g3(), iterations = 20, check = FALSE)[, 1:9],
digits = 3, row.names = FALSE)| expression | min | median | itr/sec | mem_alloc | gc/sec | n_itr | n_gc | total_time |
|---|---|---|---|---|---|---|---|---|
| g1() | 779µs | 1.4ms | 722.022 | 355.33KB | 0 | 20 | 0 | 27.7ms |
| g2() | 776µs | 1.39ms | 782.611 | 2.49KB | 0 | 20 | 0 | 25.6ms |
| g3() | 765µs | 1.39ms | 844.774 | 2.49KB | 0 | 20 | 0 | 23.7ms |
testFn0 <- function(x) prod(cos(x))
testFn0_v <- function(x) matrix(apply(x, 2, function(z) prod(cos(z))), ncol = ncol(x))
d <- harness(f = testFn0, fv = testFn0_v,
lowerLimit = rep(0, 2), upperLimit = rep(1, 2), iterations = 1000)
knitr::kable(d, digits = 3)| expression | min | median | itr/sec | mem_alloc | gc/sec | n_itr | n_gc | total_time |
|---|---|---|---|---|---|---|---|---|
| Non-vectorized Hcubature | 38µs | 40.2µs | 24160.654 | 7.66KB | 0.000 | 1000 | 0 | 41.4ms |
| Vectorized Hcubature | 66.2µs | 69.4µs | 13834.140 | 1.16KB | 13.848 | 999 | 1 | 72.2ms |
| Non-vectorized Pcubature | 51.1µs | 55.1µs | 17734.548 | 0B | 17.752 | 999 | 1 | 56.3ms |
| Vectorized Pcubature | 117.8µs | 124.1µs | 7838.017 | 18.68KB | 15.707 | 998 | 2 | 127.3ms |
| Non-vectorized cubature::cuhre | 341µs | 351.3µs | 2817.869 | 0B | 19.864 | 993 | 7 | 352.4ms |
| Vectorized cubature::cuhre | 363.3µs | 379.1µs | 2607.881 | 16.38KB | 15.742 | 994 | 6 | 381.2ms |
testFn1 <- function(x) {
val <- sum(((1 - x) / x)^2)
scale <- prod((2 / sqrt(pi)) / x^2)
exp(-val) * scale
}
testFn1_v <- function(x) {
val <- matrix(apply(x, 2, function(z) sum(((1 - z) / z)^2)), ncol(x))
scale <- matrix(apply(x, 2, function(z) prod((2 / sqrt(pi)) / z^2)), ncol(x))
exp(-val) * scale
}
d <- harness(f = testFn1, fv = testFn1_v,
lowerLimit = rep(0, 3), upperLimit = rep(1, 3), iterations = 10)
knitr::kable(d, digits = 3)| expression | min | median | itr/sec | mem_alloc | gc/sec | n_itr | n_gc | total_time |
|---|---|---|---|---|---|---|---|---|
| Non-vectorized Hcubature | 2.93ms | 2.97ms | 336.619 | 65.2KB | 37.402 | 9 | 1 | 26.74ms |
| Vectorized Hcubature | 5.04ms | 5.09ms | 196.314 | 290.5KB | 0.000 | 10 | 0 | 50.94ms |
| Non-vectorized Pcubature | 80.65µs | 84.67µs | 11151.032 | 0B | 0.000 | 10 | 0 | 896.78µs |
| Vectorized Pcubature | 162.24µs | 167.13µs | 5718.918 | 4.1KB | 0.000 | 10 | 0 | 1.75ms |
| Non-vectorized cubature::cuhre | 13.58ms | 13.79ms | 72.488 | 0B | 18.122 | 8 | 2 | 110.36ms |
| Vectorized cubature::cuhre | 20.26ms | 20.49ms | 48.701 | 971.5KB | 20.872 | 7 | 3 | 143.73ms |
testFn2 <- function(x) {
radius <- 0.50124145262344534123412
ifelse(sum(x * x) < radius * radius, 1, 0)
}
testFn2_v <- function(x) {
radius <- 0.50124145262344534123412
matrix(apply(x, 2, function(z) ifelse(sum(z * z) < radius * radius, 1, 0)), ncol = ncol(x))
}
d <- harness(which = c("hc", "hc.v", "cc", "cc_v"),
f = testFn2, fv = testFn2_v,
lowerLimit = rep(0, 2), upperLimit = rep(1, 2), iterations = 10)
knitr::kable(d, digits = 3)| expression | min | median | itr/sec | mem_alloc | gc/sec | n_itr | n_gc | total_time |
|---|---|---|---|---|---|---|---|---|
| Non-vectorized Hcubature | 42ms | 42.3ms | 22.662 | 17.7KB | 24.928 | 10 | 11 | 441.27ms |
| Vectorized Hcubature | 47.3ms | 47.4ms | 20.898 | 1011.8KB | 22.988 | 10 | 11 | 478.5ms |
| Non-vectorized cubature::cuhre | 800.5ms | 806.1ms | 1.230 | 0B | 21.531 | 10 | 175 | 8.13s |
| Vectorized cubature::cuhre | 871.1ms | 875.7ms | 1.136 | 21.2MB | 17.033 | 10 | 150 | 8.81s |
testFn3 <- function(x) prod(2 * x)
testFn3_v <- function(x) matrix(apply(x, 2, function(z) prod(2 * z)), ncol = ncol(x))
d <- harness(f = testFn3, fv = testFn3_v,
lowerLimit = rep(0, 3), upperLimit = rep(1, 3), iterations = 50)
knitr::kable(d, digits = 3)| expression | min | median | itr/sec | mem_alloc | gc/sec | n_itr | n_gc | total_time |
|---|---|---|---|---|---|---|---|---|
| Non-vectorized Hcubature | 59µs | 61.4µs | 15720.250 | 6.75KB | 0.00 | 50 | 0 | 3.18ms |
| Vectorized Hcubature | 93.2µs | 96.3µs | 9984.712 | 2.91KB | 0.00 | 50 | 0 | 5.01ms |
| Non-vectorized Pcubature | 54.3µs | 56.1µs | 17041.410 | 0B | 0.00 | 50 | 0 | 2.93ms |
| Vectorized Pcubature | 85.8µs | 90µs | 10747.824 | 19.12KB | 0.00 | 50 | 0 | 4.65ms |
| Non-vectorized cubature::cuhre | 656.6µs | 682.6µs | 1458.235 | 0B | 29.76 | 49 | 1 | 33.6ms |
| Vectorized cubature::cuhre | 651µs | 681.1µs | 1467.047 | 39.84KB | 0.00 | 50 | 0 | 34.08ms |
testFn4 <- function(x) {
a <- 0.1
s <- sum((x - 0.5)^2)
((2 / sqrt(pi)) / (2. * a))^length(x) * exp (-s / (a * a))
}
testFn4_v <- function(x) {
a <- 0.1
r <- apply(x, 2, function(z) {
s <- sum((z - 0.5)^2)
((2 / sqrt(pi)) / (2. * a))^length(z) * exp (-s / (a * a))
})
matrix(r, ncol = ncol(x))
}
d <- harness(f = testFn4, fv = testFn4_v,
lowerLimit = rep(0, 2), upperLimit = rep(1, 2), iterations = 20)
knitr::kable(d, digits = 3)| expression | min | median | itr/sec | mem_alloc | gc/sec | n_itr | n_gc | total_time |
|---|---|---|---|---|---|---|---|---|
| Non-vectorized Hcubature | 1.39ms | 1.4ms | 707.918 | 85.2KB | 37.259 | 19 | 1 | 26.8ms |
| Vectorized Hcubature | 1.81ms | 1.84ms | 543.983 | 147.2KB | 28.631 | 19 | 1 | 34.9ms |
| Non-vectorized Pcubature | 2.07ms | 2.11ms | 473.402 | 0B | 0.000 | 20 | 0 | 42.2ms |
| Vectorized Pcubature | 2.62ms | 2.64ms | 378.338 | 68.9KB | 19.913 | 19 | 1 | 50.2ms |
| Non-vectorized cubature::cuhre | 3.35ms | 3.39ms | 292.929 | 0B | 32.548 | 18 | 2 | 61.4ms |
| Vectorized cubature::cuhre | 3.78ms | 3.88ms | 256.636 | 125.6KB | 13.507 | 19 | 1 | 74ms |
testFn5 <- function(x) {
a <- 0.1
s1 <- sum((x - 1 / 3)^2)
s2 <- sum((x - 2 / 3)^2)
0.5 * ((2 / sqrt(pi)) / (2. * a))^length(x) * (exp(-s1 / (a * a)) + exp(-s2 / (a * a)))
}
testFn5_v <- function(x) {
a <- 0.1
r <- apply(x, 2, function(z) {
s1 <- sum((z - 1 / 3)^2)
s2 <- sum((z - 2 / 3)^2)
0.5 * ((2 / sqrt(pi)) / (2. * a))^length(z) * (exp(-s1 / (a * a)) + exp(-s2 / (a * a)))
})
matrix(r, ncol = ncol(x))
}
d <- harness(f = testFn5, fv = testFn5_v,
lowerLimit = rep(0, 2), upperLimit = rep(1, 2), iterations = 20)
knitr::kable(d, digits = 3)| expression | min | median | itr/sec | mem_alloc | gc/sec | n_itr | n_gc | total_time |
|---|---|---|---|---|---|---|---|---|
| Non-vectorized Hcubature | 3.65ms | 3.7ms | 268.518 | 133KB | 14.133 | 19 | 1 | 70.8ms |
| Vectorized Hcubature | 4.63ms | 4.67ms | 213.404 | 249KB | 23.712 | 18 | 2 | 84.3ms |
| Non-vectorized Pcubature | 2.54ms | 2.56ms | 390.574 | 0B | 20.557 | 19 | 1 | 48.6ms |
| Vectorized Pcubature | 3.27ms | 3.33ms | 300.653 | 69KB | 15.824 | 19 | 1 | 63.2ms |
| Non-vectorized cubature::cuhre | 7.08ms | 7.14ms | 139.891 | 0B | 15.543 | 18 | 2 | 128.7ms |
| Vectorized cubature::cuhre | 8.26ms | 8.32ms | 119.665 | 224KB | 21.117 | 17 | 3 | 142.1ms |
testFn6 <- function(x) {
a <- (1 + sqrt(10.0)) / 9.0
prod( a / (a + 1) * ((a + 1) / (a + x))^2)
}
testFn6_v <- function(x) {
a <- (1 + sqrt(10.0)) / 9.0
r <- apply(x, 2, function(z) prod( a / (a + 1) * ((a + 1) / (a + z))^2))
matrix(r, ncol = ncol(x))
}
d <- harness(f = testFn6, fv = testFn6_v,
lowerLimit = rep(0, 3), upperLimit = rep(1, 3), iterations = 20)
knitr::kable(d, digits = 3)| expression | min | median | itr/sec | mem_alloc | gc/sec | n_itr | n_gc | total_time |
|---|---|---|---|---|---|---|---|---|
| Non-vectorized Hcubature | 1.62ms | 1.63ms | 600.156 | 64.6KB | 31.587 | 19 | 1 | 31.7ms |
| Vectorized Hcubature | 2.15ms | 2.18ms | 457.613 | 156KB | 24.085 | 19 | 1 | 41.5ms |
| Non-vectorized Pcubature | 8.34ms | 8.47ms | 118.031 | 0B | 20.829 | 17 | 3 | 144ms |
| Vectorized Pcubature | 10.34ms | 10.42ms | 95.602 | 386.2KB | 16.871 | 17 | 3 | 177.8ms |
| Non-vectorized cubature::cuhre | 4.42ms | 4.48ms | 222.271 | 0B | 24.697 | 18 | 2 | 81ms |
| Vectorized cubature::cuhre | 4.76ms | 4.83ms | 206.602 | 225.8KB | 10.874 | 19 | 1 | 92ms |
testFn7 <- function(x) {
n <- length(x)
p <- 1/n
(1 + p)^n * prod(x^p)
}
testFn7_v <- function(x) {
matrix(apply(x, 2, function(z) {
n <- length(z)
p <- 1/n
(1 + p)^n * prod(z^p)
}), ncol = ncol(x))
}
d <- harness(f = testFn7, fv = testFn7_v,
lowerLimit = rep(0, 3), upperLimit = rep(1, 3), iterations = 20)
knitr::kable(d, digits = 3)| expression | min | median | itr/sec | mem_alloc | gc/sec | n_itr | n_gc | total_time |
|---|---|---|---|---|---|---|---|---|
| Non-vectorized Hcubature | 3.35ms | 3.39ms | 293.445 | 32.89KB | 15.444 | 19 | 1 | 64.7ms |
| Vectorized Hcubature | 4.01ms | 4.06ms | 245.172 | 205.61KB | 27.241 | 18 | 2 | 73.4ms |
| Non-vectorized Pcubature | 8.33ms | 8.43ms | 118.584 | 0B | 20.927 | 17 | 3 | 143.4ms |
| Vectorized Pcubature | 9.54ms | 9.65ms | 103.585 | 386.24KB | 25.896 | 16 | 4 | 154.5ms |
| Non-vectorized cubature::cuhre | 40.86ms | 41.11ms | 24.304 | 0B | 72.911 | 5 | 15 | 205.7ms |
| Vectorized cubature::cuhre | 40.84ms | 41.03ms | 24.349 | 2.04MB | 56.815 | 6 | 14 | 246.4ms |
I.1d <- function(x) {
sin(4 * x) *
x * ((x * ( x * (x * x - 4) + 1) - 1))
}
I.1d_v <- function(x) {
matrix(apply(x, 2, function(z)
sin(4 * z) *
z * ((z * ( z * (z * z - 4) + 1) - 1))),
ncol = ncol(x))
}
d <- harness(f = I.1d, fv = I.1d_v,
lowerLimit = -2, upperLimit = 2, iterations = 100)
knitr::kable(d, digits = 3)| expression | min | median | itr/sec | mem_alloc | gc/sec | n_itr | n_gc | total_time |
|---|---|---|---|---|---|---|---|---|
| Non-vectorized Hcubature | 133.8µs | 145.6µs | 6684.214 | 53.7KB | 67.517 | 99 | 1 | 14.81ms |
| Vectorized Hcubature | 217.7µs | 225.5µs | 4311.464 | 68.5KB | 0.000 | 100 | 0 | 23.19ms |
| Non-vectorized Pcubature | 56.2µs | 58.8µs | 16373.566 | 0B | 0.000 | 100 | 0 | 6.11ms |
| Vectorized Pcubature | 160.6µs | 164.9µs | 5866.920 | 0B | 0.000 | 100 | 0 | 17.05ms |
| Non-vectorized cubature::cuhre | 230.1µs | 239.6µs | 4096.623 | 0B | 41.380 | 99 | 1 | 24.17ms |
| Vectorized cubature::cuhre | 510.3µs | 530.1µs | 1887.287 | 0B | 0.000 | 100 | 0 | 52.99ms |
I.2d <- function(x) {
x1 <- x[1]; x2 <- x[2]
sin(4 * x1 + 1) * cos(4 * x2) * x1 * (x1 * (x1 * x1)^2 - x2 * (x2 * x2 - x1) +2)
}
I.2d_v <- function(x) {
matrix(apply(x, 2,
function(z) {
x1 <- z[1]; x2 <- z[2]
sin(4 * x1 + 1) * cos(4 * x2) * x1 * (x1 * (x1 * x1)^2 - x2 * (x2 * x2 - x1) +2)
}),
ncol = ncol(x))
}
d <- harness(f = I.2d, fv = I.2d_v,
lowerLimit = rep(-1, 2), upperLimit = rep(1, 2), iterations = 100)
knitr::kable(d, digits = 3)| expression | min | median | itr/sec | mem_alloc | gc/sec | n_itr | n_gc | total_time |
|---|---|---|---|---|---|---|---|---|
| Non-vectorized Hcubature | 4.66ms | 4.73ms | 211.131 | 78.4KB | 20.881 | 91 | 9 | 431ms |
| Vectorized Hcubature | 5.65ms | 5.72ms | 174.587 | 304.7KB | 17.267 | 91 | 9 | 521.2ms |
| Non-vectorized Pcubature | 424.52µs | 440.77µs | 2255.637 | 0B | 0.000 | 100 | 0 | 44.3ms |
| Vectorized Pcubature | 622.34µs | 654.02µs | 1518.458 | 18.3KB | 15.338 | 99 | 1 | 65.2ms |
| Non-vectorized cubature::cuhre | 1.29ms | 1.32ms | 750.316 | 0B | 15.313 | 98 | 2 | 130.6ms |
| Vectorized cubature::cuhre | 1.39ms | 1.42ms | 699.561 | 60.1KB | 14.277 | 98 | 2 | 140.1ms |
The recommendations are:
Vectorize your integrand. The time spent in so doing pays back enormously on all but the most trivial problems. This is easy to do and the examples above show how.
Vectorized hcubature is a good default
starting point. It handles the widest range of problems
well.
For smooth integrands in low dimensions (\(\leq 3\)), try
pcubature. It can be substantially faster than
hcubature when its assumptions hold. Experiment before
committing in a production setting, and be aware of the sampling-density
cliff documented in the Get Started vignette — when in doubt,
cross-check with cuhre.
For non-smooth or localized integrands, prefer
cubintegrate(method = "cuhre") or at least
hcubature(..., robust = TRUE). See the Robustness section
of the Get Started vignette.
## R version 4.6.1 (2026-06-24)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 26.04.1 LTS
##
## Matrix products: default
## BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
## LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.32.so; LAPACK version 3.12.0
##
## locale:
## [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
## [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
## [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
## [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
## [9] LC_ADDRESS=C LC_TELEPHONE=C
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
##
## time zone: Etc/UTC
## tzcode source: system (glibc)
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] mvtnorm_1.4-2 bench_1.1.4 cubature_2.2.0
##
## loaded via a namespace (and not attached):
## [1] vctrs_0.7.3 cli_3.6.6 knitr_1.52 rlang_1.3.0
## [5] xfun_0.61 otel_0.2.0 jsonlite_2.0.0 glue_1.8.1
## [9] buildtools_1.0.0 htmltools_0.5.9 maketools_1.3.2 sys_3.4.3
## [13] sass_0.4.10 rmarkdown_2.32 tibble_3.3.1 evaluate_1.0.5
## [17] jquerylib_0.1.4 fastmap_1.2.0 yaml_2.3.12 profmem_0.7.0
## [21] lifecycle_1.0.5 compiler_4.6.1 pkgconfig_2.0.3 Rcpp_1.1.2
## [25] digest_0.6.39 R6_2.6.1 pillar_1.11.1 magrittr_2.0.5
## [29] bslib_0.12.0 tools_4.6.1 cachem_1.1.0