/// <summary> /// Backward algorithm (without scaling) /// </summary> /// <param name="observations">A sequence of observations.</param> /// <returns></returns> private double backward(int[] observations) { if (observations == null) { throw new ArgumentNullException("observations"); } if (tempInstancts == null) { throw new ArgumentNullException("tempInstancts"); } int T = observations.Length; // For beta variables, I use the same scale factors // for each time t as I used for alpha variables. // 1. Initialization for (int i = 0; i < States; i++) { tempInstancts[T - 1].Beta[i] = 1.0 / tempInstancts[T - 1].c; } // 2. Induction for (int t = T - 2; t >= 0; t--) { for (int i = 0; i < States; i++) { double sum = 0.0; for (int j = 0; j < States; j++) { sum += (Transitions.getValue(i, j) * Emissions.getValue(j, observations[t + 1]) * tempInstancts[t + 1].Beta[j]); } tempInstancts[t].Beta[i] += sum / tempInstancts[t].c; } } // 3. Termination double POGivenLambdaScaled = 0.0; for (int i = 0; i < Probabilities.Length; i++) { POGivenLambdaScaled += Probabilities[i] * Emissions.getValue(i, observations[0]) * tempInstancts[0].Beta[i]; } double scaling = 1.0; for (int t = 0; t < T; t++) { scaling *= tempInstancts[t].c; } var POGivenLambda = POGivenLambdaScaled * scaling; return(POGivenLambda); }
/// <summary> /// Determining the frequency of the transition-emission pair values /// and dividing it by the probability of the entire string. /// /// Using the scaled values, I don't need to divide it for the /// probability of the entire string anymore /// </summary> /// <param name="t">Current time.</param> /// <param name="nextObservation">Observation at time t + 1.</param> /// <returns>Probability of transition from each state to any other at time t</returns> private MySparse2DMatrix calcXi(int t, int nextObservation) { if (tempInstancts == null) { throw new ArgumentNullException("tempInstancts"); } if (tempInstancts[t].Alpha == null || tempInstancts[t].Beta == null) { throw new ArgumentNullException("Alpha and Beta aren't set"); } MySparse2DMatrix currentXi = new MySparse2DMatrix(); double s = 0; for (int i = 0; i < States; i++) { for (int j = 0; j < States; j++) { double temp = tempInstancts[t].Alpha[i] * Transitions.getValue(i, j) * Emissions.getValue(j, nextObservation) * tempInstancts[t + 1].Beta[j]; s += temp; if (temp != 0) { currentXi.setValue(i, j, temp); } } } if (s != 0) // Scaling { for (int i = 0; i < States; i++) { for (int j = 0; j < States; j++) { if (currentXi.getValue(i, j) != 0) { currentXi.setValue(i, j, currentXi.getValue(i, j) / s); } } } } return(currentXi); }
/// <summary> /// Calculates the most likely sequence of hidden states /// that produced the given observation sequence. /// </summary> /// <remarks> /// Decoding problem. Given the HMM M = (A, B, pi) and the observation sequence /// O = {o1,o2, ..., oK}, calculate the most likely sequence of hidden states Si /// that produced this observation sequence O. This can be computed efficiently /// using the Viterbi algorithm. /// </remarks> /// <param name="observations">A sequence of observations.</param> /// <param name="probability">The state optimized probability.</param> /// <returns>The sequence of states that most likely produced the sequence.</returns> private int[] viterbi(int[] observations, out double probability) { if (observations == null) { throw new ArgumentNullException("observations"); } if (tempInstancts == null) { throw new ArgumentNullException("tempInstancts"); } if (observations.Length == 0) { probability = 0.0; return(new int[0]); } int T = observations.Length; double maxWeight; int maxState; // 1. Base for (int i = 0; i < States; i++) { tempInstancts[0].Delta[i] = Probabilities[i] * Emissions.getValue(i, observations[0]); } // 2. Induction for (int t = 1; t < T; t++) { for (int i = 0; i < States; i++) { maxWeight = 0.0; maxState = 0; for (int k = 0; k < States; k++) { double weight = tempInstancts[t - 1].Delta[k] * Transitions.getValue(k, i); if (weight > maxWeight) { maxWeight = weight; maxState = k; } } tempInstancts[t].Delta[i] = maxWeight * Emissions.getValue(i, observations[t]); tempInstancts[t].State[i] = maxState; } } // Find maximum value for time T-1 maxWeight = tempInstancts[T - 1].Delta[0]; maxState = 0; for (int k = 1; k < States; k++) { if (tempInstancts[T - 1].Delta[k] > maxWeight) { maxWeight = tempInstancts[T - 1].Delta[k]; maxState = k; } } // Trackback int[] path = new int[T]; path[T - 1] = maxState; for (int t = T - 2; t >= 0; t--) { path[t] = tempInstancts[t + 1].State[path[t + 1]]; } // Returns the sequence probability as an out parameter probability = maxWeight; // Returns the most likely (Viterbi path) for the given sequence return(path); }
/// <summary> /// Forward algorithm (with scaling) /// </summary> /// <param name="observations">A sequence of observations.</param> /// <returns></returns> private double forward(int[] observations) { if (observations == null) { throw new ArgumentNullException("observations"); } if (tempInstancts == null) { throw new ArgumentNullException("tempInstancts"); } int T = observations.Length; // 1. Initialization for (int i = 0; i < States; i++) { tempInstancts[0].c += tempInstancts[0].Alpha[i] = Probabilities[i] * Emissions.getValue(i, observations[0]); } if (tempInstancts[0].c != 0) // Scaling CHECK { for (int i = 0; i < States; i++) { tempInstancts[0].Alpha[i] = tempInstancts[0].Alpha[i] / tempInstancts[0].c; } } // 2. Induction for (int t = 1; t < T; t++) { for (int i = 0; i < States; i++) { double sum = 0.0; for (int k = 0; k < States; k++) { sum += (tempInstancts[t - 1].Alpha[k] * Transitions.getValue(k, i)); } tempInstancts[t].Alpha[i] = sum * Emissions.getValue(i, observations[t]); tempInstancts[t].c += tempInstancts[t].Alpha[i]; // Scaling coefficient } if (tempInstancts[t].c != 0) // Scaling { for (int i = 0; i < States; i++) { tempInstancts[t].Alpha[i] = tempInstancts[t].Alpha[i] / tempInstancts[t].c; } } } // 3. Termination double POGivenLambdaScaled = 0.0; for (int i = 0; i < Probabilities.Length; i++) { POGivenLambdaScaled += tempInstancts[T - 1].Alpha[i]; } double scaling = 1.0; for (int t = 0; t < T; t++) { scaling *= tempInstancts[t].c; } var POGivenLambda = POGivenLambdaScaled * scaling; return(POGivenLambda); }