tests/testthat/test-nngp-density.R

test_that("dmnorm_NN_GP matches the full multivariate normal density when k = M - 1", {
  # When every location conditions on every earlier location, the NNGP
  # factorisation is exact rather than approximate, so it must reproduce the
  # ordinary multivariate normal log-density for the same covariance.
  set.seed(1)
  M      <- 8
  k      <- M - 1
  rho    <- 0.6
  sigma2 <- 1.3
  
  coords <- matrix(runif(2 * M), ncol = 2)
  nb <- computeNeighbors(coords, k = k, proj = 4326)
  # sf::st_distance() returns a 'units' object (metres) for a geographic CRS;
  # strip it here so plain arithmetic like exp() works below. computeAD()
  # itself doesn't need this -- NIMBLE's own type coercion handles it.
  edist <- matrix(as.numeric(nb$edist_sorted), M, M)
  
  AD <- computeAD(
    edist        = nb$edist_sorted,
    nid.dist     = nb$neighbors_dist,
    neighbors.id = nb$neighbor_idx,
    rho          = rho,
    sigma2       = sigma2,
    k            = k
  )
  
  # Built from the same distance matrix computeNeighbors returned, so this is
  # an exact comparison rather than one that depends on a second, independent
  # distance calculation.
  Sigma <- sigma2 * exp(-edist / rho) + diag(1e-6, M)
  
  x  <- rnorm(M)
  mu <- rep(0, M)
  
  ll_nngp <- dmnorm_NN_GP(x, mu, AD, nb$neighbor_idx, log = TRUE)
  
  R <- chol(Sigma)
  quad   <- sum(backsolve(R, x - mu, transpose = TRUE)^2)
  ll_ref <- -0.5 * (M * log(2 * pi) + 2 * sum(log(diag(R))) + quad)
  
  expect_equal(ll_nngp, ll_ref, tolerance = 1e-6)
})

test_that("rmnorm_NN_GP simulates with the correct covariance when k = M - 1", {
  set.seed(2)
  M      <- 6
  k      <- M - 1
  rho    <- 0.5
  sigma2 <- 1
  
  coords <- matrix(runif(2 * M), ncol = 2)
  nb <- computeNeighbors(coords, k = k, proj = 4326)
  edist <- matrix(as.numeric(nb$edist_sorted), M, M)
  AD <- computeAD(nb$edist_sorted, nb$neighbors_dist, nb$neighbor_idx, rho, sigma2, k)
  
  mu <- rep(0, M)
  R  <- 4000
  draws <- t(replicate(R, rmnorm_NN_GP(1, mu, AD, nb$neighbor_idx)))
  
  Sigma_true <- sigma2 * exp(-edist / rho) + diag(1e-6, M)
  Sigma_hat  <- cov(draws)
  
  # Monte Carlo comparison over many draws; a generous absolute tolerance
  # keeps the test from being flaky while still catching a wrong covariance.
  expect_true(max(abs(Sigma_hat - Sigma_true)) < 0.15)
})

Try the BayesNSGP package in your browser

Any scripts or data that you put into this service are public.

BayesNSGP documentation built on Sept. 10, 2026, 5:08 p.m.