Quadratic State Space Examples
Second-order state-space models here have pruning as in Andreasen, Fernandez-Villaverde, and Rubio-Ramirez (2017).
At this point, the package only supports linear time-invariant models without a separate p
vector. The canonical form is
\[u_{n+1} = A_0 + A_1 u_n + u_n^{\top} A_2 u_n + B w_{n+1}\]
with
\[z_n = C_0 + C_1 u_n + u_n^{\top} C_2 u_n + v_n\]
and optionally $v_n \sim N(0, D)$ and $w_{n+1} \sim N(0,I)$. If you pass noise into the solver, it no longer needs to be Gaussian.
Quadratic state-space models do not have the full feature coverage relative to the linear models. In particular the auto-differentiation rules are only currently implemented for the logpdf
required for estimation, and the simulation doesn't have much flexibility on which model elements can be missing.
Simulating a Quadratic (and Time-Invariant) State Space Model
Creating a QuadraticStateSpaceModel
is similar to the Linear version described previously.
using DifferenceEquations, LinearAlgebra, Distributions, Random, Plots, DataFrames, Zygote, DiffEqBase
A_0 = [-7.824904812740593e-5, 0.0]
A_1 = [0.95 6.2;
0.0 0.2]
A_2 = cat([-0.0002 0.0334; 0.0 0.0],
[0.034 3.129; 0.0 0.0]; dims = 3)
B = [0.0; 0.01;;] # matrix
C_0 = [7.8e-5, 0.0]
C_1 = [0.09 0.67;
1.00 0.00]
C_2 = cat([-0.00019 0.0026; 0.0 0.0],
[0.0026 0.313; 0.0 0.0]; dims = 3)
D = [0.01, 0.01] # diagonal observation noise
u0 = zeros(2)
T = 30
prob = QuadraticStateSpaceProblem(A_0, A_1, A_2, B, u0, (0, T); C_0, C_1, C_2, observables_noise = D, syms = [:a, :b])
sol = solve(prob)
retcode: Success
Interpolation: Piecewise constant interpolation
t: 0:30
u: 31-element Vector{Vector{Float64}}:
[0.0, 0.0]
[-7.824904812740593e-5, -0.005093974919701217]
[-0.03165403703766611, -0.005076446793181592]
[-0.06153231245672637, 0.00025874381201716865]
[-0.05693135278854326, -0.0070253558970650295]
[-0.09753955721641847, 0.012469203482578893]
[-0.015029123900219096, 0.007166790975212762]
[0.030231448902375196, 0.010163790651256829]
[0.09200064638881196, 0.007974711682334176]
[0.13701202121551717, 0.010451667198664217]
⋮
[0.330361362184917, -0.008816144457571671]
[0.25913256309658145, 0.008850867626707982]
[0.3013577199745115, -0.002350686395847892]
[0.27158966280349855, -0.008246734803276578]
[0.20685133961246244, -0.01454765443585856]
[0.10668901751453155, -0.011715978965180058]
[0.028982859730797467, -0.013091026953404753]
[-0.05319547885411144, -0.004459828298316323]
[-0.07818624013534657, -0.0002607123728279444]
As in the linear case, this model can be simulated and plotted
plot(sol)
And the observables and noise can be stored
observables = hcat(sol.z...) # Observables required to be matrix. Issue #55
observables = observables[:, 2:end] # see note above on likelihood and timing
noise = sol.W
1×30 Matrix{Float64}:
-0.509397 -0.405765 0.127403 -0.70771 … -1.07478 -0.184162 0.0631253
Ensembles work as well,
trajectories = 50
u0_dist = MvNormal([1.0 0.1; 0.1 1.0]) # mean zero initial conditions
prob = QuadraticStateSpaceProblem(A_0, A_1, A_2, B, u0_dist, (0, T); C_0, C_1, C_2, observables_noise = D, syms = [:a, :b])
ens_sol = solve(EnsembleProblem(prob), DirectIteration(), EnsembleThreads(); trajectories)
summ = EnsembleSummary(ens_sol) # calculate summarize statistics such as quantiles
plot(summ)
Joint Likelihood with Noise
To calculate the likelihood, the Kalman Filter is no longer applicable. However, we can still calculate the joint likelihood we did in the linear examples. Using the simulated observables and noise,
function joint_likelihood_quad(A_0, A_1, A_2, B, C_0, C_1, C_2, D, u0, noise, observables)
prob = QuadraticStateSpaceProblem(A_0, A_1, A_2, B, u0, (0, size(observables,2)); C_0, C_1, C_2, observables, observables_noise = D, noise)
return solve(prob).logpdf
end
u0 = [0.0, 0.0]
joint_likelihood_quad(A_0, A_1, A_2, B, C_0, C_1, C_2, D, u0, noise, observables)
40.4693425146623
Which, in turn can itself be differentiated.
gradient((A_0, A_1, A_2, B, C_0, C_1, C_2, noise) -> joint_likelihood_quad(A_0, A_1, A_2, B, C_0, C_1, C_2, D, u0, noise, observables), A_0, A_1, A_2, B, C_0, C_1, C_2, noise)
([-12.642564619638382, 102.45454534108529], [16.165114859513633 2.174033415771264; 26.801516061574326 11.89003441550209], [3.791907144586649 0.15123188465917023; 20.29334251424644 0.5593397770773808;;; 0.15123188465917023 0.0014810868659952893; 0.5593397770773808 -0.0007120666663152798], [148.94923553531916; 1369.5427833831714;;], [-112.43994647644277, -20.895978286275735], [-11.268836701776792 0.30505649448920713; 14.498156730062792 -0.1684734825205818], [-4.018060371433903 -0.019065611960310806; 2.01995442447507 0.011661985410705095;;; -0.019065611960310806 -0.011708825413934951; 0.011661985410705095 -0.0015092531871091599], [-1.8539715513918331 -2.163716534206258 … -1.045782502443911 0.06217898633879742])
Note that this is calculating the gradient of the likelihood with respect to the underlying canonical representations for the quadratic state space form, but also the entire noise vector.
As in the linear case, this likelihood calculation can nested such that a separate differentiable function could generate the quadratic state space model and the gradients could be over a smaller set of structural parameters.