@preDifferentialEquation
In many longitudinal models, certain quantities depend on fixed effects, random effects, and covariates but do not vary with time. Computing these derived quantities once before ODE integration – rather than redundantly at each solver step – improves both clarity and performance.
The @preDifferentialEquation block defines such time-constant derived quantities. Values declared here are available in @DifferentialEquation, @initialDE, and @formulas.
Core Syntax
The block accepts only assignment statements. Each left-hand side must be a plain symbol, and expressions may reference any in-scope model quantities except time (t and \xi are forbidden).
prede = @preDifferentialEquation begin
ka = exp(tka + eta[1])
cl = exp(tcl + eta[2])
v = exp(tv + eta[3])
endParsing and Validation Rules
The following constraints are enforced at parse time:
- The block must be wrapped in
begin ... end. - Only assignments are allowed; other statement forms are rejected.
- Left-hand side must be a symbol.
- The time symbols
tand\xiare forbidden in expressions, since preDE values must be time-invariant. - Mutating patterns trigger a warning, as they may break automatic differentiation.
Symbol Resolution
@preDifferentialEquation expressions can reference the following model components:
- Fixed effects
- Random effects
- Constant covariates (
constant_features_i) - Helper functions declared in
@helpers - Model functions produced by learned parameter blocks (
NNParameters,SoftTreeParameters,SplineParameters,NPFParameter)
Example: Nonlinear Transform of Multivariate Random Effects
A common pattern is to define individual-level parameters as log-linear functions of population means and random effects. Here, three rate and volume parameters are derived from fixed effects and a multivariate random effect vector before being passed into the ODE system.
using NoLimits
using Distributions
using LinearAlgebra
model = @Model begin
@fixedEffects begin
tka = RealNumber(0.45)
tcl = RealNumber(1.0)
tv = RealNumber(3.45)
omega1 = RealNumber(1.0, scale=:log)
omega2 = RealNumber(1.0, scale=:log)
omega3 = RealNumber(1.0, scale=:log)
sigma = RealNumber(0.3, scale=:log)
end
@covariates begin
t = Covariate()
end
@randomEffects begin
eta = RandomEffect(MvNormal([0.0, 0.0, 0.0], Diagonal([omega1, omega2, omega3])); column=:ID)
end
@preDifferentialEquation begin
ka = exp(tka + eta[1])
cl = exp(tcl + eta[2])
v = exp(tv + eta[3])
end
@DifferentialEquation begin
D(depot) ~ -ka * depot
D(center) ~ ka * depot - cl / v * center
end
@initialDE begin
depot = 0.0
center = 0.0
end
@formulas begin
y ~ Erlang(3, log1p((center(t) / v)^2) + sigma)
end
endExample: Using Helpers and Learned Model Functions
PreDE expressions can also incorporate neural networks, soft decision trees, and spline functions. This is useful when individual-level baseline quantities are modeled as flexible, learned functions of covariates.
using NoLimits
using Lux
using Distributions
chain = Chain(Dense(2, 3, tanh), Dense(3, 1))
knots = collect(range(0.0, 1.0; length=6))
model = @Model begin
@helpers begin
sat(u) = u / (1 + abs(u))
end
@fixedEffects begin
ζ = NNParameters(chain; function_name=:NNB, calculate_se=false)
Γ = SoftTreeParameters(2, 2; function_name=:STB, calculate_se=false)
sp = SplineParameters(knots; function_name=:SPB, calculate_se=false)
sy = RealNumber(0.4, scale=:log)
end
@covariates begin
t = Covariate()
x = ConstantCovariateVector([:Age, :BMI]; constant_on=:ID)
end
@preDifferentialEquation begin
nn = NNB([x.Age, x.BMI], ζ)[1]
st = STB([x.Age, x.BMI], Γ)[1]
spv = SPB(0.4, sp)
drive = sat(nn + st + spv)
end
@formulas begin
y ~ Gamma(log1p(drive^2) + 1e-6, sy)
end
endAccessors
The following functions provide programmatic access to preDE internals:
get_prede_names(prede)– returns the declared preDE variable names.get_prede_syms(prede)– returns the parsed symbol dependencies for each variable.get_prede_builder(prede)– returns the evaluation function that computes preDE values from model inputs.
Data-Model Constraint for Random Effects in preDE
Because preDE values are computed once per individual per model evaluation, any random effects referenced in this block must be grouped by primary_id in the DataModel. Random effects grouped by a different column (e.g., a site-level grouping) are not permitted in preDE expressions, since their values would not be uniquely determined at the individual level.