Getting Started
This tutorial walks through the core workflow of DifferenceEquations.jl: defining a linear state-space model, simulating it, and computing likelihoods.
Creating a Linear State Space Model
A LinearStateSpaceProblem represents a linear time-invariant state-space model:
\[u_{n+1} = A\, u_n + B\, w_{n+1}, \qquad z_n = C\, u_n + v_n\]
Define the model primitives, create a problem, and solve:
using DifferenceEquations, LinearAlgebra, Random
A = [0.95 6.2; 0.0 0.2]
B = [0.0; 0.01;;] # 2×1 Matrix (Julia's ;; creates a column matrix)
C = [0.09 0.67; 1.00 0.00]
u0 = zeros(2)
T = 10
prob = LinearStateSpaceProblem(A, B, u0, (0, T); C)
sol = solve(prob)
sol.u[end]2-element Vector{Float64}:
0.044879388496239715
-0.01200993769889138Computing Likelihood
To compute log-likelihoods, provide observables (a Vector{Vector} of length T) and observables_noise (the observation noise covariance matrix — e.g., Diagonal(d) for diagonal noise or Symmetric(H * H') for a general covariance).
Observations correspond to $z_1, z_2, \ldots, z_T$ – that is, the states after the initial condition. Pass T observation vectors for a tspan of (0, T).
First, simulate some data to use as observables:
Random.seed!(123)
D = Diagonal([0.1, 0.1]) # diagonal observation noise covariance matrix
prob_sim = LinearStateSpaceProblem(A, B, u0, (0, T); C, observables_noise = D)
sol_sim = solve(prob_sim)
# Extract observations at times 1..T (skip the initial condition at t=0)
observables = sol_sim.z[2:end]
length(observables) # should be T10Compute the joint log-likelihood given fixed noise using DirectIteration:
prob_lik = LinearStateSpaceProblem(A, B, u0, (0, length(observables)); C,
observables = observables,
observables_noise = D,
noise = sol_sim.W)
sol_lik = solve(prob_lik)
sol_lik.logpdf # joint log-likelihood-0.2908180730779734For the marginal log-likelihood (integrating out the latent noise), use a KalmanFilter by additionally providing a Gaussian prior on u0:
prob_kf = LinearStateSpaceProblem(A, B, u0, (0, length(observables)); C,
observables = observables,
observables_noise = D,
u0_prior_mean = zeros(2),
u0_prior_var = Matrix(1.0I, 2, 2))
sol_kf = solve(prob_kf) # KalmanFilter is auto-selected
sol_kf.logpdf # marginal log-likelihood-5.285155583498729DataFrame Conversion
Convert the state trajectory to a DataFrame for analysis. Column names come from syms if provided:
using DataFrames
prob_df = LinearStateSpaceProblem(A, B, u0, (0, T); C,
syms = (:capital, :productivity))
sol_df = solve(prob_df)
DataFrame(sol_df)| Row | timestamp | capital | productivity |
|---|---|---|---|
| Int64 | Float64 | Float64 | |
| 1 | 0 | 0.0 | 0.0 |
| 2 | 1 | 0.0 | -0.0108822 |
| 3 | 2 | -0.0674695 | 0.00486115 |
| 4 | 3 | -0.033957 | 0.00240549 |
| 5 | 4 | -0.0173451 | 0.00196485 |
| 6 | 5 | -0.00429576 | 0.0104891 |
| 7 | 6 | 0.0609515 | 0.00238549 |
| 8 | 7 | 0.0726939 | 0.00218506 |
| 9 | 8 | 0.0826066 | -0.0167342 |
| 10 | 9 | -0.0252757 | 0.00772098 |
| 11 | 10 | 0.0238582 | 0.0130312 |
Next Steps
- Linear Simulation – detailed simulation examples, symbolic indexing, fixed noise, and ensemble runs.
- Likelihood & Kalman Filter – marginal and joint likelihood, gradient-based estimation.
- Quadratic Models – second-order perturbation models.
- Generic Callbacks – user-defined nonlinear transition and observation functions.
- Workspace API – allocation-free repeated solves for performance-critical loops.
- Enzyme AD – differentiating through solvers and filters with Enzyme.jl.