Parameter Priors

The parameter prior framework controls the distributions placed on real-valued model parameters: pairwise interactions, category thresholds, continuous-variable means, and the diagonal of the precision matrix. All priors inherit from BaseParameterPrior in src/priors/parameter_prior.h. The user-facing R-side constructors are documented on the Prior Constructors reference page.

BaseParameterPrior interface

class BaseParameterPrior {
  virtual double logp(double x) const = 0;
  virtual double logp(double x, double scale_factor) const;
  virtual double grad(double x) const = 0;
  virtual double grad(double x, double scale_factor) const;
  virtual std::unique_ptr<BaseParameterPrior> clone() const = 0;
};

Each prior provides a log-density logp(x) and gradient grad(x) evaluated at a single point \(x\), plus optional (x, scale_factor) overloads that multiply the prior’s scale by a per-pair factor. The scaled overloads are used by the OMRF and mixed MRF when standardize = TRUE (see below). Priors that have no natural scale (the beta-prime threshold prior) ignore the factor and delegate to the unscaled overload.

The standardize scale adjustment

With standardize = TRUE in bgm(), the prior scale for each pairwise interaction is adjusted to the range of the response scores. Variables with more response categories have larger score products \((x_i \cdot x_j)\), which typically correspond to smaller interaction effects \(\omega_{ij}\); without standardization, a fixed prior scale is relatively wide for these smaller effects, yielding less shrinkage for high-category pairs and more for low-category pairs. Standardization scales the prior proportionally to the maximum score product, equalizing relative shrinkage across pairs.

After internal recoding, regular ordinal variables have scores \(0, 1, \ldots, m\). The adjusted scale for the interaction between variables \(i\) and \(j\) is scale * m_i * m_j, so that scale itself applies to the unit-interval case (binary variables, where \(m_i = m_j = 1\)). For Blume-Capel variables with reference category \(b\), scores are centered as \(-b, \ldots, m-b\), and the adjustment uses the maximum absolute product of the score endpoints. For mixed pairs, ordinal variables use raw score endpoints \((0, m)\) and Blume-Capel variables use centered score endpoints \((-b, m-b)\).

clone() produces an independent copy for parallel chain execution.

Concrete classes

Class Family Hyperparameters R constructor
CauchyPrior Cauchy(0, scale) scale cauchy_prior()
NormalPrior Normal(0, scale) scale normal_prior()
BetaPrimePrior logit-Beta(\(\alpha\), \(\beta\)) alpha, beta beta_prime_prior()
GammaScalePrior Gamma(shape, rate) on positive support shape, rate gamma_prior(), exponential_prior()

CauchyPrior and NormalPrior implement both the unscaled and scaled overloads. BetaPrimePrior is used only for thresholds and ignores the scale factor. GammaScalePrior is used only for the precision-matrix diagonal where pair-specific scaling does not apply.

Factory dispatch

Two inline factories at the bottom of parameter_prior.h map a (type, hyperparameters) triple to the concrete class:

Factory Type strings Returns
create_parameter_prior(type, scale, alpha, beta) "cauchy", "normal", "beta-prime" unique_ptr<BaseParameterPrior>
create_scale_prior(type, shape, rate) "gamma", "exponential" unique_ptr<BaseParameterPrior> (GammaScalePrior)

The R-side unpack_parameter_prior() and unpack_scale_prior() helpers flatten each prior object into its (family, hyperparameters) representation; the C++ bridge then calls the matching factory.

Where the priors are used

Each model holds unique_ptr<BaseParameterPrior> members for the prior roles it needs:

Role Used by Default
Pairwise interactions GGM, OMRF, mixed MRF, bgmCompare cauchy_prior(scale = 1)
Category thresholds OMRF, mixed MRF, bgmCompare beta_prime_prior(0.5, 0.5)
Continuous means mixed MRF normal_prior(scale = 1)
Precision-matrix diagonal GGM, mixed MRF gamma_prior(shape = 1, rate = 1)

The model’s logp_and_gradient() and Metropolis update functions call into the held priors at every iteration. Replacing the inline R::dcauchy() / closed-form lambda calls used in earlier versions with this polymorphic dispatch is what allows the user to swap families without touching the C++ code.