Differential Equations and Simulation
Prerequisites: Memory, allocation, and globals
Simulating how a system changes over time — populations, chemicals,
circuits, orbits — usually means solving ordinary differential
equations (ODEs): du/dt = f(u, p, t).
A solver from scratch
The classic fourth-order Runge–Kutta method takes a few lines of generic Julia:
function rk4(f, u0, p, tspan, dt)
t0, t1 = tspan
ts = collect(t0:dt:t1)
us = [u0]
u = u0
for t in ts[1:end-1]
k1 = f(u, p, t)
k2 = f(u .+ dt / 2 .* k1, p, t + dt / 2)
k3 = f(u .+ dt / 2 .* k2, p, t + dt / 2)
k4 = f(u .+ dt .* k3, p, t + dt)
u = u .+ dt / 6 .* (k1 .+ 2 .* k2 .+ 2 .* k3 .+ k4)
push!(us, u)
end
return ts, us
end
# exponential decay du/dt = -k u, exact solution exp(-k t)
decay(u, k, t) = -k * u
ts, us = rk4(decay, 1.0, 0.5, (0.0, 4.0), 0.1)
println(round(us[end], digits = 6), " vs exact ", round(exp(-0.5 * 4), digits = 6))
# Lotka–Volterra predator–prey model
function lotka(u, p, t)
prey, pred = u
α, β, δ, γ = p
return [α * prey - β * prey * pred, δ * prey * pred - γ * pred]
end
ts, us = rk4(lotka, [10.0, 5.0], (1.1, 0.4, 0.1, 0.4), (0.0, 20.0), 0.01)
prey = first.(us)
println("prey range: ", round.(extrema(prey), digits = 2))
0.135335 vs exact 0.135335
prey range: (0.51, 13.68)
Because rk4 is generic, the same code works for a scalar, a vector,
or any type that supports + and *.
DifferentialEquations.jl
For real work, use DifferentialEquations.jl (part of the SciML ecosystem): hundreds of solvers, automatic step-size control, stiff problems, events, stochastic and delay equations.
# illustrative — needs the DifferentialEquations package
using DifferentialEquations
function lotka!(du, u, p, t)
prey, pred = u
α, β, δ, γ = p
du[1] = α * prey - β * prey * pred
du[2] = δ * prey * pred - γ * pred
end
prob = ODEProblem(lotka!, [10.0, 5.0], (0.0, 20.0), (1.1, 0.4, 0.1, 0.4))
sol = solve(prob, Tsit5(), reltol = 1e-8)
sol(7.5) # interpolated state at any time
sol.t[end], sol.u[end]
using Plots
plot(sol, labels = ["prey" "predator"])
- The in-place form
f!(du, u, p, t)writes intoduand avoids allocations. Tsit5()is a good default for non-stiff problems;Rodas5P()orQNDF()for stiff ones — or callsolve(prob)and let it choose.solis callable:sol(t)interpolates between steps.
The wider SciML ecosystem
- ModelingToolkit.jl — write models symbolically (equations and components), and it generates fast simulation code.
- SciMLSensitivity.jl — gradients of solutions with respect to parameters, for fitting and machine learning.
- Catalyst.jl — chemical reaction networks.
- DiffEqFlux.jl and neural ODEs — combine differential equations with neural networks ("scientific machine learning").