
Prior Templates for Stationary and Non-Stationary Models
Source:vignettes/prior-templates.Rmd
prior-templates.RmdWho 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
ar1andmatern - a checklist for practical prior calibration
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)inar1
So priors are usually on theta (coefficient) scale.
target = "coef" vs target = "field"
-
target = "coef": prior on coefficients (thetaparameters) -
target = "field": prior on linear predictors (B %*% theta) for noise
For noise components this means:
-
mu: prior onB_mu %*% theta_mu -
sigma: prior onB_sigma %*% theta_sigma(beforeexp) -
nu: prior onB_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_KExample 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(...).
Final checklist
- Start with simple priors and a small model.
- Inspect
model$operator$param_namebefore naming operator priors. - Use
target = "field"only for non-stationary noise components. - Prefer one global prior (
theta = ...) and override only key parameters. - Refit and check
traceplot()andsummary()before loosening priors.