ProCreations's picture
Publish validated GP-FVM reproduction
c711202 verified
Raw
History Blame Contribute Delete
3.84 kB
export LinearSDE, Matern32SDE, IWPSDE,
F, L, qc, Pinf,
discretize_vanloan, sde_dim
using LinearAlgebra
using MatrixEquations
# -- Interface ---------------------------------------------------------------
abstract type LinearSDE end
"""
sde_dim(sde) -> Int
Dimension `n` of the state `u(t) ∈ R^n`.
"""
sde_dim(::LinearSDE) = error("sde_dim not implemented for $(typeof(sde))")
"""
F(sde) -> Matrix{<:Real}
L(sde) -> Matrix{<:Real}
qc(sde) -> Real
H(sde) -> Matrix{<:Real}
Continuous-time linear SDE:
du = F u dt + L dβ, E[dβ dβᵀ] = qc * I * dt
Observation model (for the primary process):
y = H u
`qc` is the scalar diffusion intensity for the driving Wiener process.
"""
F(::LinearSDE) = error("F not implemented")
L(::LinearSDE) = error("L not implemented")
qc(::LinearSDE) = error("qc not implemented")
"""
Pinf(sde) -> Matrix
Stationary state covariance solving the continuous-time Lyapunov equation
F P + P Fᵀ + L (qc) Lᵀ = 0.
Provides a generic implementation; override if you have a closed form.
"""
function Pinf(sde::LinearSDE)
A = F(sde); B = L(sde); q = qc(sde)
Qc = B * (q * I(size(B,2))) * B' # diffusion covariance
return lyapc(A, Qc)
end
# -- Discretization ----------------------------------------------------------
"""
discretize_vanloan(sde, Δt) -> (A, Q)
Exact discretization using the Van Loan method:
u_{k} = A u_{k-1} + ε_k, ε_k ~ N(0, Q)
where
A = exp(F Δt),
Q = ∫₀^{Δt} exp(F τ) L qc Lᵀ exp(Fᵀ τ) dτ.
"""
function discretize_vanloan(sde::LinearSDE, Δt::Real)
A = F(sde); B = L(sde); q = qc(sde)
n = size(A,1)
G = B * sqrt(q) # so that GGᵀ = B q Bᵀ
Z = zeros(eltype(A), n, n)
# Van Loan block matrix
M = [ -A G*G'
Z A' ] * Δt
E = exp(M)
E11 = E[1:n, 1:n]
E12 = E[1:n, n+1:end]
A_d = Matrix(transpose(E[n+1:end, n+1:end])) # equals exp(A Δt)
Q_d = E11 * E12 # equals ∫ exp(A τ) GGᵀ exp(Aᵀ τ) dτ
Q_d = 0.5 * (Q_d + Q_d')
return (A_d, Q_d)
end
# -- Matérn 3/2 in time ------------------------------------------------------
"""
Matern32SDE(σ2, ℓ)
Exact SDE for a Matérn-3/2 temporal GP with variance `σ2` and lengthscale `ℓ`.
State is `[f, ḟ]` so the dimension is 2.
Dynamics:
F = [ 0 1; -λ^2 -2λ ], L = [0; 1], qc = 4 λ^3 σ², H = [1 0]
with λ = √3 / ℓ.
"""
struct Matern32SDE <: LinearSDE
σ2::Float64
ℓ::Float64
end
sde_dim(::Matern32SDE) = 2
function F(sde::Matern32SDE)
λ = sqrt(3.0) / sde.ℓ
return [ 0.0 1.0
-λ^2 -2λ ]
end
L(::Matern32SDE) = [0.0; 1.0]
function qc(sde::Matern32SDE)
λ = sqrt(3.0) / sde.ℓ
return 4.0 * λ^3 * sde.σ2
end
# Optional: closed-form P∞ for Matérn-3/2
function Pinf(sde::Matern32SDE)
λ = sqrt(3.0) / sde.ℓ
σ2 = sde.σ2
return Diagonal([σ2, (λ^2) * σ2])
end
function discretize_vanloan(sde::Matern32SDE, Δ::Real)
σ2 = sde.σ2
λ = sqrt(3.0)/sde.ℓ
r = λ*Δ
e = exp(-r)
A = e .* [1+r Δ;
-λ^21-r]
Q11 = σ2 * (1 - exp(-2r)*(1 + 2r + 2r*r))
Q12 = 2 * σ2 * λ^3 * Δ^2 * exp(-2r) # == 2*σ2*λ*r^2*exp(-2r)
Q22 = λ^2 * σ2 * (1 - exp(-2r)*(1 - 2r + 2r*r))
Q = [Q11 Q12; Q12 Q22]
# (Optional) tiny symmetrization for FP noise:
Q = 0.5*(Q + Q')
return A, Q
end
struct IWPSDE <: LinearSDE
σ2::Float64
end
sde_dim(::IWPSDE) = 2
F(::IWPSDE) = [0.0 1.0; 0.0 0.0]
L(::IWPSDE) = [0.0; 1.0]
qc(sde::IWPSDE) = sde.σ2
Pinf(::IWPSDE) = error("Pinf doesn't exist for IWP SDE")
function discretize_vanloan(sde::IWPSDE, Δ::Real)
A = [1.0 Δ; 0.0 1.0]
Q = sde.σ2 .* [Δ^3/3 Δ^2/2; Δ^2/2 Δ]
Q = 0.5 * (Q + Q')
return A, Q
end