Observation Models
Observation models define the relationship between observations y
and the latent GMRF field x
, typically through likelihood functions. They enable Bayesian inference by connecting your data to the underlying Gaussian process through flexible probabilistic models.
GaussianMarkovRandomFields.jl implements observation models using a factory pattern that separates model configuration from materialized evaluation instances. This design provides major performance benefits in optimization loops and cleaner automatic differentiation boundaries.
Core Concepts
The Factory Pattern
Observation models follow a two-stage pattern:
ObservationModel: A factory that defines the model structure and hyperparameters
ObservationLikelihood: A materialized instance with specific data and hyperparameters for fast evaluation
# Step 1: Configure observation model (factory)
obs_model = ExponentialFamily(Normal)
# Step 2: Materialize with data and hyperparameters
obs_lik = obs_model(y; σ=1.2)
# Step 3: Fast evaluation in hot loops
ll = loglik(x, obs_lik) # Only x argument needed!
grad = loggrad(x, obs_lik) # Fast x-only evaluation
hess = loghessian(x, obs_lik)
This pattern eliminates the need to repeatedly pass data and hyperparameters, providing significant performance benefits in optimization and sampling algorithms.
Evaluation Interface
All materialized observation likelihoods support a common interface:
loglik(x, obs_lik)
: Evaluate log-likelihoodloggrad(x, obs_lik)
: Compute gradient with respect to latent fieldloghessian(x, obs_lik)
: Compute Hessian matrix
Exponential Family Models
The most common observation models are exponential family distributions connected to the latent field through link functions.
Basic Usage
using GaussianMarkovRandomFields
using Distributions
# Poisson model for count data (canonical LogLink)
poisson_model = ExponentialFamily(Poisson)
x = [1.0, 2.0] # Latent field (log-intensity due to LogLink)
y = [2, 7] # Count observations
obs_lik = poisson_model(y)
ll = loglik(x, obs_lik)
# Normal model for continuous data (canonical IdentityLink)
normal_model = ExponentialFamily(Normal)
x = [1.5, 2.3] # Latent field (direct mean due to IdentityLink)
y = [1.2, 2.8] # Continuous observations
obs_lik = normal_model(y; σ=0.5) # Normal requires σ hyperparameter
ll = loglik(x, obs_lik)
# Bernoulli model for binary data (canonical LogitLink)
bernoulli_model = ExponentialFamily(Bernoulli)
x = [0.0, 1.5] # Latent field (logit-probability due to LogitLink)
y = [0, 1] # Binary observations
obs_lik = bernoulli_model(y)
ll = loglik(x, obs_lik)
Supported Distributions and Links
Distribution | Canonical Link | Alternative Links | Hyperparameters |
---|---|---|---|
Normal | IdentityLink | LogLink | σ (std. dev.) |
Poisson | LogLink | IdentityLink | none |
Bernoulli | LogitLink | LogLink | none |
Binomial | LogitLink | IdentityLink | none* |
*For Binomial, the number of trials is provided through the data structure BinomialObservations
, not as a hyperparameter.
Custom Link Functions
# Non-canonical link function
poisson_identity = ExponentialFamily(Poisson, IdentityLink())
# Note: Requires positive latent field values for valid Poisson intensities
Custom Observation Models
For models not covered by exponential families, you can define custom log-likelihood functions using automatic differentiation.
Basic AutoDiff Models
# Define custom log-likelihood function
function custom_loglik(x; y=[1.0, 2.0], σ=1.0)
μ = sin.(x) # Custom transformation
return -0.5 * sum((y .- μ).^2) / σ^2 - length(y) * log(σ)
end
# Create observation model
obs_model = AutoDiffObservationModel(custom_loglik; n_latent=2, hyperparams=(:y, :σ))
# Materialize with data
obs_lik = obs_model(y=[1.2, 1.8], σ=0.5)
# Use normally - gradients and Hessians computed automatically!
x = [0.5, 1.0]
ll = loglik(x, obs_lik)
grad = loggrad(x, obs_lik) # Automatic differentiation
hess = loghessian(x, obs_lik) # Potentially sparse!
Automatic Differentiation Requirements
AutoDiff observation models require an automatic differentiation backend. We support and recommend the following backends in order of preference:
Enzyme.jl (recommended for performance)
Mooncake.jl (good balance of performance and compatibility)
Zygote.jl (reliable fallback)
ForwardDiff.jl (for small problems)
# Load an AD backend (required for AutoDiffObservationModel)
using Enzyme # Recommended
# Or use another supported backend:
# using Mooncake
# using Zygote
# using ForwardDiff
# Now you can use AutoDiff models
obs_model = AutoDiffObservationModel(my_loglik; n_latent=10)
obs_lik = obs_model(y=data)
grad = loggrad(x, obs_lik) # Uses your loaded AD backend
Sparse Hessian Computation
AutoDiff observation models can automatically detect and exploit sparsity in Hessian matrices using our package extensions. This requires loading both an AD backend and additional sparsity packages:
# Load AD backend + sparse AD packages
using Enzyme # Or your preferred AD backend
using SparseConnectivityTracer, SparseMatrixColorings
# The package extension is automatically activated
obs_model = AutoDiffObservationModel(my_loglik; n_latent=100)
obs_lik = obs_model(y=data)
# Hessian computation now automatically:
# - Detects sparsity pattern using TracerSparsityDetector
# - Uses greedy coloring for efficient computation
# - Returns sparse matrix when beneficial
hess = loghessian(x, obs_lik) # May be sparse!
The sparse Hessian features provide dramatic performance improvements for large-scale problems with structured sparsity.
Nonlinear Least Squares
Use when observations are Gaussian with mean given by an arbitrary (possibly nonlinear) function of the latent field: y | x ~ Normal(f(x), σ)
.
Key properties
Out-of-place
f
: definef(x)::AbstractVector
with length equal tolength(y)
.Gauss–Newton: gradient and Hessian use the Gauss–Newton approximation (no exact Hessian term).
∇ℓ(x) = J(x)' (w ⊙ r)
, wherer = y − f(x)
,w = 1 ./ σ.^2
∇²ℓ(x) ≈ − J(x)' Diagonal(w) J(x)
σ
: accepts a scalar or vector (heteroskedastic), both interpreted as standard deviations.Sparse autodiff: requires loading
SparseConnectivityTracer
andSparseMatrixColorings
to activate the sparse Jacobian backend.
Example
using GaussianMarkovRandomFields
using SparseConnectivityTracer, SparseMatrixColorings # activate sparse Jacobian backend
# Nonlinear mapping f: R^2 -> R^3
f(x) = [x[1] + 2x[2], sin(x[1]), x[2]^2]
# Observations and noise
y = [1.0, 0.5, 2.0]
σ = [0.3, 0.4, 0.5] # vector sigma allowed
# Build model and materialize likelihood
model = NonlinearLeastSquaresModel(f, 2)
lik = model(y; σ=σ)
# Evaluate
x = [0.1, 0.2]
ll = loglik(x, lik)
g = loggrad(x, lik) # uses sparse DI.jacobian under the hood
H = loghessian(x, lik) # Gauss–Newton: -J' W J
# Conditional distribution p(y | x)
dist = conditional_distribution(model, x; σ=0.3)
API
GaussianMarkovRandomFields.NonlinearLeastSquaresModel Type
NonlinearLeastSquaresModel(f, n)
Observation model for nonlinear least squares with Gaussian noise: y | x ~ Normal(f(x), σ)
This model uses a Gauss–Newton approximation for the Hessian: ∇ℓ(x) = J(x)' (w ⊙ r), where r = y - f(x), w = 1 ./ σ.^2 ∇²ℓ(x) ≈ -J(x)' Diagonal(w) J(x)
Notes
Requires the sparse-AD extension (SparseConnectivityTracer + SparseMatrixColorings) to be loaded. If missing, construction or evaluation will error with a clear message.
f
must be out-of-place with signaturef(x)::AbstractVector
of the same length asy
.σ
can be a scalar or a vector matchinglength(y)
(heteroskedastic case). It must be positive.
GaussianMarkovRandomFields.NonlinearLeastSquaresLikelihood Type
NonlinearLeastSquaresLikelihood <: ObservationLikelihood
Materialized likelihood for NonlinearLeastSquaresModel with precomputed weights and cached sparse-Jacobian state. Hessian uses Gauss–Newton.
sourceAdvanced Features
Linear Transformations and Design Matrices
For GLM-style modeling where observations are related to linear combinations of latent field components:
# Design matrix mapping latent field to linear predictors
# Rows = observations, Columns = latent components
A = [1.0 20.0 1.0 0.0; # obs 1: intercept + temp + group1
1.0 25.0 1.0 0.0; # obs 2: intercept + temp + group1
1.0 30.0 0.0 1.0] # obs 3: intercept + temp + group2
base_model = ExponentialFamily(Poisson) # LogLink by default
obs_model = LinearlyTransformedObservationModel(base_model, A)
# Latent field now includes all components: [β₀, β₁, u₁, u₂]
x_full = [2.0, 0.1, -0.5, 0.3] # intercept, slope, group effects
obs_lik = obs_model(y)
ll = loglik(x_full, obs_lik) # Chain rule applied automatically
Binomial Observations
For binomial data, use the BinomialObservations
utility:
# Create binomial observations with successes and trials
y = BinomialObservations([3, 1, 4], [5, 8, 6]) # (successes, trials) pairs
# Use with Binomial model
binomial_model = ExponentialFamily(Binomial)
obs_lik = binomial_model(y)
# Access components
successes(y) # [3, 1, 4]
trials(y) # [5, 8, 6]
y[1] # (3, 5) - tuple access
Composite Observations
For multiple observation types in a single model:
# Multiple observation vectors
count_data = [1, 3, 0, 2]
binary_data = [0, 1, 1, 0]
obs = CompositeObservations(count_data, binary_data)
# Corresponding models for each observation type
poisson_model = ExponentialFamily(Poisson)
bernoulli_model = ExponentialFamily(Bernoulli)
composite_model = CompositeObservationModel(poisson_model, bernoulli_model)
obs_lik = composite_model(obs)
# Latent field x now corresponds to concatenated observations
ll = loglik(x, obs_lik)
API Reference
Core Types and Interface
GaussianMarkovRandomFields.ObservationModel Type
ObservationModel
Abstract base type for all observation models for GMRFs.
An observation model defines the relationship between observations y
and the latent field x
, typically through a likelihood function. ObservationModel types serve as factories for creating ObservationLikelihood instances via callable syntax.
Usage Pattern
# Step 1: Create observation model (factory)
obs_model = ExponentialFamily(Normal)
# Step 2: Materialize with data and hyperparameters
obs_lik = obs_model(y; σ=1.2) # Creates ObservationLikelihood
# Step 3: Use materialized likelihood in hot loops
ll = loglik(x, obs_lik) # Fast x-only evaluation
See also: ObservationLikelihood
, ExponentialFamily
GaussianMarkovRandomFields.ObservationLikelihood Type
ObservationLikelihood
Abstract base type for materialized observation likelihoods.
Observation likelihoods are created by materializing an observation model with specific hyperparameters θ and observed data y. They provide efficient evaluation methods that only depend on the latent field x, eliminating the need to repeatedly pass θ and y.
This design provides major performance benefits in optimization loops and cleaner automatic differentiation boundaries.
Usage Pattern
# Step 1: Configure observation model (factory)
obs_model = ExponentialFamily(Normal)
# Step 2: Materialize with data and hyperparameters
obs_lik = obs_model(y; σ=1.2)
# Step 3: Fast evaluation in hot loops
ll = loglik(x, obs_lik) # Only x argument needed!
grad = loggrad(x, obs_lik) # Fast x-only evaluation
GaussianMarkovRandomFields.hyperparameters Method
hyperparameters(obs_model::ObservationModel) -> Tuple{Vararg{Symbol}}
Return a tuple of required hyperparameter names for this observation model.
This method defines which hyperparameters the observation model expects to receive when materializing an ObservationLikelihood instance.
Arguments
obs_model
: An observation model implementing theObservationModel
interface
Returns
Tuple{Vararg{Symbol}}
: Tuple of parameter names (e.g.,(:σ,)
or(:α, :β)
)
Example
hyperparameters(ExponentialFamily(Normal)) == (:σ,)
hyperparameters(ExponentialFamily(Bernoulli)) == ()
Implementation
All observation models should implement this method. The default returns an empty tuple.
sourceGaussianMarkovRandomFields.latent_dimension Function
latent_dimension(obs_model::ObservationModel, y::AbstractVector) -> Union{Int, Nothing}
Return the latent field dimension for this observation model given observations y.
For most observation models, this will be length(y)
(1:1 mapping). For transformed observation models like LinearlyTransformedObservationModel
, this will be the dimension of the design matrix.
Returns nothing
if the latent dimension cannot be determined automatically.
Arguments
obs_model
: An observation model implementing theObservationModel
interfacey
: Vector of observations
Returns
Int
: The latent field dimension, ornothing
if unknown
Example
latent_dimension(ExponentialFamily(Normal), y) == length(y)
latent_dimension(LinearlyTransformedObservationModel(base, A), y) == size(A, 2)
latent_dimension(ef::ExponentialFamily, y::AbstractVector) -> Int
Return the latent field dimension for exponential family models.
For ExponentialFamily models, there is a direct 1:1 mapping between observations and latent field components, so the latent dimension equals the observation dimension.
sourceGaussianMarkovRandomFields.loglik Function
loglik(x, lik::ExponentialFamilyLikelihood) -> Float64
Generic loglik implementation for all exponential family likelihoods using product_distribution.
sourceloglik(x, lik::NormalLikelihood) -> Float64
Specialized fast implementation for Normal likelihood that avoids product_distribution overhead.
Computes: ∑ᵢ logpdf(Normal(μᵢ, σ), yᵢ) = -n/2 * log(2π) - n * log(σ) - 1/(2σ²) * ∑ᵢ(yᵢ - μᵢ)²
sourceloglik(x, composite_lik::CompositeLikelihood) -> Float64
Compute the log-likelihood of a composite likelihood by summing component contributions.
Each component likelihood receives the full latent field x
and contributes to the total log-likelihood. This handles cases where components may have overlapping dependencies on the latent field.
loglik(x, obs_lik::AutoDiffLikelihood) -> Real
Evaluate the log-likelihood function at latent field x
.
Calls the stored log-likelihood function, which typically includes all necessary hyperparameters and data as a closure.
sourceGaussianMarkovRandomFields.loggrad Function
loggrad(x, obs_lik::ObservationLikelihood) -> Vector{Float64}
Automatic differentiation fallback for ObservationLikelihood gradient computation.
sourceloggrad(x, lik::NormalLikelihood{IdentityLink}) -> Vector{Float64}
Compute gradient of Normal likelihood with canonical identity link w.r.t. latent field x.
sourceloggrad(x, lik::PoissonLikelihood{LogLink}) -> Vector{Float64}
Compute gradient of Poisson likelihood with canonical log link w.r.t. latent field x.
sourceloggrad(x, lik::BernoulliLikelihood{LogitLink}) -> Vector{Float64}
Compute gradient of Bernoulli likelihood with canonical logit link w.r.t. latent field x.
sourceloggrad(x, lik::BinomialLikelihood{LogitLink}) -> Vector{Float64}
Compute gradient of Binomial likelihood with canonical logit link w.r.t. latent field x.
sourceloggrad(x, lik::ExponentialFamilyLikelihood) -> Vector{Float64}
Optimized chain rule implementation for exponential family likelihoods. Handles indexing via helper embedding.
sourceloggrad(x, composite_lik::CompositeLikelihood) -> Vector{Float64}
Compute the gradient of the log-likelihood by summing component gradients.
Each component contributes its gradient with respect to the full latent field x
. For overlapping dependencies, gradients are automatically summed at each latent field element.
GaussianMarkovRandomFields.loghessian Function
loghessian(x, obs_lik::ObservationLikelihood) -> AbstractMatrix{Float64}
Automatic differentiation fallback for ObservationLikelihood Hessian computation.
sourceloghessian(x, lik::NormalLikelihood{IdentityLink}) -> Diagonal{Float64}
Compute Hessian of Normal likelihood with canonical identity link w.r.t. latent field x.
sourceloghessian(x, lik::PoissonLikelihood{LogLink}) -> Diagonal{Float64}
Compute Hessian of Poisson likelihood with canonical log link w.r.t. latent field x.
sourceloghessian(x, lik::BernoulliLikelihood{LogitLink}) -> Diagonal{Float64}
Compute Hessian of Bernoulli likelihood with canonical logit link w.r.t. latent field x.
sourceloghessian(x, lik::BinomialLikelihood{LogitLink}) -> Diagonal{Float64}
Compute Hessian of Binomial likelihood with canonical logit link w.r.t. latent field x.
sourceloghessian(x, lik::ExponentialFamilyLikelihood) -> Diagonal{Float64}
Optimized chain rule implementation for exponential family likelihoods. Handles indexing via helper embedding.
sourceloghessian(x, composite_lik::CompositeLikelihood) -> AbstractMatrix{Float64}
Compute the Hessian of the log-likelihood by summing component Hessians.
Each component contributes its Hessian with respect to the full latent field x
. For overlapping dependencies, Hessians are automatically summed element-wise.
Exponential Family Models
GaussianMarkovRandomFields.ExponentialFamily Type
ExponentialFamily{F<:Distribution, L<:LinkFunction} <: ObservationModel
Observation model for exponential family distributions with link functions.
This struct represents observation models where the observations come from an exponential family distribution (Normal, Poisson, Bernoulli, Binomial) and the mean parameter is related to the latent field through a link function.
Mathematical Model
For observations yᵢ and latent field values xᵢ:
Linear predictor: ηᵢ = xᵢ
Mean parameter: μᵢ = g⁻¹(ηᵢ) where g is the link function
Observations: yᵢ ~ F(μᵢ, θ) where F is the distribution family
Fields
family::Type{F}
: The distribution family (e.g.,Poisson
,Bernoulli
)link::L
: The link function connecting mean parameters to linear predictors
Type Parameters
F
: A subtype ofDistribution
from Distributions.jlL
: A subtype ofLinkFunction
Constructors
# Use canonical link (recommended)
ExponentialFamily(Poisson) # Uses LogLink()
ExponentialFamily(Bernoulli) # Uses LogitLink()
ExponentialFamily(Normal) # Uses IdentityLink()
# Specify custom link function
ExponentialFamily(Poisson, IdentityLink()) # Non-canonical
Supported Combinations
Normal
withIdentityLink
(canonical) orLogLink
Poisson
withLogLink
(canonical) orIdentityLink
Bernoulli
withLogitLink
(canonical) orLogLink
Binomial
withLogitLink
(canonical) orIdentityLink
Hyperparameters (θ)
Different families require different hyperparameters:
Normal
:θ = [σ]
(standard deviation)Poisson
:θ = []
(no hyperparameters)Bernoulli
:θ = []
(no hyperparameters)Binomial
:θ = [n]
(number of trials)
Examples
# Poisson model for count data
model = ExponentialFamily(Poisson)
x = [1.0, 2.0] # Latent field (log scale due to LogLink)
θ = Float64[] # No hyperparameters
y = [2, 7] # Count observations
obs_lik = obs_model(y; θ_named...)
ll = loglik(x, obs_lik)
dist = conditional_distribution(model, x, θ)
# Bernoulli model for binary data
model = ExponentialFamily(Bernoulli)
x = [0.0, 1.0] # Latent field (logit scale due to LogitLink)
y = [0, 1] # Binary observations
Performance Notes
Canonical link functions have optimized implementations that avoid redundant computations. Non-canonical links use general chain rule formulations which may be slower.
See also: LinkFunction
, loglik
, conditional_distribution
GaussianMarkovRandomFields.conditional_distribution Function
conditional_distribution(obs_model::ObservationModel, x; kwargs...) -> Distribution
Construct the conditional distribution p(y | x, θ) for sampling new observations.
This function returns a Distribution object that represents the probability distribution over observations y given latent field values x and hyperparameters θ. It is used for sampling new observations from the observation model.
Arguments
obs_model
: An observation model implementing theObservationModel
interfacex
: Latent field values (vector)kwargs...
: Hyperparameters as keyword arguments
Returns
Distribution object that can be used with rand()
to generate observations
Example
model = ExponentialFamily(Poisson)
x = [1.0, 2.0]
dist = conditional_distribution(model, x) # Poisson has no hyperparameters
y = rand(dist) # Sample observations
# For Normal distribution with hyperparameters
model_normal = ExponentialFamily(Normal)
x = [0.0, 1.0]
dist = conditional_distribution(model_normal, x; σ=0.5)
y = rand(dist) # Sample observations
Implementation
All observation models should implement this method. The default throws an error.
sourceconditional_distribution(obs_model::ExponentialFamily, x; θ_named...) -> Distribution
Construct the data-generating distribution p(y | x, θ).
This function returns a Distribution object that represents the probability distribution over observations y given latent field values x and hyperparameters θ. It is used for sampling new observations.
Arguments
obs_model
: An ExponentialFamily observation modelx
: Latent field values (vector)θ_named...
: Hyperparameters as keyword arguments
Returns
Distribution object that can be used with rand()
to generate observations
Example
model = ExponentialFamily(Poisson)
x = [1.0, 2.0]
dist = conditional_distribution(model, x) # Poisson has no hyperparameters
y = rand(dist) # Sample observations
# For Normal distribution with hyperparameters
model_normal = ExponentialFamily(Normal)
x = [0.0, 1.0]
dist = conditional_distribution(model_normal, x; σ=0.5)
y = rand(dist) # Sample observations
Note: This API now follows the same convention as materializing likelihoods:
obs_lik = obs_model(y; θ_named...) # Materialize likelihood
ll = loglik(x, obs_lik) # Evaluate likelihood
dist = conditional_distribution(obs_model, x; θ_named...) # Create distribution
y_new = rand(dist) # Sample new data
GaussianMarkovRandomFields.ExponentialFamilyLikelihood Type
ExponentialFamilyLikelihood{L, I} <: ObservationLikelihood
Abstract type for exponential family observation likelihoods.
This intermediate type allows for generic implementations that work across all exponential family distributions while still allowing specialized methods for specific combinations.
Type Parameters
L
: Link function typeI
: Index type (Nothing for non-indexed, UnitRange or Vector for indexed)
GaussianMarkovRandomFields.NormalLikelihood Type
NormalLikelihood{L<:LinkFunction} <: ObservationLikelihood
Materialized Normal observation likelihood with precomputed hyperparameters.
Fields
link::L
: Link function connecting latent field to mean parametery::Vector{Float64}
: Observed dataσ::Float64
: Standard deviation hyperparameterinv_σ²::Float64
: Precomputed 1/σ² for performancelog_σ::Float64
: Precomputed log(σ) for log-likelihood computation
Example
obs_model = ExponentialFamily(Normal)
obs_lik = obs_model([1.0, 2.0, 1.5]; σ=0.5) # NormalLikelihood{IdentityLink}
ll = loglik([0.9, 2.1, 1.4], obs_lik)
GaussianMarkovRandomFields.PoissonLikelihood Type
PoissonLikelihood{L<:LinkFunction} <: ObservationLikelihood
Materialized Poisson observation likelihood.
Fields
link::L
: Link function connecting latent field to rate parametery::Vector{Int}
: Count observations
Example
obs_model = ExponentialFamily(Poisson) # Uses LogLink by default
obs_lik = obs_model([1, 3, 0, 2]) # PoissonLikelihood{LogLink}
ll = loglik([0.0, 1.1, -2.0, 0.7], obs_lik) # x values on log scale
GaussianMarkovRandomFields.BernoulliLikelihood Type
BernoulliLikelihood{L<:LinkFunction} <: ObservationLikelihood
Materialized Bernoulli observation likelihood for binary data.
Fields
link::L
: Link function connecting latent field to probability parametery::Vector{Int}
: Binary observations (0 or 1)
Example
obs_model = ExponentialFamily(Bernoulli) # Uses LogitLink by default
obs_lik = obs_model([1, 0, 1, 0]) # BernoulliLikelihood{LogitLink}
ll = loglik([0.5, -0.2, 1.1, -0.8], obs_lik) # x values on logit scale
GaussianMarkovRandomFields.BinomialLikelihood Type
BinomialLikelihood{L<:LinkFunction} <: ObservationLikelihood
Materialized Binomial observation likelihood.
Fields
link::L
: Link function connecting latent field to probability parametery::Vector{Int}
: Number of successes for each trialn::Vector{Int}
: Number of trials per observation (can vary across observations)
Example
obs_model = ExponentialFamily(Binomial) # Uses LogitLink by default
obs_lik = obs_model([3, 1, 4]; trials=[5, 8, 6]) # BinomialLikelihood{LogitLink}
ll = loglik([0.2, -1.0, 0.8], obs_lik) # x values on logit scale
Link Functions
GaussianMarkovRandomFields.LinkFunction Type
LinkFunction
Abstract base type for link functions used in exponential family models.
A link function g(μ) connects the mean parameter μ of a distribution to the linear predictor η through the relationship g(μ) = η, or equivalently μ = g⁻¹(η).
Implemented Link Functions
IdentityLink
: g(μ) = μ (for Normal distributions)LogLink
: g(μ) = log(μ) (for Poisson distributions)LogitLink
: g(μ) = logit(μ) (for Bernoulli/Binomial distributions)
Interface
Concrete link functions must implement:
apply_link(link, μ)
: Apply the link function g(μ)apply_invlink(link, η)
: Apply the inverse link function g⁻¹(η)
For performance in GMRF computations, they should also implement:
derivative_invlink(link, η)
: First derivative of g⁻¹(η)second_derivative_invlink(link, η)
: Second derivative of g⁻¹(η)
See also: ExponentialFamily
, apply_link
, apply_invlink
GaussianMarkovRandomFields.IdentityLink Type
IdentityLink <: LinkFunction
Identity link function: g(μ) = μ.
This is the canonical link for Normal distributions. The mean parameter μ is directly equal to the linear predictor η.
Mathematical Definition
Link: g(μ) = μ
Inverse link: g⁻¹(η) = η
First derivative: d/dη g⁻¹(η) = 1
Second derivative: d²/dη² g⁻¹(η) = 0
Example
link = IdentityLink()
μ = apply_invlink(link, 1.5) # μ = 1.5
η = apply_link(link, μ) # η = 1.5
GaussianMarkovRandomFields.LogLink Type
LogLink <: LinkFunction
Logarithmic link function: g(μ) = log(μ).
This is the canonical link for Poisson and Gamma distributions. It ensures the mean parameter μ remains positive by mapping the real-valued linear predictor η to μ = exp(η).
Mathematical Definition
Link: g(μ) = log(μ)
Inverse link: g⁻¹(η) = exp(η)
First derivative: d/dη g⁻¹(η) = exp(η)
Second derivative: d²/dη² g⁻¹(η) = exp(η)
Example
link = LogLink()
μ = apply_invlink(link, 1.0) # μ = exp(1.0) ≈ 2.718
η = apply_link(link, μ) # η = log(μ) = 1.0
See also: IdentityLink
, LogitLink
GaussianMarkovRandomFields.LogitLink Type
LogitLink <: LinkFunction
Logit link function: g(μ) = logit(μ) = log(μ/(1-μ)).
This is the canonical link for Bernoulli and Binomial distributions. It maps probabilities μ ∈ (0,1) to the real line via the logistic transformation, ensuring μ = logistic(η) = 1/(1+exp(-η)) remains a valid probability.
Mathematical Definition
Link: g(μ) = logit(μ) = log(μ/(1-μ))
Inverse link: g⁻¹(η) = logistic(η) = 1/(1+exp(-η))
First derivative: d/dη g⁻¹(η) = μ(1-μ) where μ = logistic(η)
Second derivative: d²/dη² g⁻¹(η) = μ(1-μ)(1-2μ)
Example
link = LogitLink()
μ = apply_invlink(link, 0.0) # μ = logistic(0.0) = 0.5
η = apply_link(link, μ) # η = logit(0.5) = 0.0
See also: IdentityLink
, LogLink
GaussianMarkovRandomFields.apply_link Function
apply_link(link::LinkFunction, μ) -> Real
Apply the link function g(μ) to transform mean parameters to linear predictor scale.
This function computes η = g(μ), where g is the link function. This transformation is typically used to ensure the mean parameter satisfies appropriate constraints (e.g., positivity for Poisson, probability bounds for Bernoulli).
Arguments
link
: A link function (IdentityLink, LogLink, or LogitLink)μ
: Mean parameter value(s) in the natural parameter space
Returns
The transformed value(s) η on the linear predictor scale
Examples
apply_link(LogLink(), 2.718) # ≈ 1.0
apply_link(LogitLink(), 0.5) # = 0.0
apply_link(IdentityLink(), 1.5) # = 1.5
See also: apply_invlink
, LinkFunction
GaussianMarkovRandomFields.apply_invlink Function
apply_invlink(link::LinkFunction, η) -> Real
Apply the inverse link function g⁻¹(η) to transform linear predictor to mean parameters.
This function computes μ = g⁻¹(η), where g⁻¹ is the inverse link function. This is the primary transformation used in GMRF models to convert the latent field values to the natural parameter space of the observation distribution.
Arguments
link
: A link function (IdentityLink, LogLink, or LogitLink)η
: Linear predictor value(s)
Returns
The transformed value(s) μ in the natural parameter space
Examples
apply_invlink(LogLink(), 1.0) # ≈ 2.718 (= exp(1))
apply_invlink(LogitLink(), 0.0) # = 0.5 (= logistic(0))
apply_invlink(IdentityLink(), 1.5) # = 1.5
See also: apply_link
, LinkFunction
Custom AutoDiff Models
GaussianMarkovRandomFields.AutoDiffObservationModel Type
AutoDiffObservationModel{F, B, SB, H} <: ObservationModel
Observation model that uses automatic differentiation for a user-provided log-likelihood function.
This serves as a factory for creating AutoDiffLikelihood instances. The user provides a log-likelihood function that can accept hyperparameters, and when materialized, creates a closure with the hyperparameters baked in.
Type Parameters
F
: Type of the log-likelihood functionB
: Type of the AD backend for gradientsSB
: Type of the AD backend for HessiansH
: Type of the hyperparameters tuple
Fields
loglik_func::F
: User-provided log-likelihood function with signature(x; kwargs...) -> Real
n_latent::Int
: Number of latent field componentsgrad_backend::B
: AD backend for gradient computationhess_backend::SB
: AD backend for Hessian computationhyperparams::H
: Tuple of hyperparameter names that this model expects
Usage
# Define your log-likelihood function with hyperparameters
function my_loglik(x; σ=1.0, y=[1.0, 2.0])
μ = x # or some transformation of x
return -0.5 * sum((y .- μ).^2) / σ^2 - length(y) * log(σ)
end
# Create observation model specifying expected hyperparameters
obs_model = AutoDiffObservationModel(my_loglik; n_latent=2, hyperparams=(:σ, :y))
# Materialize with specific hyperparameters
obs_lik = obs_model(σ=0.5, y=[1.2, 1.8]) # Creates AutoDiffLikelihood
# Use normally
ll = loglik(x, obs_lik)
grad = loggrad(x, obs_lik)
hess = loghessian(x, obs_lik)
See also: AutoDiffLikelihood
, ObservationModel
GaussianMarkovRandomFields.AutoDiffLikelihood Type
AutoDiffLikelihood{F, B, SB, GP, HP} <: ObservationLikelihood
Automatic differentiation-based observation likelihood that wraps a user-provided log-likelihood function.
This is a materialized likelihood created from an AutoDiffObservationModel. The log-likelihood function is typically a closure that already includes hyperparameters and data.
Type Parameters
F
: Type of the log-likelihood function (usually a closure)B
: Type of the AD backend for gradientsSB
: Type of the AD backend for HessiansGP
: Type of the gradient preparation objectHP
: Type of the Hessian preparation object
Fields
loglik_func::F
: Log-likelihood function with signature(x) -> Real
grad_backend::B
: AD backend for gradient computationhess_backend::SB
: AD backend for Hessian computationgrad_prep::GP
: Preparation object for gradient computationhess_prep::HP
: Preparation object for Hessian computation
Usage
Typically created via AutoDiffObservationModel factory:
# Define your log-likelihood function with hyperparameters
function my_loglik(x; σ=1.0, y=[1.0, 2.0])
μ = x # or some transformation of x
return -0.5 * sum((y .- μ).^2) / σ^2 - length(y) * log(σ)
end
# Create observation model
obs_model = AutoDiffObservationModel(my_loglik, n_latent=2)
# Materialize with data and hyperparameters
obs_lik = obs_model(σ=0.5, y=[1.2, 1.8]) # Creates AutoDiffLikelihood
# Use in the standard way
x = [1.1, 1.9]
ll = loglik(x, obs_lik)
grad = loggrad(x, obs_lik) # Uses prepared AD with optimal backends
hess = loghessian(x, obs_lik) # Automatically sparse when available!
Sparse Hessian Features
The Hessian computation automatically:
Detects sparsity pattern using TracerSparsityDetector
Uses greedy coloring for efficient computation
Returns a sparse matrix when beneficial
Falls back to dense computation for small problems
See also: loglik
, loggrad
, loghessian
FEM Helper Functions
GaussianMarkovRandomFields.PointEvaluationObsModel Function
PointEvaluationObsModel(fem::FEMDiscretization, points::AbstractMatrix, family::Type{<:Distribution}) -> LinearlyTransformedObservationModel
Create an observation model for observing function values at specified points using a FEM discretization.
This helper automatically constructs a LinearlyTransformedObservationModel
where:
The design matrix comes from
evaluation_matrix(fem, points)
The base observation model is
ExponentialFamily(family)
Arguments
fem
: FEM discretization defining the basis functionspoints
: N×D matrix where each row is a spatial point to observefamily
: Distribution family for observations (e.g.,Normal
,Poisson
)
Returns
LinearlyTransformedObservationModel
ready for materialization with data
Example
# Create FEM discretization
grid = generate_grid(Triangle, (20, 20))
ip = Lagrange{RefTriangle, 1}()
qr = QuadratureRule{RefTriangle}(2)
fem = FEMDiscretization(grid, ip, qr)
# Observation points
points = [0.1 0.2; 0.5 0.7; 0.9 0.3] # 3 points in 2D
# Create observation model for Poisson count data
obs_model = PointEvaluationObsModel(fem, points, Poisson)
# Materialize with data
y = [2, 5, 1] # Count observations
obs_lik = obs_model(y) # Ready for loglik evaluation
GaussianMarkovRandomFields.PointDerivativeObsModel Function
PointDerivativeObsModel(fem::FEMDiscretization, points::AbstractMatrix, family::Type{<:Distribution}; derivative_idcs=nothing) -> LinearlyTransformedObservationModel
Create an observation model for observing first derivatives at specified points using a FEM discretization.
This helper automatically constructs a LinearlyTransformedObservationModel
where:
The design matrix comes from
derivative_matrices(fem, points; derivative_idcs)
The base observation model is
ExponentialFamily(family)
Arguments
fem
: FEM discretization defining the basis functionspoints
: N×D matrix where each row is a spatial point to observe derivativesfamily
: Distribution family for observations (e.g.,Normal
,Poisson
)
Keywords
derivative_idcs
: Indices of partial derivatives to compute. Defaults to[1, 2, ..., ndim(fem)]
(all spatial directions)
Returns
LinearlyTransformedObservationModel
ready for materialization with data
Example
# Create observation model for x-derivatives of a 2D function
obs_model = PointDerivativeObsModel(fem, points, Normal; derivative_idcs=[1])
# Or observe both x and y derivatives
obs_model = PointDerivativeObsModel(fem, points, Normal; derivative_idcs=[1, 2])
# This creates concatenated observations: [∂u/∂x at all points, ∂u/∂y at all points]
# Materialize with gradient data
y = [0.1, 0.3, -0.2, 0.5, 0.1, 0.0] # [∂u/∂x₁, ∂u/∂x₂, ∂u/∂x₃, ∂u/∂y₁, ∂u/∂y₂, ∂u/∂y₃]
obs_lik = obs_model(y; σ=0.1)
GaussianMarkovRandomFields.PointSecondDerivativeObsModel Function
PointSecondDerivativeObsModel(fem::FEMDiscretization, points::AbstractMatrix, family::Type{<:Distribution}; derivative_idcs=nothing) -> LinearlyTransformedObservationModel
Create an observation model for observing second derivatives at specified points using a FEM discretization.
This helper automatically constructs a LinearlyTransformedObservationModel
where:
The design matrix comes from
second_derivative_matrices(fem, points; derivative_idcs)
The base observation model is
ExponentialFamily(family)
Arguments
fem
: FEM discretization defining the basis functionspoints
: N×D matrix where each row is a spatial point to observe second derivativesfamily
: Distribution family for observations (e.g.,Normal
,Poisson
)
Keywords
derivative_idcs
: Indices of second partial derivatives to compute. Defaults to diagonal terms[(1,1), (2,2), ...]
up tondim(fem)
Returns
LinearlyTransformedObservationModel
ready for materialization with data
Example
# Create observation model for Laplacian (∂²u/∂x² + ∂²u/∂y²) observations
obs_model = PointSecondDerivativeObsModel(fem, points, Normal; derivative_idcs=[(1,1), (2,2)])
# Materialize with Laplacian data
y = [0.05, -0.1, 0.2, 0.15, -0.05, 0.1] # [∂²u/∂x²₁, ∂²u/∂x²₂, ∂²u/∂x²₃, ∂²u/∂y²₁, ∂²u/∂y²₂, ∂²u/∂y²₃]
obs_lik = obs_model(y; σ=0.01)
Advanced Features
GaussianMarkovRandomFields.LinearlyTransformedObservationModel Type
LinearlyTransformedObservationModel{M, A} <: ObservationModel
Observation model that applies a linear transformation to the latent field before passing to a base observation model. This enables GLM-style modeling with design matrices while maintaining full compatibility with existing observation models.
Mathematical Foundation
The wrapper transforms the full latent field x_full to linear predictors η via a design matrix A:
η = A * x_full
Base model operates on η as usual: p(y | η, θ)
Chain rule applied for gradients/Hessians:
∇_{x_full} ℓ = A^T ∇_η ℓ
∇²_{x_full} ℓ = A^T ∇²_η ℓ A
Type Parameters
M <: ObservationModel
: Type of the base observation modelA
: Type of the design matrix (typically AbstractMatrix)
Fields
base_model::M
: The underlying observation model that operates on linear predictorsdesign_matrix::A
: Matrix mapping full latent field to observation-specific linear predictors
Usage Pattern
# Step 1: Create base observation model
base_model = ExponentialFamily(Poisson) # LogLink by default
# Step 2: Create design matrix (maps latent field to linear predictors)
# For: y ~ intercept + temperature + group_effects
A = [1.0 20.0 1.0 0.0 0.0; # obs 1: intercept + temp + group1
1.0 25.0 1.0 0.0 0.0; # obs 2: intercept + temp + group1
1.0 30.0 0.0 1.0 0.0; # obs 3: intercept + temp + group2
1.0 15.0 0.0 0.0 1.0] # obs 4: intercept + temp + group3
# Step 3: Create wrapped model
obs_model = LinearlyTransformedObservationModel(base_model, A)
# Step 4: Use in GMRF model - latent field now includes all components
# x_full = [β₀, β₁, u₁, u₂, u₃] # intercept, slope, group effects
# Step 5: Materialize with data and hyperparameters
obs_lik = obs_model(y; σ=1.2) # Creates LinearlyTransformedLikelihood
# Step 6: Fast evaluation in optimization loops
ll = loglik(x_full, obs_lik)
Hyperparameters
All hyperparameters come from the base observation model. The design matrix introduces no new hyperparameters - it's a fixed linear transformation.
See also: LinearlyTransformedLikelihood
, ExponentialFamily
, ObservationModel
GaussianMarkovRandomFields.LinearlyTransformedLikelihood Type
LinearlyTransformedLikelihood{L, A} <: ObservationLikelihood
Materialized likelihood for LinearlyTransformedObservationModel with precomputed base likelihood and design matrix.
This is created by calling a LinearlyTransformedObservationModel instance with data and hyperparameters, following the factory pattern used throughout the package.
Type Parameters
L <: ObservationLikelihood
: Type of the materialized base likelihoodA
: Type of the design matrix
Fields
base_likelihood::L
: Materialized base observation likelihood (contains y and θ)design_matrix::A
: Design matrix mapping full latent field to linear predictors
Usage
This type is typically created automatically:
ltom = LinearlyTransformedObservationModel(base_model, design_matrix)
ltlik = ltom(y; σ=1.2) # Creates LinearlyTransformedLikelihood
ll = loglik(x_full, ltlik) # Fast evaluation
GaussianMarkovRandomFields.BinomialObservations Type
BinomialObservations <: AbstractVector{Tuple{Int, Int}}
Combined observation type for binomial data containing both successes and trials.
This type packages binomial observation data (number of successes and trials) into a single vector-like object where each element is a (successes, trials) tuple.
Fields
successes::Vector{Int}
: Number of successes for each observationtrials::Vector{Int}
: Number of trials for each observation
Example
# Create binomial observations
y = BinomialObservations([3, 1, 4], [5, 8, 6])
# Access as tuples
y[1] # (3, 5)
y[2] # (1, 8)
GaussianMarkovRandomFields.successes Function
successes(y::BinomialObservations) -> Vector{Int}
Extract the successes vector from binomial observations.
sourceGaussianMarkovRandomFields.trials Function
trials(y::BinomialObservations) -> Vector{Int}
Extract the trials vector from binomial observations.
sourceGaussianMarkovRandomFields.CompositeObservations Type
CompositeObservations{T<:Tuple} <: AbstractVector{Float64}
A composite observation vector that stores observation data as a tuple of component vectors.
This type implements the AbstractVector
interface and allows combining different observation datasets while maintaining their structure. The composite vector presents a unified view where indexing delegates to the appropriate component vector.
Fields
components::T
: Tuple of observation vectors, one per likelihood component
Example
y1 = [1.0, 2.0, 3.0] # Gaussian observations
y2 = [4.0, 5.0] # More Gaussian observations
y_composite = CompositeObservations((y1, y2))
length(y_composite) # 5
y_composite[1] # 1.0
y_composite[4] # 4.0
collect(y_composite) # [1.0, 2.0, 3.0, 4.0, 5.0]
GaussianMarkovRandomFields.CompositeObservationModel Type
CompositeObservationModel{T<:Tuple} <: ObservationModel
An observation model that combines multiple component observation models.
This type follows the factory pattern - it stores component observation models and creates CompositeLikelihood
instances when called with observation data and hyperparameters.
Fields
components::T
: Tuple of component observation models for type stability
Example
gaussian_model = ExponentialFamily(Normal)
poisson_model = ExponentialFamily(Poisson)
composite_model = CompositeObservationModel((gaussian_model, poisson_model))
# Materialize with data and hyperparameters
y_composite = CompositeObservations(([1.0, 2.0], [3, 4]))
composite_lik = composite_model(y_composite; σ=1.5)
GaussianMarkovRandomFields.CompositeLikelihood Type
CompositeLikelihood{T<:Tuple} <: ObservationLikelihood
A materialized composite likelihood that combines multiple component likelihoods.
Created by calling a CompositeObservationModel
with observation data and hyperparameters. Provides efficient evaluation of log-likelihood, gradient, and Hessian by summing contributions from all component likelihoods.
Fields
components::T
: Tuple of materialized component likelihoods