@randomEffects
Random effects capture between-group variability in nonlinear mixed-effects models, allowing individual-level (or site-level, cluster-level, etc.) deviations from population parameters. The @randomEffects block declares these latent variables, each linked to a grouping column that defines the hierarchical structure.
Core Syntax
Each random effect is declared as an assignment. The right-hand side must be a RandomEffect(distribution; column=:GROUP_COL) call, where distribution is any univariate or multivariate distribution and column specifies the grouping variable in the data.
name = RandomEffect(Normal(0.0, 1.0); column=:GROUP_COL)Multiple random effects with different distributions and grouping structures can be declared in a single block:
re = @randomEffects begin
η_id = RandomEffect(TDist(6.0); column=:ID)
η_mv = RandomEffect(MvNormal([0.0, 0.0], [0.5 0.0; 0.0 0.8]); column=:ID)
η_site = RandomEffect(SkewNormal(0.0, 1.0, 0.8); column=:SITE)
endParsing Rules
The following constraints are enforced at macro expansion time:
- The block must be
begin ... end. - Only assignments are allowed.
- The left-hand side must be a symbol.
- The right-hand side must be a
RandomEffect(...)call. - Exactly one positional argument is required: the distribution expression.
- The
columnkeyword is required and must be a symbol. - Unsupported keywords are rejected.
- The symbols
tandξ(reserved for time) are forbidden inside distribution expressions.
Available Bindings in Distribution Expressions
Distribution expressions are not limited to literal values. At construction time, NoLimits resolves the following symbols within each distribution expression:
- Fixed effects declared in
@fixedEffects - Constant covariates declared in
@covariates - Model functions generated by parameter blocks (e.g., neural networks, soft trees, splines, normalizing flows)
- Helper functions declared in
@helpers
This enables flexible, data-driven random-effect distributions whose parameters depend on covariates, learned representations, or user-defined transformations.
Example: Multiple Grouping Structures
Models may include random effects at several hierarchical levels simultaneously. In the following example, subject-level effects (η_id, η_mv) are grouped by :ID, while a site-level effect (η_site) is grouped by :SITE.
using NoLimits
using Distributions
model = @Model begin
@fixedEffects begin
s = RealNumber(0.3, scale=:log)
μ_re = RealVector([0.0, 0.0])
Ω_re = RealPSDMatrix([0.4 0.0; 0.0 0.7], scale=:cholesky)
end
@covariates begin
t = Covariate()
end
@randomEffects begin
η_id = RandomEffect(TDist(6.0); column=:ID)
η_mv = RandomEffect(MvNormal(μ_re, Ω_re); column=:ID)
η_site = RandomEffect(SkewNormal(0.0, 1.0, 0.7); column=:SITE)
end
@formulas begin
μ = tanh(η_id) + η_mv[1]^2 + tanh(η_mv[2]) + η_site^2
y ~ Laplace(μ, s)
end
endExample: Covariate- and Function-Parameterized Distributions
Random-effect distributions can be parameterized by neural networks, soft trees, or other learned functions of covariates. This is useful when the distribution of individual-level parameters is expected to vary systematically with observed characteristics.
using NoLimits
using Distributions
using Lux
chain = Chain(Dense(2, 4, tanh), Dense(4, 1))
model = @Model begin
@helpers begin
softplus(u) = log1p(exp(u))
end
@fixedEffects begin
ζη = NNParameters(chain; function_name=:NN1, calculate_se=false)
Γη = SoftTreeParameters(2, 2; function_name=:ST1, calculate_se=false)
sη = RealNumber(0.6, scale=:log)
sy = RealNumber(0.4, scale=:log)
end
@covariates begin
t = Covariate()
x = ConstantCovariateVector([:Age, :BMI]; constant_on=:ID)
end
@randomEffects begin
η = RandomEffect(
LogNormal(
NN1([x.Age, x.BMI], ζη)[1] + ST1([x.Age, x.BMI], Γη)[1],
sη
);
column=:ID
)
end
@formulas begin
y ~ Gamma(softplus(η) + 1e-6, sy)
end
endExample: Normalizing Flow Random Effects
For applications where standard parametric distributions are insufficiently flexible, NoLimits supports normalizing planar flows as random-effect distributions. A NormalizingPlanarFlow(ψ) transforms a base distribution through a sequence of invertible mappings, with parameters ψ registered as fixed effects via NPFParameter.
using NoLimits
using Distributions
model = @Model begin
@fixedEffects begin
ψ = NPFParameter(1, 3, seed=1, calculate_se=false)
sy = RealNumber(0.3, scale=:log)
end
@covariates begin
t = Covariate()
end
@randomEffects begin
η_flow = RandomEffect(NormalizingPlanarFlow(ψ); column=:ID)
end
@formulas begin
y ~ Gamma(log1p(abs(η_flow)^2) + 1e-6, sy)
end
endMetadata and Builder Accessors
The parsed random-effects object provides programmatic access to its components through the following accessor functions:
| Accessor | Returns |
|---|---|
get_re_names | Vector of random effect names |
get_re_groups | NamedTuple mapping each effect to its grouping column |
get_re_types | NamedTuple mapping each effect to its distribution type symbol |
get_re_syms | NamedTuple mapping each effect to the symbols used in its distribution |
get_re_dist_exprs | Distribution expressions (as parsed) |
get_create_random_effect_distribution | Function that constructs distributions at runtime given fixed effects, covariates, model functions, and helpers |
get_re_logpdf | Function computing the joint log-density of all random effects |
Grouping and Data Requirements
- The
columnkeyword defines the hierarchical level at which each random effect varies. This grouping structure is used during data-model construction, batching, and estimation. - When a model includes multiple grouping columns, any constant covariate used within a random-effect distribution must declare
constant_onfor the corresponding grouping column to ensure consistency. - Grouping columns must be present in the data and may not have unique values per observation (which would render the random effects unidentifiable).
Row-Varying Group Membership Within an Individual
NoLimits supports one grouping column per declared random effect. If you need an interaction grouping such as SITE:YEAR, create that composite column in the data and point column at it explicitly.
Whether a non-primary_id grouping column may change within an individual depends on the model class:
| Context | May a non-primary_id grouping column vary within an individual? | Semantics |
|---|---|---|
| Non-ODE formulas | Yes | The random effect used for each observation row is selected from that row's grouping value. |
| Discrete-time HMM outcomes | Yes | Row-wise selection is applied at each observation row. |
| ODE models | No | Random effects must remain uniquely defined at the individual level during ODE evaluation. |
| Continuous-time HMM outcomes | Yes | Row-wise selection is applied at each observation row. |
Two related constraints still apply:
- Random effects referenced in
@preDifferentialEquationmust be grouped byprimary_id. - Constant covariates keep their current validation rules: they must remain constant within
primary_idand within every declaredconstant_ongrouping.