The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
W Bases and Heterogeneous Families per
SlotThe AMM canonical decomposition
\[\theta_i = \theta_{\text{ref}} + a(x_i) + b(x_i) \odot \theta_{\text{ref}} + W(\theta_{\text{ref}})\,x_i,\]
where \(\odot\) denotes the Hadamard
(elementwise) product, coherent with the canonical notation of
vignette("v00_framework_overview", package = "gdpar") §8.2
and vignette("v01_amm_identifiability", package = "gdpar")
§3.3,
allows two refinements that take a model out of the polynomial /
homogeneous baseline and into the intermediate regime — that
is, still a single-anchor canonical fit, but with a richer modulating
geometry and per-slot heterogeneous families when the user runs
distributional regression (K > 1). This vignette
documents both refinements in the order in which a user typically
encounters them.
W bases (sub-phase 8.3.8).
The default W_basis(type = "polynomial", dim = 2L) is
sufficient when the modulating effect \(W(\theta_{\text{ref}})\) is smooth and
low-curvature. For non-monotone or sharply varying effects, a B-spline
basis preserves the AMM canonical form while giving local
flexibility.K > 1, the default
homogeneous family (the same gdpar_family for every slot)
can be replaced by a named list of gdpar_family
objects, one per slot, when the location slot’s likelihood admits
multiple compatible auxiliary parametrizations (e.g., a Gaussian
location with a Beta-distributed dispersion slot).Both refinements are orthogonal: a fit may combine a B-spline
W with heterogeneous families, or use one without the
other.
For the API of K > 1 distributional regression as a
whole and for the residual / DHARMa workflow that complements these
fits, see
vignette("vop05_distributional_K_dharma", package = "gdpar").
W basesW(\theta_{\text{ref}}) is a univariate function of the
anchor evaluated at each observation’s posterior reference value. The
polynomial basis of dimension \(\dim_W\) writes
\[W(\theta_{\text{ref}}) = \sum_{j=1}^{\dim_W} W_j \,(\theta_{\text{ref}}^j - \theta_{\text{anchor}}^j),\]
which is smooth and parsimonious but globally rigid. For modulating effects that are piecewise smooth — for example, a saturating curve or a sigmoidal shift — a B-spline basis evaluates a local set of basis functions, each supported on a few neighbouring knots, and gives finer control over local curvature without inflating the global degree.
Rule of thumb. Start with
W_basis(type = "polynomial", dim = 2L) (the default).
Switch to B-spline when (i) posterior predictive checks (see
vignette("vop05_distributional_K_dharma")) show systematic
deviations of W away from a global low-degree polynomial,
or (ii) you have prior knowledge that the modulating effect is
non-monotone over the operative range of theta_ref.
library(gdpar)
# Polynomial baseline (default)
W_poly <- W_basis(type = "polynomial", dim = 2L)
# B-spline with internal knots given explicitly, cubic by default
W_bs <- W_basis(
type = "bspline",
knots = c(-0.5, 0.0, 0.5),
degree = 3L,
boundary_knots = c(-1.5, 1.5)
)Three points of contract:
boundary_knots is mandatory for
B-spline bases (sub-phase 8.3.8, decision D4). It pins the domain of the
basis evaluation; outside it, the R-side validator aborts. Stan-side
evaluation is unconstrained, mirroring the conventional
splines::bs() behaviour, but a fit that crosses the
boundary at sampling time will return basis values that extrapolate. Set
boundary_knots to cover the operative range of
theta_ref with a small margin (e.g., 5 to 10% of the prior
anchor’s marginal SD on each side).degree = 3L is the default (cubic).
degree = 1L gives piecewise linear basis functions;
degree = 2L quadratic. Higher than cubic is rarely needed
and increases the dimension of W_raw (which is \(\dim_W = \text{length}(\text{knots}) +
\text{degree} - 1\) for a clamped basis without boundary
repetition).set.seed(2026L)
n <- 80L
theta_ref_true <- 0.5
# A sigmoidal modulating effect that the polynomial baseline cannot
# represent without raising dim_W substantially.
x_var <- runif(n, -2, 2)
W_true <- function(z) 1.2 / (1 + exp(-3 * z)) - 0.6
y <- theta_ref_true + 0.4 * (x_var - mean(x_var)) +
W_true(theta_ref_true) * (x_var - mean(x_var)) * 0.7 +
rnorm(n, sd = 0.2)
d <- data.frame(y = y, x = x_var)
library(gdpar)
fit_bs <- gdpar(
y ~ a(x) + W(),
data = d,
family = gdpar_family("gaussian"),
W = W_basis(type = "bspline",
knots = c(-1, 0, 1),
degree = 3L,
boundary_knots = c(-2.2, 2.2)),
chains = 2L, iter_warmup = 400L, iter_sampling = 400L,
refresh = 0L
)
co <- coef(fit_bs)
coThe coef() output reports the per-basis coefficient
W_raw[j, k] summarised by posterior mean and 5%/50%/95%
quantiles, exactly as in the polynomial case. Compare with a polynomial
fit of the same dim_W to see whether the B-spline absorbs
structure that the polynomial flattens.
predict(fit_bs) returns posterior draws of
theta_i on the training data, evaluated through the
B-spline basis.predict(fit_bs, newdata = ...)) is implemented for
K = 1 polynomial W; B-spline new-data
prediction is supported through the same predict.gdpar_fit
API and reads the Stan-side apply_W_basis_diff() helper at
fit time. For K > 1 distributional regression, B-spline
new-data prediction is queued for Session 8.4 and raises
gdpar_unsupported_feature_error.theta_ref drifts outside the boundary_knots
range, the basis values extrapolate. The R-side validator inspects the
prior’s anchor mean ± 3 SD at construction time and warns if the range
is too tight; the user can either widen boundary_knots or
tighten the anchor prior.K > 1)For distributional regression K > 1,
gdpar() accepts a single gdpar_family and
replicates the canonical per-slot parametrization across every slot. For
Gaussian K = 2, slot 1 is the location (mu
with identity link) and slot 2 is the dispersion (sigma
with log link). The two slots share the same Gaussian likelihood family;
only the slot-specific link differs.
The heterogeneous-per-slot path (sub-phase 8.3.7, decision D5) targets the use case where the auxiliary slots (slot \(k > 1\)) have a different parametric structure than the location slot. Two motivating examples:
The canonical decision D5 (sub-phase 8.3.7) also restricts
heterogeneous specifications to compatible families: slot 1
(“location”) must belong to the canonical primary
{gaussian, poisson, neg_binomial_2, beta, gamma, lognormal_loc_scale}
family-id set, and slot \(k > 1\)
must declare a family whose support is coherent with the
slot’s canonical role. Incompatible combinations (e.g., a Gaussian
location with a Bernoulli dispersion) are rejected by
.gdpar_validate_heterogeneous_family_K with
gdpar_input_error.
The pattern name lognormal_loc_scale listed in the
primary family-id set above is not part of the enum of
gdpar_family(name): the package registers it as a
K = 2 custom-family pattern (canonised in
Sub-phase 8.3.4), accessed via
gdpar_family_custom_K(stan_lpdf_id = "lognormal_loc_scale", ...).
See
vignette("vop05_distributional_K_dharma", package = "gdpar")
§2.4 for the literal recipe and
vignette("vop05_distributional_K_dharma", package = "gdpar")
§6 for the complementary gdpar_family_custom()
(K = 1) constructor.
On the Stan side, the per-slot dispatch of the inverse link is
implemented by the helper function
apply_inv_link_by_id(link_id, eta) canonised in
inst/stan/amm_distrib_K.stan:228. It maps the integer
link_id carried per slot in the data block
(inv_link_id_per_slot) to the corresponding inverse-link
evaluation, so that each slot’s linear predictor is transformed by its
own inv_link without conditional R-side branching at
compile time. This is the Stan-side mechanism that makes the
heterogeneous-per-slot path of Sub-phase 8.3.7 transparent to the model
template.
fit_het <- gdpar(
gdpar_bf(y ~ a(x1), sigma ~ a(x2)),
data = d,
family = list(
mu = gdpar_family("gaussian"),
sigma = gdpar_family("beta")
),
chains = 2L, iter_warmup = 400L, iter_sampling = 400L
)The named list keys must match the slot names of the
gdpar_formula_set (the bf() constructor’s
left-hand side names, or the param_specs names of the
location family). Mismatches raise gdpar_input_error with
the expected slot names listed.
Sub-phase 8.3.7 supports heterogeneous specifications for
K = 2 in three combinations of practical interest:
Other combinations are queued for sub-phase 8.4 (heterogeneous
K = 3+ families) and emit
gdpar_unsupported_feature_error at construction time with a
message pointing to Session 8.4 deudas.
set.seed(818L)
n <- 100L
x1 <- rnorm(n); x2 <- rnorm(n)
mu_true <- 0.3 + 0.6 * (x1 - mean(x1))
sigma_eta <- 0.5 + 0.3 * (x2 - mean(x2))
# Beta-distributed sigma in (0, 1) via inverse-logit of sigma_eta
sigma_p <- 1 / (1 + exp(-sigma_eta))
y <- rbeta(n, shape1 = 2 + 5 * sigma_p, shape2 = 5 - 4 * sigma_p)
d <- data.frame(y = y, x1 = x1, x2 = x2)
fit_het <- gdpar(
gdpar_bf(y ~ a(x1), sigma ~ a(x2)),
data = d,
family = list(
mu = gdpar_family("beta"),
sigma = gdpar_family("beta")
),
chains = 2L, iter_warmup = 400L, iter_sampling = 400L,
refresh = 0L
)
co <- coef(fit_het)
str(co, max.level = 2L)coef.gdpar_fit for K > 1 returns a named
list of gdpar_coef objects, one per slot (decision E4.A,
sub-phase 8.3.10). Each entry follows the standard scalar contract
(theta_ref, a, b, W
sub-components with posterior summaries) and can be coerced to a
long-tidy data frame via as.data.frame.
The heterogeneous path runs the same identifiability diagnostics as the homogeneous one:
identifiability_report slot
of gdpar_fit): C4-bis cross-slot Gram check, rank check of
each slot’s Z_a design, and slot-aware information
ratio.identifiability_post_fit
slot): information contraction per slot. A ratio below 0.1 emits
gdpar_information_error warning; in \([0.1, 0.5)\) emits
gdpar_information_warning; above 0.5 is silent.When the dispersion slot’s canonical kind is Beta or Gamma, the prior
on its anchor theta_ref_k[k] is the canonical one of the
slot’s family (not the location’s), via the placeholder
THETA_REF_PRIOR_BLOCK resolved at codegen time. This is the
decision D-ID per-slot of sub-phase 8.3.4 generalised to heterogeneous
specifications.
W with heterogeneous
familiesThe two refinements are orthogonal at the design level. A typical combined fit declares:
fit_combo <- gdpar(
gdpar_bf(y ~ a(x1) + W(), sigma ~ a(x2)),
data = d,
family = list(
mu = gdpar_family("gaussian"),
sigma = gdpar_family("beta")
),
W = W_basis(type = "bspline", knots = c(-1, 0, 1),
degree = 3L, boundary_knots = c(-2.5, 2.5)),
chains = 2L, iter_warmup = 400L, iter_sampling = 400L
)The W block is globally shared across slots
(canonical decision: “Scope of W: global”, handoff 28). The same
W_raw and sigma_W enter the linear predictor
of every slot, evaluated at the slot’s own theta_ref_k[k]
and anchored at the slot’s theta_anchor_K[k]. This is by
design and reflects the role of W as a structural coupler
in the AMM canonical form; the alternatives (W-per-slot, W-only-in-mu)
are queued as scope deudas for Session 8.4.
J_groups > 1): both coef.gdpar_fit and
predict.gdpar_fit with newdata raise
gdpar_unsupported_feature_error; the in-sample path
(predict(fit, newdata = NULL)) is supported. Session 8.4
will extend the per-slot extraction to per-group threading.W on new data
(predict(fit, newdata = ..., type = ...)): the in-sample
path supports B-spline through apply_W_basis_diff() in
Stan; the R-side reconstruction on new data presently mirrors only the
polynomial branch. Session 8.4 will lift this.K = 3+: sub-phase 8.3.7
covers K = 2 combinations only. K = 3
extensions (Student-t with heterogeneous dispersion, Tweedie with
heterogeneous power, mixtures with heterogeneous mixture weight) are
queued for Session 8.4.For the residual diagnostics that complement these intermediate
specifications — including gdpar_dharma_object() and the
three-layer G1 / G2 / G3 residual hierarchy — see
vignette("vop05_distributional_K_dharma", package = "gdpar").
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.