Approach: use the forward algorithm to compute α_t(s) = P(o1..ot, state_t = s). I'll give straightforward Python pseudocode, then a numerically stable version using scaling factors and show how to get log-likelihood. Finally a small 2-state discrete HMM example with a sequence of length 10.Basic forward (may underflow for long T)python
import numpy as np
def forward(A, B, pi, O):
# A: (N,N) transition, B: (N,M) emission probs, pi: (N,) initial, O: list of obs indices length T
N = A.shape[0]; T = len(O)
alpha = np.zeros((T, N))
# t=0
alpha[0] = pi * B[:, O[0]]
for t in range(1, T):
for j in range(N):
alpha[t,j] = B[j, O[t]] * np.sum(alpha[t-1] * A[:, j])
prob = np.sum(alpha[T-1])
return alpha, prob
Numerical stability — scaling factors- At each t compute c[t] = 1 / sum(alpha[t]) and multiply alpha[t] *= c[t]. Store log-likelihood = -sum(log(c[t])).Stable forward with scaling:python
def forward_scaled(A, B, pi, O):
N = A.shape[0]; T = len(O)
alpha = np.zeros((T, N))
c = np.zeros(T)
alpha[0] = pi * B[:, O[0]]
c[0] = 1.0 / np.sum(alpha[0])
alpha[0] *= c[0]
for t in range(1, T):
for j in range(N):
alpha[t,j] = B[j, O[t]] * np.sum(alpha[t-1] * A[:, j])
c[t] = 1.0 / np.sum(alpha[t])
alpha[t] *= c[t]
log_likelihood = -np.sum(np.log(c)) # log P(O)
return alpha, log_likelihood
Log-space alternative- Work with logA, logB, logpi and use log-sum-exp for numeric safety. That yields log α directly; final log-likelihood = logsumexp(log_alpha[T-1]).Complexity: O(N^2 T) time, O(N T) space (can reduce to O(N)). Space/time same for scaled/log versions.Example HMM (N=2, emissions {0,1}), sequence length 10:- A = [[0.7,0.3],[0.4,0.6]]- B = [[0.9,0.1],[0.2,0.8]]- pi=[0.6,0.4]- Example sequence O = [0,0,1,0,1,1,0,0,1,0] (length 10)Using forward_scaled returns alpha matrix and log_likelihood (e.g. ≈ -5.3 depending on the sequence). Scaling prevents underflow for T=10+ and gives stable log-likelihood for model comparison and learning (e.g., in Baum-Welch).