To implement Viterbi robustly, run the DP in log-space to avoid underflow: compute log(pi) + sum of log transitions and log emissions. Keep two arrays per time step: delta[t][s] = max log-prob of path ending in state s at time t, and psi[t][s] = argmax previous state. Reconstruct by backtracking psi from argmax at final time.python
import math
from typing import List, Tuple
def log(x):
return math.log(x) if x > 0 else float("-inf")
def viterbi(A: List[List[float]], B: List[List[float]], pi: List[float], O: List[int]) -> Tuple[List[int], float]:
"""
A: SxS transition matrix (A[i][j] = P(s_j | s_i))
B: SxM emission matrix (B[s][o] = P(o | s))
pi: length-S initial state probs
O: length-T observation indices (0..M-1)
Returns: (most likely state sequence as list of state indices, log-probability)
"""
S = len(pi)
T = len(O)
if S == 0 or T == 0:
return ([], float("-inf") if T>0 else 0.0)
# precompute logs
logA = [[log(a) for a in row] for row in A]
logB = [[log(b) for b in row] for row in B]
logpi = [log(p) for p in pi]
# dp arrays: delta_prev, delta_curr: length S
delta = [logpi[s] + logB[s][O[0]] for s in range(S)]
psi = [[0]*S for _ in range(T)] # backpointers
for t in range(1, T):
new_delta = [float("-inf")] * S
for j in range(S):
best_val = float("-inf")
best_state = 0
emit = logB[j][O[t]]
for i in range(S):
val = delta[i] + logA[i][j] + emit
if val > best_val:
best_val = val
best_state = i
new_delta[j] = best_val
psi[t][j] = best_state
delta = new_delta
# termination
best_final = max(range(S), key=lambda s: delta[s])
best_logp = delta[best_final]
# backtrack
path = [0]*T
path[-1] = best_final
for t in range(T-1, 0, -1):
path[t-1] = psi[t][path[t]]
return path, best_logp
Key points:- Use log-space to prevent underflow; treat zeros as -inf.- Time complexity: O(T * S^2) (for dense A). Space: O(T * S) for psi plus O(S) for current DP row. You can reduce space to O(S) for psi if you store backpointers compactly or only keep last K steps.- Edge cases: zero probabilities, empty observations, non-normalized matrices (works in log-space but semantically expect probabilities).