13.3 Solving a System of Linear ODEs using a Matrix Exponential
The solution to \(\frac{d}{dt} y = ay\) is \(y = y_0e^{at}\), where the constant \(y_0\) is determined by boundary conditions. We can extend this solution to the vector case:
\[ \frac{d}{dt}y = A y \] id:ode.linODEs
where \(y\) is now a vector of length \(n\) and \(A\) is an \(n\) by \(n\) matrix. The solution is then given by:
\[ y = e^{tA}y_0 \] id:ode.linOEs.sln
where the matrix exponential is formally defined by the convergent power series:
\[ e^{tA} = \sum_{n=0}^{\infty} \dfrac{tA^n}{n!} = I + tA + \frac{t^2A^2}{2!} + ... \] id:ode.matrix_exp.def
We can apply this technique to the simple harmonic oscillator example, by setting
\[ y = \left[\begin{array}{c} y_1 \\ y_2 \\ \end{array}\right] \ \ \ \ \ A = \left[\begin{array}{cc} 0 & 1 \\ -1 & -\theta \\ \end{array}\right] \] id:ode.sho_matrix
The Stan model to simulate noisy observations using a matrix exponential function
is given below. Because we are performing matrix
operations, we declare y0
and y_hat
as vectors, instead of using arrays,
as in the previous example code.
In general, computing a matrix exponential will be more efficient than using a numerical solver. We can however only apply this technique to systems of ODEs.
data {
int<lower=1> T;
vector[2] y0;
real ts[T];
real theta[1];
}
model {
}
generated quantities {
vector[2] y_hat[T];
matrix[2, 2] A = [[ 0, 1],
[-1, -theta[1]]]
for (t in 1:T)
y_hat[t] = matrix_exp((t - 1) * A) * y0;
// add measurement error
for (t in 1:T) {
y_hat[t, 1] += normal_rng(0, 0.1);
y_hat[t, 2] += normal_rng(0, 0.1);
}
}
id:sho-sim-me.figure
This Stan program simulates noisy measurements from a simple harmonic
oscillator. The system of linear differential equations is coded as a
matrix. The system parameters theta
and initial state y0
are read
in as data along observation times ts
. The generated quantities
block is used to solve the ODE for the specified times and then add
random measurement error, producing observations y_hat
. Because the
ODEs are linear, we can use the matrix_exp
function to solve the
system.