API
Dataloaders
HybridDynamicModels.SegmentedTimeSeries — TypeSegmentedTimeSeries(data; segment_length=2, shift=nothing, batchsize=1, shuffle=false, partial_segment=false, partial_batch=false, rng=GLOBAL_RNG)An object that iterates over mini-batches of segments of data, each segment containing segment_length data points, each mini-batch containing batchsize segments. The last dimension in each tensor is the time dimension.
Arguments
data: Input data (array, tuple, or named tuple).segment_length: Number of time points in each segment.shift: Step size between consecutive segments (default:segment_length).batchsize: Number of segments per batch.shuffle: Whether to shuffle segment order.partial_segment: Allow shorter final segments.partial_batch: Allow smaller final batches.rng: Random number generator for shuffling.
Inputs
data: The time series data to segment.
Outputs
- Iterator yielding batches of data segments.
Behavior
Creates overlapping or non-overlapping segments from time series data for training dynamical models. Segments can be shuffled and batched for efficient training.
Example
julia> Xtrain = rand(10, 100)
julia> sdl = SegmentedTimeSeries(Xtrain; segment_length=2, batchsize=1)
julia> for batch in sdl
println("Batch: ", summary(batch))
end!!!warning Undefined behavior when data dimensions are incompatible
HybridDynamicModels.tokenize — Functiontokenize(sdl::SegmentedTimeSeries)Convert a SegmentedTimeSeries to use token-based indexing.
Arguments
sdl: The SegmentedTimeSeries to tokenize.
Inputs
sdl: SegmentedTimeSeries object.
Outputs
- Tokenized SegmentedTimeSeries with integer-based segment access.
Behavior
Transforms segment indices into a token-based system for easier access to individual segments.
Example
julia> sdl = SegmentedTimeSeries(rand(10, 100); segment_length=2)
julia> tokenized_sdl = tokenize(sdl)
julia> tokens(tokenized_sdl) # Returns available tokensHybridDynamicModels.tokens — Functiontokens(sdl::SegmentedTimeSeries)Get the available tokens for a tokenized SegmentedTimeSeries.
Arguments
sdl: A tokenized SegmentedTimeSeries.
Inputs
sdl: Tokenized SegmentedTimeSeries object.
Outputs
- Range of available tokens (1 to number of segments).
Behavior
Returns the range of tokens that can be used to access individual segments in a tokenized SegmentedTimeSeries.
Example
julia> sdl = SegmentedTimeSeries(rand(10, 100); segment_length=2)
julia> tokenized_sdl = tokenize(sdl)
julia> collect(tokens(tokenized_sdl)) # [1, 2, 3, ...]HybridDynamicModels.create_train_val_loaders — Functioncreate_train_val_loaders(data; segment_length, valid_length, kwargs...)Create separate training and validation SegmentedTimeSeries loaders from a dataset with the same number of batches.
This function splits the data into non-overlapping training and validation segments. The training data uses segments with gaps equal to valid_length to leave space for validation segments. The validation data starts after the first training segment and uses segments of length valid_length. Both loaders are guaranteed to have the same number of batches, with tokens referring to the same ordering.
Arguments
data: Input data (can be an array, tuple, or named tuple)segment_length: Size of training segmentsvalid_length: Size of validation segmentskwargs...: Additional arguments passed to SegmentedTimeSeries constructors
Returns
dataloader_train: SegmentedTimeSeries for training datadataloader_valid: SegmentedTimeSeries for validation data
Examples
With array data
data = rand(10, 100) # 10 features, 100 time steps
train_loader, val_loader = create_train_val_loaders(data;
segment_length=20, valid_length=5, batchsize=4)
# Both loaders will have the same number of batches
@assert length(train_loader) == length(val_loader)With tuple data (data, time steps)
data = rand(10, 100)
tsteps = 1:100
train_loader, val_loader = create_train_val_loaders((data, tsteps);
segment_length=20, valid_length=5, batchsize=4)With named tuple data
dataset = (observations = rand(10, 100), times = 1:100, metadata = rand(5, 100))
train_loader, val_loader = create_train_val_loaders(dataset;
segment_length=20, valid_length=5)Notes
- Training segments are spaced
segment_length + valid_lengthapart to avoid overlap with validation - Validation segments start at position
segment_length + 1to avoid overlap with first training segment - Both loaders have
partial_segment = falseandpartial_batch = falseto ensure consistent sizes - Both loaders are guaranteed to have the same number of batches for synchronized training/validation
Parameter Layers
HybridDynamicModels.ParameterLayer — TypeParameterLayer(;constraint::AbstractConstraint = NoConstraint(),
init_value = (;),
init_state_value = (;))A layer representing parameters, optionally with constraints.
Arguments
constraint: Constraint to transform parameters.init_value: Initial parameter values (NamedTuple or AbstractArray).init_state_value: Internal state (NamedTuple).
Inputs
ps: Parameters of the layer.st: States of the layer.
Outputs
- Constrained parameter values merged with states.
Behavior
Applies constraints to parameters during forward pass. Parameters are transformed from unconstrained to constrained space.
Example
julia> param = ParameterLayer(; constraint = NoConstraint(),
init_value = (;a = ones(2)),
init_state_value = (;b = (0.0, 1.0)))
julia> ps, st = Lux.setup(Random.default_rng(), param)
julia> x, _ = param(ps, st)
julia> x == (a = [1.0, 1.0], b = (0.0, 1.0))
trueHybridDynamicModels.NoConstraint — TypeNoConstraint()Applies no transformation to parameters.
Arguments
- None.
Inputs
x: Parameter values.st: States.
Outputs
- Unmodified parameter values and states.
Behavior
Identity transformation - parameters remain unconstrained.
Example
julia> constraint = NoConstraint()
julia> x, st = constraint([1.0, 2.0], (;))
([1.0, 2.0], (;))HybridDynamicModels.BoxConstraint — TypeBoxConstraint(lb::AbstractArray, ub::AbstractArray)Constrains parameters to lie within specified bounds using sigmoid transformation.
Arguments
lb: Lower bounds array.ub: Upper bounds array.
Inputs
y: Unconstrained parameter values.st: States containing bounds.
Outputs
- Constrained parameter values within bounds.
Behavior
Transforms unconstrained parameters to constrained space using sigmoid function.
Example
julia> constraint = BoxConstraint([0.0], [1.0])
julia> x, st = constraint([0.0], (;lb=[0.0], ub=[1.0]))
([0.5], (;lb=[0.0], ub=[1.0]))HybridDynamicModels.NamedTupleConstraint — TypeNamedTupleConstraint(constraints::NamedTuple)Applies different constraints to different fields of a NamedTuple.
Arguments
constraints: NamedTuple of constraint objects.
Inputs
x: NamedTuple of parameter values.st: NamedTuple of states.
Outputs
- Constrained parameter values.
Behavior
Applies field-specific constraints to NamedTuple parameters.
Example
julia> constraints = (a = BoxConstraint([0.0], [1.0]), b = NoConstraint())
julia> constraint = NamedTupleConstraint(constraints)Models
HybridDynamicModels.ODEModel — TypeODEModel(layers::NamedTuple, dudt::Function; kwargs...)Wraps an ODE model for simulation using Lux layers.
Arguments
layers: NamedTuple of Lux layers representing the layers of the model.dudt: Function that computes the derivative of the state, with signaturedudt(layers, u, ps, t).kwargs: Additional keyword arguments passed to the solver (e.g.,tspan,saveat,alg).
Inputs
- (
x,ps,st)x: aNamedTupleorAbstractVector{NamedTuple}(batch mode).ps: Parameters of the model.st: States of the model.
- A tuple of (
x,ps,st): batch mode. - (
ps,st): Ifxnot provided, defaults tokwargs.
Outputs
- (
sol,st)sol: Solution of the ODE problem, with second dimension corresponding to time and batches stacked along the third dimension, if applicable.st: Updated states of the model.
Behavior
layers are wrapped in StatefulLuxLayers to maintain their states. The derivative function dudt should be defined as dudt(layers, u, ps, t) where u is the current state and t is the current time. The function returns the derivative of the state.
Example
julia> layers = (; layer1 = Lux.Dense(10, 10, relu))
julia> dudt(layers, u, ps, t) = layers.layer1(u, ps.layer1)[1]
julia> ode_model = ODEModel(layers, dudt, tspan = (0f0, 1f0), saveat = range(0f0, stop=1f0, length=100), alg = Tsit5())
julia> ps, st = Lux.setup(Random.default_rng(), ode_model)
julia> ode_model((; u0 = ones(Float32, 10)), ps, st)!!!warning Undefined behavior when ps is not a NamedTuple
HybridDynamicModels.AnalyticModel — TypeAnalyticModel(layers::NamedTuple, fun::Function; kwargs...)Wraps an analytic model for direct evaluation using Lux layers.
Arguments
layers: NamedTuple of Lux layers representing the layers of the model.fun: Function that computes the analytic solution, with signaturefun(layers, u0, t0, ps, t).kwargs: Additional keyword arguments (e.g., default values foru0,tspan,saveat).
Inputs
- (
x,ps,st)x: aNamedTupleorAbstractVector{NamedTuple}(batch mode).ps: Parameters of the model.st: States of the model.
- A tuple of (
x,ps,st): batch mode. - (
ps,st): Ifxnot provided, defaults tokwargs.
Outputs
- (
sol,st)sol: Solution array evaluated at specified time points, with second dimension corresponding to time and batches stacked along the third dimension, if applicable.st: Updated states of the model.
Behavior
layers are wrapped in StatefulLuxLayer to maintain their states. The analytic function fun should be defined as fun(layers, u0, t0, ps, t) where t can be a vector of time points and t0 is extracted from tspan.
Example
julia> layers = (; params = ParameterLayer(init_value = (a = 1.0, b = 0.5)))
julia> analytic_solution(layers, u0, t0, ps, t) = u0 .* exp.(layers.params(ps.params)[1].a .* (t .- t0))
julia> model = AnalyticModel(layers, analytic_solution; u0 = [1.0], tspan = (0.0, 1.0), saveat = 0:0.1:1.0)
julia> ps, st = Lux.setup(Random.default_rng(), model)
julia> model((; u0 = [1.0]), ps, st)!!!warning Undefined behavior when ps is not a NamedTuple
HybridDynamicModels.ARModel — TypeARModel(layers::NamedTuple, fun::Function; kwargs...)Wraps an autoregressive (AR) model.
Arguments
layers: NamedTuple of Lux layers representing the layers associated with the model.fun: Function that computes the next time step, with signaturefun(layers, u, ps, t).kwargs: Additional keyword arguments (e.g., default values foru0,tspan,saveat,dt).
Inputs
- (
x,ps,st)x: aNamedTupleorAbstractVector{NamedTuple}(batch mode).ps: Parameters of the model.st: States of the model.
- A tuple of (
x,ps,st): batch mode. - (
ps,st): Ifxnot provided, defaults tokwargs.
Outputs
- (
sol,st)sol: Solution array with iterative predictions, with second dimension corresponding to time and batches stacked along the third dimension, if applicable.st: Updated states of the model.
Behavior
layers are wrapped in StatefulLuxLayer to maintain their states. The AR function fun should be defined as fun(layers, u, ps, t) where:
uis the current statetis the current time- The function returns the next state
The model iteratively applies the function to generate a time series from initial conditions.
Example
julia> using HybridDynamicModels, Lux, Random
julia> layers = (;
predictor = Dense(2, 2),
params = ParameterLayer(init_value = (; decay = [95f-2],))
);
julia> ar_step(layers, u, ps, t) = layers.predictor(u, ps.predictor) .* layers.params(ps.params).decay;
julia> model = ARModel(layers, ar_step; dt = 1f-1, u0 = [1f0, 5f-1], tspan = (0f0, 1f0), saveat = 0:1f-1:1f0);
julia> ps, st = Lux.setup(Random.default_rng(), model);
julia> x = (; u0 = [1f0, 5f-1]);
julia> y, st = model(x, ps, st);
julia> size(y) # 2 state variables, 11 time points
(2, 11) !!!warning Undefined behavior when x is not a NamedTuple
Initial Conditions
HybridDynamicModels.ICLayer — TypeICLayer(ics::AbstractLuxLayer)
ICLayer(ics::<:ParameterLayer)
ICLayer(ics::Vector{<:ParameterLayer})Initial condition layer.
Arguments
ics: Lux layer, ParameterLayer, or vector of ParameterLayer.
Inputs
- (
x,ps,st) withxa NamedTuple or AbstractVector{NamedTuple} (batch mode). - (
ps,st) whenicsis a ParameterLayer.
Outputs
- Initial conditions merged with other fields.
- Updated states.
Behavior
Processes initial conditions through wrapped layers and merges with input data.
Example
julia> ic_layer = ICLayer(ParameterLayer(init_value = (;u0 = [1.0])))Training API
HybridDynamicModels.train — Functiontrain(backend::AbstractOptimBackend, model, dataloader::SegmentedTimeSeries, infer_ics::InferICs, rng=Random.default_rng(); pstype=Lux.f64)Train a dynamical model using segmented time series data.
Arguments
backend: Training configuration and optimization settings.model: Lux model to train.dataloader: Time series data split into segments.infer_ics: Initial condition inference configuration.rng: Random number generator.pstype: Precision type for parameters.
Inputs
backend: Training backend (SGDBackend, MCSamplingBackend, etc.).model: Model to train.dataloader: SegmentedTimeSeries data.infer_ics: InferICs configuration.
Outputs
- NamedTuple with training results (varies by backend).
Behavior
Trains model using specified backend on segmented time series data.
Example
julia> backend = SGDBackend(Adam(1e-3), 1000, AutoZygote(), MSELoss())
julia> dataloader = SegmentedTimeSeries(data, segment_length=20)
julia> infer_ics = InferICs(true)
julia> result = train(backend, model, dataloader, infer_ics)HybridDynamicModels.InferICs — TypeInferICs(infer::Bool, u0_constraint=NoConstraint())Configuration for initial condition inference in training.
Arguments
infer: Whether to treat initial conditions as learnable parameters.u0_constraint: Constraint for initial condition optimization.
Inputs
infer: Boolean flag for inference.u0_constraint: Constraint object.
Outputs
- InferICs configuration object.
Behavior
Controls whether initial conditions are learned or fixed during training.
Example
julia> infer_ics = InferICs(true, NoConstraint())Lux.jl training backend
HybridDynamicModels.SGDBackend — TypeSGDBackend(opt, n_epochs, adtype, loss_fn, callback)Training backend using Lux.jl for mode estimation.
!!! warning Conditional loading You need to load Optimisers, ComponentArrays and Lux before loading HybridDynamicModels to use SGDBackend.
Arguments
opt: Optimizers.jl rule for parameter updates.n_epochs: Number of training epochs.adtype: Automatic differentiation backend.loss_fn: Loss function for training.callback: User-defined callback function.
Inputs
opt: Optimization rule (e.g., Adam(1e-3)).n_epochs: Total training epochs.adtype: AD backend (e.g., AutoZygote()).loss_fn: Loss function.callback: Optional callback.
Outputs
- NamedTuple with trained parameters and states.
Behavior
Uses stochastic gradient descent for maximum likelihood estimation.
Example
julia> backend = SGDBackend(Adam(1e-3), 1000, AutoZygote(), MSELoss())Turing.jl backend
HybridDynamicModels.BayesianLayer — TypeBayesianLayer(layer, priors)Wrapper layer that adds Bayesian priors to Lux layers for probabilistic modeling.
Arguments
layer: Lux layer to make Bayesian.priors: Prior distributions for parameters.
Inputs
layer: Any Lux layer.priors: Distribution or NamedTuple of distributions.
Outputs
- Layer with Bayesian priors for MCMC inference.
Behavior
Enables probabilistic modeling by attaching priors to layer parameters.
Example
julia> dense_layer = Dense(10, 5)
julia> bayesian_dense = BayesianLayer(dense_layer, Normal(0, 1))HybridDynamicModels.getpriors — Functiongetpriors(layer)Extract prior distributions from Bayesian layers.
Arguments
layer: Layer or model containing BayesianLayer components.
Inputs
layer: Bayesian or composite layer.
Outputs
- NamedTuple of prior distributions.
Behavior
Recursively extracts priors from Bayesian layers in model hierarchy.
Example
julia> priors = getpriors(bayesian_model)HybridDynamicModels.create_turing_model — Functioncreate_turing_model(ps_priors, data_distrib, st_model)Create a Turing model for Bayesian inference from a BayesianLayer model.
Arguments
ps_priors: A nested structure (typically a NamedTuple) containing prior distributions for model parameters. Each leaf should be aDistributions.Distribution.data_distrib: A function or distribution constructor that creates the likelihood distribution for observed data points.st_model: A stateful Lux model that can be called with parameters to generate predictions.
Returns
A function (xs, ys) -> Turing.Model that creates a Turing model when given input data xs and observed data ys.
HybridDynamicModels.MCSamplingBackend — TypeMCSamplingBackend(sampler, n_iterations, datadistrib; kwargs...)Training backend for Bayesian inference using Monte Carlo sampling. !!! warning Conditional loading You need to load Turing, ComponentArrays and Lux before loading HybridDynamicModels to use MCSamplingBackend.
Arguments
sampler: Turing.jl MCMC sampler.n_iterations: Number of MCMC samples.datadistrib: Data distribution for likelihood.kwargs: Additional sampler options.
Inputs
sampler: MCMC sampling algorithm.n_iterations: Total posterior samples.datadistrib: Distribution for data likelihood.
Outputs
- NamedTuple with MCMC chains and model state.
Behavior
Performs Bayesian inference using MCMC sampling on models with priors.
Example
julia> backend = MCSamplingBackend(NUTS(0.65), 1000, LogNormal)