Skip to contents

Who this is for

This vignette is for users who want to set priors in ngme2 without reading internal implementation code.

You will get:

  • a quick mental model for priors in ngme2
  • copy-paste templates for stationary and non-stationary settings
  • concrete examples for ar1 and matern
  • a checklist for practical prior calibration
library(ngme2)
#> This is ngme2 of version 0.9.1
#> - See our homepage: https://davidbolin.github.io/ngme2 for more details.
#> 
#> Attaching package: 'ngme2'
#> The following object is masked from 'package:stats':
#> 
#>     ar
set.seed(1)

Quick start

The prior API has two layers:

  • define one prior with prior_*()
  • map priors to parameter names with priors(...)
prior_normal(mean = 0, sd = 1)
#> $dist
#> [1] "normal"
#> 
#> $hyper
#> mean   sd 
#>    0    1 
#> 
#> $target
#> [1] "coef"
#> 
#> attr(,"class")
#> [1] "ngme_prior_spec"
prior_pc_sd(u = 1, alpha = 0.1)
#> $dist
#> [1] "pc.sd"
#> 
#> $hyper
#>     u alpha 
#>   1.0   0.1 
#> 
#> $target
#> [1] "coef"
#> 
#> attr(,"class")
#> [1] "ngme_prior_spec"
prior_half_cauchy(scale = 1)
#> $dist
#> [1] "half.cauchy"
#> 
#> $hyper
#> scale 
#>     1 
#> 
#> $target
#> [1] "coef"
#> 
#> attr(,"class")
#> [1] "ngme_prior_spec"
prior_none()
#> $dist
#> [1] "none"
#> 
#> $hyper
#> numeric(0)
#> 
#> $target
#> [1] "coef"
#> 
#> attr(,"class")
#> [1] "ngme_prior_spec"

priors(
  mu = prior_normal(0, 1),
  sigma = prior_normal(0, 1),
  nu = prior_normal(0, 1)
)
#> $mu
#> $dist
#> [1] "normal"
#> 
#> $hyper
#> mean   sd 
#>    0    1 
#> 
#> $target
#> [1] "coef"
#> 
#> attr(,"class")
#> [1] "ngme_prior_spec"
#> 
#> $sigma
#> $dist
#> [1] "normal"
#> 
#> $hyper
#> mean   sd 
#>    0    1 
#> 
#> $target
#> [1] "coef"
#> 
#> attr(,"class")
#> [1] "ngme_prior_spec"
#> 
#> $nu
#> $dist
#> [1] "normal"
#> 
#> $hyper
#> mean   sd 
#>    0    1 
#> 
#> $target
#> [1] "coef"
#> 
#> attr(,"class")
#> [1] "ngme_prior_spec"
#> 
#> attr(,"class")
#> [1] "ngme_priors"

Natural scale vs theta scale

Most model parameters are optimized internally on an unconstrained theta scale.

Typical transformations are:

  • sigma = exp(theta_sigma)
  • nu = nu_lower_bound + exp(theta_nu)
  • rho = tanh(theta_rho) in ar1

So priors are usually on theta (coefficient) scale.

target = "coef" vs target = "field"

  • target = "coef": prior on coefficients (theta parameters)
  • target = "field": prior on linear predictors (B %*% theta) for noise

For noise components this means:

  • mu: prior on B_mu %*% theta_mu
  • sigma: prior on B_sigma %*% theta_sigma (before exp)
  • nu: prior on B_nu %*% theta_nu (before lower-bound + exp)

For operator parameters (theta_K), use target = "coef".

Example 1: Stationary AR(1) with explicit priors

Put a prior directly on AR(1) rho by naming it in priors(...).

n <- 120
idx <- 1:n

op_ar1 <- f(
  map = idx,
  model = ar1(),
  noise = noise_normal(),
  prior = priors(
    rho = prior_normal(0, 0.6)
  )
)

op_ar1$operator$param_name
#> [1] "rho"
op_ar1$prior_theta_K
#> [[1]]
#> [[1]]$type
#> [1] "normal"
#> 
#> [[1]]$param
#> [1] 0.000000 2.777778
#> 
#> [[1]]$target
#> [1] "coef"

Global operator prior fallback:

f(
  map = idx,
  model = ar1(),
  noise = noise_normal(),
  prior = priors(theta = prior_normal(0, 1))
)$prior_theta_K
#> [[1]]
#> [[1]]$type
#> [1] "normal"
#> 
#> [[1]]$param
#> [1] 0 1
#> 
#> [[1]]$target
#> [1] "coef"

Example 2: Stationary NIG noise priors

noise_stationary <- noise_nig(
  mu = 0,
  sigma = 1,
  nu = 1,
  prior = priors(
    mu = prior_normal(0, 1),
    sigma = prior_normal(0, 1),
    nu = prior_normal(0, 1)
  )
)

noise_stationary$prior_mu
#> $type
#> [1] "normal"
#> 
#> $param
#> [1] 0 1
#> 
#> $target
#> [1] "coef"
noise_stationary$prior_sigma
#> $type
#> [1] "normal"
#> 
#> $param
#> [1] 0 1
#> 
#> $target
#> [1] "coef"
noise_stationary$prior_nu
#> $type
#> [1] "normal"
#> 
#> $param
#> [1] 0 1
#> 
#> $target
#> [1] "coef"

Example 3: Fixed-effect (beta) priors in ngme()

You can now set priors for fixed effects using prior_beta = ... in ngme().

fit <- ngme(
  y ~ x1 + x2 + f(idx, model = ar1(), noise = noise_normal()),
  data = dat,
  family = noise_normal(),
  prior_beta = priors(
    beta = prior_normal(0, 2), # fallback for all beta coefficients
    x1 = prior_normal(0, 1), # override by coefficient name
    feff_1 = prior_half_cauchy(1) # optional index-based override
  )
)

Example 4: Non-stationary noise (mu, sigma, nu)

Use basis matrices and field priors when you want smooth regularization over space/time.

n <- 80
x <- seq(0, 1, length.out = n)

B_mu <- cbind(1, x)
B_sigma <- cbind(1, x, x^2)
B_nu <- matrix(1, n, 1)

noise_ns <- noise_nig(
  theta_mu = rep(0, ncol(B_mu)),
  B_mu = B_mu,
  theta_sigma = rep(0, ncol(B_sigma)),
  B_sigma = B_sigma,
  theta_nu = rep(0, ncol(B_nu)),
  B_nu = B_nu,
  prior = priors(
    mu = prior_normal(0, 1, target = "field"),
    sigma = prior_normal(0, 0.7, target = "field"),
    nu = prior_normal(0, 0.7, target = "coef")
  )
)

noise_ns$prior_mu
#> $type
#> [1] "normal"
#> 
#> $param
#> [1] 0 1
#> 
#> $target
#> [1] "field"
noise_ns$prior_sigma
#> $type
#> [1] "normal"
#> 
#> $param
#> [1] 0.000000 2.040816
#> 
#> $target
#> [1] "field"
noise_ns$prior_nu
#> $type
#> [1] "normal"
#> 
#> $param
#> [1] 0.000000 2.040816
#> 
#> $target
#> [1] "coef"

Recommended non-stationary noise template:

prior_noise_ns_template <- priors(
  mu = prior_normal(0, 1.0, target = "field"),
  sigma = prior_normal(0, 0.7, target = "field"),
  nu = prior_normal(0, 0.7, target = "coef")
)

prior_noise_ns_template
#> $mu
#> $dist
#> [1] "normal"
#> 
#> $hyper
#> mean   sd 
#>    0    1 
#> 
#> $target
#> [1] "field"
#> 
#> attr(,"class")
#> [1] "ngme_prior_spec"
#> 
#> $sigma
#> $dist
#> [1] "normal"
#> 
#> $hyper
#> mean   sd 
#>  0.0  0.7 
#> 
#> $target
#> [1] "field"
#> 
#> attr(,"class")
#> [1] "ngme_prior_spec"
#> 
#> $nu
#> $dist
#> [1] "normal"
#> 
#> $hyper
#> mean   sd 
#>  0.0  0.7 
#> 
#> $target
#> [1] "coef"
#> 
#> attr(,"class")
#> [1] "ngme_prior_spec"
#> 
#> attr(,"class")
#> [1] "ngme_priors"

Example 5: Non-stationary Matern kappa

For non-stationary Matern, names are typically theta_kappa1, theta_kappa2, …

Use theta = ... as fallback, then override selected coefficients.

mesh_1d <- fmesher::fm_mesh_1d(seq(0, 1, length.out = 100))
x <- seq(0, 1, length.out = mesh_1d$n)
B_kappa <- cbind(1, x, x^2)

model_matern_ns <- f(
  map = seq(0, 1, length.out = mesh_1d$n),
  model = matern(
    mesh = mesh_1d,
    alpha = 2,
    fix_alpha = TRUE,
    B_kappa = B_kappa,
    theta_kappa = rep(0, ncol(B_kappa))
  ),
  noise = noise_normal(),
  prior = priors(
    theta = prior_normal(0, 0.5), # fallback
    theta_kappa1 = prior_normal(log(2), 0.5) # override first coefficient
  )
)

model_matern_ns$operator$param_name
model_matern_ns$prior_theta_K

Example 6: End-to-end fitting script

This chunk is copy-paste ready for user analysis. It is set to eval=FALSE only to keep package vignette build fast.

set.seed(2026)
n <- 300
idx <- 1:n
x1 <- runif(n)
x2 <- rnorm(n)

latent_model <- f(
  map = idx,
  model = ar1(),
  noise = noise_normal(sigma = 0.7),
  prior = priors(rho = prior_normal(0, 0.6))
)
W <- simulate(latent_model, nsim = 1, seed = 2026)[[1]]

y <- 1.5 * x1 - 0.8 * x2 + W + rnorm(n, sd = 0.4)
dat <- data.frame(y, x1, x2, idx)

fit <- ngme(
  y ~ x1 + x2 + f(
    idx,
    model = ar1(),
    noise = noise_normal(
      prior = priors(sigma = prior_normal(0, 0.7))
    ),
    prior = priors(rho = prior_normal(0, 0.6))
  ),
  data = dat,
  control_opt = control_opt(iterations = 2000, burnin = 500)
)

summary(fit)
traceplot(fit, name = "rho")

Copy-paste templates

A. Stationary AR(1) + normal noise

template_stationary_ar1 <- list(
  latent_prior = priors(
    rho = prior_normal(0, 0.6)
  ),
  noise_prior = priors(
    sigma = prior_normal(0, 0.7)
  )
)

template_stationary_ar1
#> $latent_prior
#> $rho
#> $dist
#> [1] "normal"
#> 
#> $hyper
#> mean   sd 
#>  0.0  0.6 
#> 
#> $target
#> [1] "coef"
#> 
#> attr(,"class")
#> [1] "ngme_prior_spec"
#> 
#> attr(,"class")
#> [1] "ngme_priors"
#> 
#> $noise_prior
#> $sigma
#> $dist
#> [1] "normal"
#> 
#> $hyper
#> mean   sd 
#>  0.0  0.7 
#> 
#> $target
#> [1] "coef"
#> 
#> attr(,"class")
#> [1] "ngme_prior_spec"
#> 
#> attr(,"class")
#> [1] "ngme_priors"

B. Stationary Matern

template_stationary_matern <- list(
  fixed_alpha = priors(
    kappa = prior_normal(log(2), 0.5)
  ),
  free_alpha = priors(
    alpha = prior_normal(0, 0.7),
    kappa = prior_normal(log(2), 0.5)
  )
)

template_stationary_matern
#> $fixed_alpha
#> $kappa
#> $dist
#> [1] "normal"
#> 
#> $hyper
#>      mean        sd 
#> 0.6931472 0.5000000 
#> 
#> $target
#> [1] "coef"
#> 
#> attr(,"class")
#> [1] "ngme_prior_spec"
#> 
#> attr(,"class")
#> [1] "ngme_priors"
#> 
#> $free_alpha
#> $alpha
#> $dist
#> [1] "normal"
#> 
#> $hyper
#> mean   sd 
#>  0.0  0.7 
#> 
#> $target
#> [1] "coef"
#> 
#> attr(,"class")
#> [1] "ngme_prior_spec"
#> 
#> $kappa
#> $dist
#> [1] "normal"
#> 
#> $hyper
#>      mean        sd 
#> 0.6931472 0.5000000 
#> 
#> $target
#> [1] "coef"
#> 
#> attr(,"class")
#> [1] "ngme_prior_spec"
#> 
#> attr(,"class")
#> [1] "ngme_priors"

C. Non-stationary noise

template_nonstationary_noise <- priors(
  mu = prior_normal(0, 1.0, target = "field"),
  sigma = prior_normal(0, 0.7, target = "field"),
  nu = prior_normal(0, 0.7, target = "coef")
)

template_nonstationary_noise
#> $mu
#> $dist
#> [1] "normal"
#> 
#> $hyper
#> mean   sd 
#>    0    1 
#> 
#> $target
#> [1] "field"
#> 
#> attr(,"class")
#> [1] "ngme_prior_spec"
#> 
#> $sigma
#> $dist
#> [1] "normal"
#> 
#> $hyper
#> mean   sd 
#>  0.0  0.7 
#> 
#> $target
#> [1] "field"
#> 
#> attr(,"class")
#> [1] "ngme_prior_spec"
#> 
#> $nu
#> $dist
#> [1] "normal"
#> 
#> $hyper
#> mean   sd 
#>  0.0  0.7 
#> 
#> $target
#> [1] "coef"
#> 
#> attr(,"class")
#> [1] "ngme_prior_spec"
#> 
#> attr(,"class")
#> [1] "ngme_priors"

D. Non-stationary Matern kappa

template_nonstationary_kappa <- priors(
  theta = prior_normal(0, 0.5),
  theta_kappa1 = prior_normal(log(2), 0.5)
)

template_nonstationary_kappa
#> $theta
#> $dist
#> [1] "normal"
#> 
#> $hyper
#> mean   sd 
#>  0.0  0.5 
#> 
#> $target
#> [1] "coef"
#> 
#> attr(,"class")
#> [1] "ngme_prior_spec"
#> 
#> $theta_kappa1
#> $dist
#> [1] "normal"
#> 
#> $hyper
#>      mean        sd 
#> 0.6931472 0.5000000 
#> 
#> $target
#> [1] "coef"
#> 
#> attr(,"class")
#> [1] "ngme_prior_spec"
#> 
#> attr(,"class")
#> [1] "ngme_priors"

Troubleshooting

Error: “Unknown operator prior names”

Check valid names first:

tmp <- f(1:20, model = ar1(), noise = noise_normal())
tmp$operator$param_name
#> [1] "rho"

Then match those names exactly in priors(...).

Unsure whether to use natural scale or theta scale

Start with target = "coef" (theta scale). Use target = "field" for non-stationary noise when you want to regularize B %*% theta directly.

Unstable estimates

Start tighter (smaller sd), then relax gradually.

Final checklist

  1. Start with simple priors and a small model.
  2. Inspect model$operator$param_name before naming operator priors.
  3. Use target = "field" only for non-stationary noise components.
  4. Prefer one global prior (theta = ...) and override only key parameters.
  5. Refit and check traceplot() and summary() before loosening priors.