State space / kalman filtering advice for inverse model

Here a Stan function for doing the Kalman filtering, using notation mostly based on https://users.aalto.fi/~ssarkka/pub/cup_book_online_20131111.pdf:

dX/dt = A_c X_t + B_c U_t + N(0, Q)
y_t = HX_t + N(0, R),

real kf(
  matrix Ac,  // q*q, continuous time state transition matrix
  matrix Bc,  // q*m, continuous time input matrix
  matrix H,   // p*q, measurement matrix
  matrix U,   // N*m, input vector
  matrix y,   // N*p, measurement vector
  vector w,   // q, process noise
  vector v,   // q, measurement noise
  real dt,    // time interval
  vector X1,  // q,  initital X  at t=1
  matrix P1   // q*q, covariance at t=1
) {
  int N = dims(y)[1];  // number of time steps
  int p = dims(y)[2];  // number of observation variables
  int m = dims(Bc)[2]; // number of input variables
  int q = dims(A)[1];  // number of states
  
  vector[q] X[N];
  vector[p] v;
  matrix[q, q] Q = diag_matrix(w);   // state covariance
  matrix[p, p] R = diag_matrix(v);   // observation covariance
  matrix[q, q] P;     // covariance
  matrix[q, p] K;     // Kalman gain
  matrix[p, p] S;     // aka F 
  matrix[p, q] H;     // aka C
  matrix[p, p] S_inv; 
  matrix[q, q] A;
  matrix[q, m] B;
  matrix[q, q] I = diag_matrix(rep_vector(1., q));
  real ll = 0;        // logliklihood
  
  // continuous time to discrete time
  A = matrix_exp(A_c*dt);
  B = (A - diag_matrix(rep_vector(1., q))) / A_c * B_c;
  
  // Initialization
  X[1] = rep_vector(0, q);  
  P = rep_matrix(0, q, q);
  
  for (k in 1:N) {
    k__1 = max(k-1, 1);        // k-1
  
    /* Time update */
    X[k] = A*X[k__1] + B*U[k]; // predict state mean values
    P = A*P*A' + Q;            // predict state covariance matrix
  
    /* measurement update */  
    v = y[k]' - H*X[k];        // output mean residual
    S = H*P*H' + R;            // output covariance
    S_inv = inverse(S); 
    K = P*H' * S_inv;          // Kalman gain
  
    // update state covariance
    P = (I - K*H)*P*(I - K*H)' + K*R*K';  
    //P = (I-K*H)*P;      // less stable
  
    X[k] += (K*v);            // update states
  
    // log-likelihood
    ll -= (log_determinant(S) + v'*S_inv*v); 
  }
  return 0.5*(ll - p*N*log(2*pi()));
}

I just simplified the code above from a more specific code of mine. I haven’t tested it and probably missed a parenthesis or something. But, it should give you the general idea of how to implement a Kalman filter in Stan. @Charles_Driver post above shows how to get your model into general state-space representation.

A great resource on Bayesian filters is found here https://github.com/rlabbe/Kalman-and-Bayesian-Filters-in-Python.

2 Likes