In case this is useful to someone else in the future I have now figured out how to implement Viterbi for the constrained HMM that I mentioned before (see below).
Here because the middle state has a lower step length than the outer two, I reorder the alpha parameter. In my model the state sequence must be 1->2->3. It cannot go backwards, for example 2->1. It also cannot jump from 1->3. So from each state the following transitions are permitted:
- State 1: 1->1 or 1->2.
- State 2: 2->2 or 2->3.
- Sate 3: 3->3 only.
Unlike most HMMs I do not have a transition probability matrix, because really I am just trying to implement a two change point model in an efficient manner.
data{
int T;
int K;
real Speed[T];
}
transformed data {
real log_unif;
log_unif = -log(T);
}
parameters{
positive_ordered[K] alpha;
}
transformed parameters{
matrix[T,K] lp;
vector[K] alphaReordered;
alphaReordered[1] = alpha[1];
alphaReordered[2] = alpha[3];
alphaReordered[3] = alpha[2];
lp[1] = rep_row_vector(-log(K),K);
for(t in 2:T){
for(k in 1:K){
if(k==1) # early (long)
lp[t,k] = lp[t-1,k] + exponential_lpdf(Speed[t]|alphaReordered[k]);
else if(k==2) # mid (short)
lp[t,k] = log_sum_exp(lp[t-1,1:2]) + exponential_lpdf(Speed[t]|alphaReordered[k]);
else # late (medium)
lp[t,k] = log_sum_exp(lp[t-1,2:3]) + exponential_lpdf(Speed[t]|alphaReordered[k]);
}
}
}
model{
target += log_sum_exp(lp[T]);
alpha ~ exponential(1);
}
generated quantities{
int path[T];
vector[K] stepLength;
for(k in 1:K)
stepLength[k] = 1/alphaReordered[k];
{
matrix[T,K] mLogProb;
int mBestIndex[T-1,K];
real aLogProbTemp;
int aBestFinal;
for(t in 1:T){
for(k in 1:K){
if(t==1){
if(k==1)
mLogProb[t,k] = exponential_lpdf(Speed[t]|alphaReordered[k]);
else
mLogProb[t,k] = negative_infinity();
}else if(t==2){
if(k==1){
mLogProb[t,k] = mLogProb[t-1,1] + exponential_lpdf(Speed[t]|alphaReordered[k]);
mBestIndex[t-1,k] = 1;
}
else if(k==2){
mLogProb[t,k] = mLogProb[t-1,1] + exponential_lpdf(Speed[t]|alphaReordered[k]);
mBestIndex[t-1,k] = 1;
}
else{
mLogProb[t,k] = negative_infinity();
mBestIndex[t-1,k] = 0;
}
}else if(t==3){
if(k==1){
mLogProb[t,k] = mLogProb[t-1,1] + exponential_lpdf(Speed[t]|alphaReordered[k]);
mBestIndex[t-1,k] = 1;
}
else if(k==2){
mLogProb[t,k] = max(mLogProb[t-1,1:2]) + exponential_lpdf(Speed[t]|alphaReordered[k]);
aLogProbTemp = negative_infinity();
for(i in 1:2){
if(mLogProb[t-1,i]>aLogProbTemp){
aLogProbTemp = mLogProb[t-1,i];
mBestIndex[t-1,k] = i;
}
}
}
else{
mLogProb[t,k] = mLogProb[t-1,2] + exponential_lpdf(Speed[t]|alphaReordered[k]);
mBestIndex[t-1,k] = 2;
}
}else{
if(k==1){
mLogProb[t,k] = mLogProb[t-1,1] + exponential_lpdf(Speed[t]|alphaReordered[k]);
mBestIndex[t-1,k] = 1;
}
else if(k==2){
mLogProb[t,k] = max(mLogProb[t-1,1:2]) + exponential_lpdf(Speed[t]|alphaReordered[k]);
aLogProbTemp = negative_infinity();
for(i in 1:2){
if(mLogProb[t-1,i]>aLogProbTemp){
aLogProbTemp = mLogProb[t-1,i];
mBestIndex[t-1,k] = i;
}
}
}
else{
mLogProb[t,k] = max(mLogProb[t-1,2:3]) + exponential_lpdf(Speed[t]|alphaReordered[k]);
aLogProbTemp = negative_infinity();
for(i in 2:3){
if(mLogProb[t-1,i]>aLogProbTemp){
aLogProbTemp = mLogProb[t-1,i];
mBestIndex[t-1,k] = i;
}
}
}
}
}
}
aLogProbTemp = negative_infinity();
for(k in 1:K){
if(mLogProb[T,k]>aLogProbTemp){
aLogProbTemp = mLogProb[T,k];
aBestFinal = k;
}
}
for(t in 1:T){
int aIndex;
aIndex = T - t + 1;
if(aIndex == T){
path[aIndex] = aBestFinal;
}else if(aIndex==1){
path[aIndex] = 1;
}else{
path[aIndex] = mBestIndex[aIndex,path[aIndex+1]];
}
}
}
}