/// <summary>Samples a single position in the sequence.</summary> /// <remarks> /// Samples a single position in the sequence. /// Does not modify the sequence passed in. /// returns the score of the new label for the position to sample /// </remarks> /// <param name="sequence">the sequence to start with</param> /// <param name="pos">the position to sample.</param> /// <param name="temperature">the temperature to control annealing</param> private Pair <int, double> SamplePositionHelper(ISequenceModel model, int[] sequence, int pos, double temperature) { double[] distribution = model.ScoresOf(sequence, pos); if (temperature != 1.0) { if (temperature == 0.0) { // set the max to 1.0 int argmax = ArrayMath.Argmax(distribution); Arrays.Fill(distribution, double.NegativeInfinity); distribution[argmax] = 0.0; } else { // take all to a power // use the temperature to increase/decrease the entropy of the sampling distribution ArrayMath.MultiplyInPlace(distribution, 1.0 / temperature); } } ArrayMath.LogNormalize(distribution); ArrayMath.ExpInPlace(distribution); int newTag = ArrayMath.SampleFromDistribution(distribution, random); double newProb = distribution[newTag]; return(new Pair <int, double>(newTag, newProb)); }
/// <summary>Samples each label in turn from left to right.</summary> /// <returns>an array containing the int tags of the best sequence</returns> public virtual int[] BestSequence(ISequenceModel ts) { // Also allocate space for rightWindow, just in case sequence model uses // it, even though this implementation doesn't. Probably it shouldn't, // or the left-to-right sampling is invalid, but our test classes do. int[] sample = new int[ts.Length() + ts.LeftWindow() + ts.RightWindow()]; for (int pos = ts.LeftWindow(); pos < sample.Length - ts.RightWindow(); pos++) { double[] scores = ts.ScoresOf(sample, pos); for (int i = 0; i < scores.Length; i++) { scores[i] = Math.Exp(scores[i]); } ArrayMath.Normalize(scores); int l = ArrayMath.SampleFromDistribution(scores); sample[pos] = ts.GetPossibleValues(pos)[l]; } return(sample); }
// todo: The current version has variables for a 2 model version and arrays for an n-model version. Unify. /// <summary> /// <inheritDoc/> /// /// </summary> public virtual double[] ScoresOf(int[] sequence, int pos) { if (models != null) { double[] dist = ArrayMath.Multiply(models[0].ScoresOf(sequence, pos), wts[0]); for (int i = 1; i < models.Length; i++) { double[] dist_i = models[i].ScoresOf(sequence, pos); ArrayMath.AddMultInPlace(dist, dist_i, wts[i]); } return(dist); } double[] dist1 = model1.ScoresOf(sequence, pos); double[] dist2 = model2.ScoresOf(sequence, pos); double[] dist_1 = new double[dist1.Length]; for (int i_1 = 0; i_1 < dist1.Length; i_1++) { dist_1[i_1] = model1Wt * dist1[i_1] + model2Wt * dist2[i_1]; } return(dist_1); }
/// <summary> /// Runs the Viterbi algorithm on the sequence model, and then proceeds to efficiently /// backwards decode the best k label sequence assignments. /// </summary> /// <remarks> /// Runs the Viterbi algorithm on the sequence model, and then proceeds to efficiently /// backwards decode the best k label sequence assignments. /// This sequence finder only works on SequenceModel's with rightWindow == 0. /// </remarks> /// <param name="ts">The SequenceModel to find the best k label sequence assignments of</param> /// <param name="k">The number of top-scoring assignments to find.</param> /// <returns>A Counter with k entries that map from a sequence assignment (int array) to a double score</returns> public virtual ICounter <int[]> KBestSequences(ISequenceModel ts, int k) { // Set up tag options int length = ts.Length(); int leftWindow = ts.LeftWindow(); int rightWindow = ts.RightWindow(); if (rightWindow != 0) { throw new ArgumentException("KBestSequenceFinder only works with rightWindow == 0 not " + rightWindow); } int padLength = length + leftWindow + rightWindow; int[][] tags = new int[padLength][]; int[] tagNum = new int[padLength]; for (int pos = 0; pos < padLength; pos++) { tags[pos] = ts.GetPossibleValues(pos); tagNum[pos] = tags[pos].Length; } int[] tempTags = new int[padLength]; // Set up product space sizes int[] productSizes = new int[padLength]; int curProduct = 1; for (int i = 0; i < leftWindow; i++) { curProduct *= tagNum[i]; } for (int pos_1 = leftWindow; pos_1 < padLength; pos_1++) { if (pos_1 > leftWindow + rightWindow) { curProduct /= tagNum[pos_1 - leftWindow - rightWindow - 1]; } // shift off curProduct *= tagNum[pos_1]; // shift on productSizes[pos_1 - rightWindow] = curProduct; } double[][] windowScore = new double[padLength][]; // Score all of each window's options for (int pos_2 = leftWindow; pos_2 < leftWindow + length; pos_2++) { windowScore[pos_2] = new double[productSizes[pos_2]]; Arrays.Fill(tempTags, tags[0][0]); for (int product = 0; product < productSizes[pos_2]; product++) { int p = product; int shift = 1; for (int curPos = pos_2; curPos >= pos_2 - leftWindow; curPos--) { tempTags[curPos] = tags[curPos][p % tagNum[curPos]]; p /= tagNum[curPos]; if (curPos > pos_2) { shift *= tagNum[curPos]; } } if (tempTags[pos_2] == tags[pos_2][0]) { // get all tags at once double[] scores = ts.ScoresOf(tempTags, pos_2); // fill in the relevant windowScores for (int t = 0; t < tagNum[pos_2]; t++) { windowScore[pos_2][product + t * shift] = scores[t]; } } } } // Set up score and backtrace arrays double[][][] score = new double[padLength][][]; int[][][][] trace = new int[padLength][][][]; int[][] numWaysToMake = new int[padLength][]; for (int pos_3 = 0; pos_3 < padLength; pos_3++) { score[pos_3] = new double[productSizes[pos_3]][]; trace[pos_3] = new int[productSizes[pos_3]][][]; // the 2 is for backtrace, and which of the k best for that backtrace numWaysToMake[pos_3] = new int[productSizes[pos_3]]; Arrays.Fill(numWaysToMake[pos_3], 1); for (int product = 0; product < productSizes[pos_3]; product++) { if (pos_3 > leftWindow) { // loop over possible predecessor types int sharedProduct = product / tagNum[pos_3]; int factor = productSizes[pos_3] / tagNum[pos_3]; numWaysToMake[pos_3][product] = 0; for (int newTagNum = 0; newTagNum < tagNum[pos_3 - leftWindow - 1] && numWaysToMake[pos_3][product] < k; newTagNum++) { int predProduct = newTagNum * factor + sharedProduct; numWaysToMake[pos_3][product] += numWaysToMake[pos_3 - 1][predProduct]; } if (numWaysToMake[pos_3][product] > k) { numWaysToMake[pos_3][product] = k; } } score[pos_3][product] = new double[numWaysToMake[pos_3][product]]; Arrays.Fill(score[pos_3][product], double.NegativeInfinity); trace[pos_3][product] = new int[numWaysToMake[pos_3][product]][]; Arrays.Fill(trace[pos_3][product], new int[] { -1, -1 }); } } // Do forward Viterbi algorithm // this is the hottest loop, so cache loop control variables hoping for a little speed.... // loop over the classification spot for (int pos_4 = leftWindow; pos_4 < posMax; pos_4++) { // loop over window product types for (int product = 0; product < productMax; product++) { // check for initial spot double[] scorePos = score[pos_4][product]; int[][] tracePos = trace[pos_4][product]; if (pos_4 == leftWindow) { // no predecessor type scorePos[0] = windowScore[pos_4][product]; } else { // loop over possible predecessor types/k-best int sharedProduct = product / tagNum[pos_4 + rightWindow]; int factor = productSizes[pos_4] / tagNum[pos_4 + rightWindow]; for (int newTagNum = 0; newTagNum < maxTagNum; newTagNum++) { int predProduct = newTagNum * factor + sharedProduct; double[] scorePosPrev = score[pos_4 - 1][predProduct]; for (int k1 = 0; k1 < scorePosPrev.Length; k1++) { double predScore = scorePosPrev[k1] + windowScore[pos_4][product]; if (predScore > scorePos[0]) { // new value higher then lowest value we should keep int k2 = Arrays.BinarySearch(scorePos, predScore); k2 = k2 < 0 ? -k2 - 2 : k2 - 1; // open a spot at k2 by shifting off the lowest value System.Array.Copy(scorePos, 1, scorePos, 0, k2); System.Array.Copy(tracePos, 1, tracePos, 0, k2); scorePos[k2] = predScore; tracePos[k2] = new int[] { predProduct, k1 }; } } } } } } // Project the actual tag sequence int[] whichDerivation = new int[k]; int[] bestCurrentProducts = new int[k]; double[] bestFinalScores = new double[k]; Arrays.Fill(bestFinalScores, double.NegativeInfinity); // just the last guy for (int product_1 = 0; product_1 < productSizes[padLength - 1]; product_1++) { double[] scorePos = score[padLength - 1][product_1]; for (int k1 = scorePos.Length - 1; k1 >= 0 && scorePos[k1] > bestFinalScores[0]; k1--) { int k2 = Arrays.BinarySearch(bestFinalScores, scorePos[k1]); k2 = k2 < 0 ? -k2 - 2 : k2 - 1; // open a spot at k2 by shifting off the lowest value System.Array.Copy(bestFinalScores, 1, bestFinalScores, 0, k2); System.Array.Copy(whichDerivation, 1, whichDerivation, 0, k2); System.Array.Copy(bestCurrentProducts, 1, bestCurrentProducts, 0, k2); bestCurrentProducts[k2] = product_1; whichDerivation[k2] = k1; bestFinalScores[k2] = scorePos[k1]; } } ClassicCounter <int[]> kBestWithScores = new ClassicCounter <int[]>(); for (int k1_1 = k - 1; k1_1 >= 0 && bestFinalScores[k1_1] > double.NegativeInfinity; k1_1--) { int lastProduct = bestCurrentProducts[k1_1]; for (int last = padLength - 1; last >= length - 1 && last >= 0; last--) { tempTags[last] = tags[last][lastProduct % tagNum[last]]; lastProduct /= tagNum[last]; } for (int pos_5 = leftWindow + length - 2; pos_5 >= leftWindow; pos_5--) { int bestNextProduct = bestCurrentProducts[k1_1]; bestCurrentProducts[k1_1] = trace[pos_5 + 1][bestNextProduct][whichDerivation[k1_1]][0]; whichDerivation[k1_1] = trace[pos_5 + 1][bestNextProduct][whichDerivation[k1_1]][1]; tempTags[pos_5 - leftWindow] = tags[pos_5 - leftWindow][bestCurrentProducts[k1_1] / (productSizes[pos_5] / tagNum[pos_5 - leftWindow])]; } kBestWithScores.SetCount(Arrays.CopyOf(tempTags, tempTags.Length), bestFinalScores[k1_1]); } return(kBestWithScores); }
private static Pair<int[], Double> BestSequenceNew(ISequenceModel ts, double[][] linearConstraints) { int length = ts.Length(); int leftWindow = ts.LeftWindow(); int rightWindow = ts.RightWindow(); int padLength = length + leftWindow + rightWindow; if (linearConstraints != null && linearConstraints.Length != padLength) throw new Exception(@"linearConstraints.length (" + linearConstraints.Length + @") does not match padLength (" + padLength + @") of SequenceModel" + @", length==" + length + @", leftW=" + leftWindow + @", rightW=" + rightWindow); int[][] tags = new int[padLength][]; int[] tagNum = new int[padLength]; if (DEBUG) { Console.Error.WriteLine(@"Doing bestSequence length " + length + @"; leftWin " + leftWindow + @"; rightWin " + rightWindow + @"; padLength " + padLength); } for (int pos = 0; pos < padLength; pos++) { tags[pos] = ts.GetPossibleValues(pos); tagNum[pos] = tags[pos].Length; if (DEBUG) { Console.Error.WriteLine(@"There are " + tagNum[pos] + @" values at position " + pos + @": " + Arrays.ToString(tags[pos])); } } int[] tempTags = new int[padLength]; int[] productSizes = new int[padLength]; int curProduct = 1; for (int i = 0; i < leftWindow + rightWindow; i++) { curProduct *= tagNum[i]; } for (int pos = leftWindow + rightWindow; pos < padLength; pos++) { if (pos > leftWindow + rightWindow) { curProduct /= tagNum[pos - leftWindow - rightWindow - 1]; } curProduct *= tagNum[pos]; productSizes[pos - rightWindow] = curProduct; } double[][] windowScore = new double[padLength][]; for (int pos = leftWindow; pos < leftWindow + length; pos++) { if (DEBUG) { Console.Error.WriteLine(@"scoring word " + pos + @" / " + (leftWindow + length) + @", productSizes = " + productSizes[pos] + @", tagNum = " + tagNum[pos] + @"..."); } windowScore[pos] = new double[productSizes[pos]]; Arrays.Fill(tempTags, tags[0][0]); if (DEBUG) { Console.Error.WriteLine(@"windowScore[" + pos + @"] has size (productSizes[pos]) " + windowScore[pos].Length); } for (int product = 0; product < productSizes[pos]; product++) { int p = product; int shift = 1; for (int curPos = pos + rightWindow; curPos >= pos - leftWindow; curPos--) { tempTags[curPos] = tags[curPos][p % tagNum[curPos]]; p /= tagNum[curPos]; if (curPos > pos) { shift *= tagNum[curPos]; } } if (tempTags[pos] == tags[pos][0]) { double[] scores = ts.ScoresOf(tempTags, pos); if (DEBUG) { Console.Error.WriteLine(@"Matched at array index [product] " + product + @"; tempTags[pos] == tags[pos][0] == " + tempTags[pos]); } if (DEBUG) { Console.Error.WriteLine(@"For pos " + pos + @" scores.length is " + scores.Length + @"; tagNum[pos] = " + tagNum[pos] + @"; windowScore[pos].length = " + windowScore[pos].Length); } if (DEBUG) { Console.Error.WriteLine(@"scores: " + Arrays.ToString(scores)); } for (int t = 0; t < tagNum[pos]; t++) { if (DEBUG) { Console.Error.WriteLine(@"Setting value of windowScore[" + pos + @"][" + product + @"+" + t + @"*" + shift + @"] = " + scores[t]); } windowScore[pos][product + t * shift] = scores[t]; } } } } double[][] score = new double[padLength][]; int[][] trace = new int[padLength][]; for (int pos = 0; pos < padLength; pos++) { score[pos] = new double[productSizes[pos]]; trace[pos] = new int[productSizes[pos]]; } for (int pos = leftWindow; pos < length + leftWindow; pos++) { for (int product = 0; product < productSizes[pos]; product++) { if (pos == leftWindow) { score[pos][product] = windowScore[pos][product]; if (linearConstraints != null) { if (DEBUG && linearConstraints[pos][product % tagNum[pos]] != 0) Console.Error.WriteLine(@"Applying linear constraints=" + linearConstraints[pos][product % tagNum[pos]] + @" to preScore=" + windowScore[pos][product] + @" at pos=" + pos + @" for tag=" + (product % tagNum[pos])); score[pos][product] += linearConstraints[pos][product % tagNum[pos]]; } trace[pos][product] = -1; } else { score[pos][product] = Double.NegativeInfinity; trace[pos][product] = -1; int sharedProduct = product / tagNum[pos + rightWindow]; int factor = productSizes[pos] / tagNum[pos + rightWindow]; for (int newTagNum = 0; newTagNum < tagNum[pos - leftWindow - 1]; newTagNum++) { int predProduct = newTagNum * factor + sharedProduct; double predScore = score[pos - 1][predProduct] + windowScore[pos][product]; if (linearConstraints != null) { if (DEBUG && pos == 2 && linearConstraints[pos][product % tagNum[pos]] != 0) { Console.Error.WriteLine(@"Applying linear constraints=" + linearConstraints[pos][product % tagNum[pos]] + @" to preScore=" + predScore + @" at pos=" + pos + @" for tag=" + (product % tagNum[pos])); Console.Error.WriteLine(@"predScore:" + predScore + @" = score[" + (pos - 1) + @"][" + predProduct + @"]:" + score[pos - 1][predProduct] + @" + windowScore[" + pos + @"][" + product + @"]:" + windowScore[pos][product]); } predScore += linearConstraints[pos][product % tagNum[pos]]; } if (predScore > score[pos][product]) { score[pos][product] = predScore; trace[pos][product] = predProduct; } } } } } double bestFinalScore = Double.NegativeInfinity; int bestCurrentProduct = -1; for (int product = 0; product < productSizes[leftWindow + length - 1]; product++) { if (score[leftWindow + length - 1][product] > bestFinalScore) { bestCurrentProduct = product; bestFinalScore = score[leftWindow + length - 1][product]; } } int lastProduct = bestCurrentProduct; for (int last = padLength - 1; last >= length - 1 && last >= 0; last--) { tempTags[last] = tags[last][lastProduct % tagNum[last]]; lastProduct /= tagNum[last]; } for (int pos = leftWindow + length - 2; pos >= leftWindow; pos--) { int bestNextProduct = bestCurrentProduct; bestCurrentProduct = trace[pos + 1][bestNextProduct]; tempTags[pos - leftWindow] = tags[pos - leftWindow][bestCurrentProduct / (productSizes[pos] / tagNum[pos - leftWindow])]; } return new Pair<int[], Double>(tempTags, bestFinalScore); }
public static DFSA <string, int> GetGraph(ISequenceModel ts, IIndex <string> classIndex) { DFSA <string, int> viterbiSearchGraph = new DFSA <string, int>(null); // Set up tag options int length = ts.Length(); int leftWindow = ts.LeftWindow(); int rightWindow = ts.RightWindow(); System.Diagnostics.Debug.Assert((rightWindow == 0)); int padLength = length + leftWindow + rightWindow; // NOTE: tags[i][j] : i is index into pos, and j into product int[][] tags = new int[padLength][]; int[] tagNum = new int[padLength]; for (int pos = 0; pos < padLength; pos++) { tags[pos] = ts.GetPossibleValues(pos); tagNum[pos] = tags[pos].Length; } // Set up Viterbi search graph: DFSAState <string, int>[][] graphStates = null; DFSAState <string, int> startState = null; DFSAState <string, int> endState = null; if (viterbiSearchGraph != null) { int stateId = -1; startState = new DFSAState <string, int>(++stateId, viterbiSearchGraph, 0.0); viterbiSearchGraph.SetInitialState(startState); graphStates = new DFSAState[length][]; for (int pos_1 = 0; pos_1 < length; ++pos_1) { //System.err.printf("%d states at pos %d\n",tags[pos].length,pos); graphStates[pos_1] = new DFSAState[tags[pos_1].Length]; for (int product = 0; product < tags[pos_1].Length; ++product) { graphStates[pos_1][product] = new DFSAState <string, int>(++stateId, viterbiSearchGraph); } } // Accepting state: endState = new DFSAState <string, int>(++stateId, viterbiSearchGraph, 0.0); endState.SetAccepting(true); } int[] tempTags = new int[padLength]; // Set up product space sizes int[] productSizes = new int[padLength]; int curProduct = 1; for (int i = 0; i < leftWindow; i++) { curProduct *= tagNum[i]; } for (int pos_2 = leftWindow; pos_2 < padLength; pos_2++) { if (pos_2 > leftWindow + rightWindow) { curProduct /= tagNum[pos_2 - leftWindow - rightWindow - 1]; } // shift off curProduct *= tagNum[pos_2]; // shift on productSizes[pos_2 - rightWindow] = curProduct; } double[][] windowScore = new double[padLength][]; // Score all of each window's options for (int pos_3 = leftWindow; pos_3 < leftWindow + length; pos_3++) { windowScore[pos_3] = new double[productSizes[pos_3]]; Arrays.Fill(tempTags, tags[0][0]); for (int product = 0; product < productSizes[pos_3]; product++) { int p = product; int shift = 1; for (int curPos = pos_3; curPos >= pos_3 - leftWindow; curPos--) { tempTags[curPos] = tags[curPos][p % tagNum[curPos]]; p /= tagNum[curPos]; if (curPos > pos_3) { shift *= tagNum[curPos]; } } if (tempTags[pos_3] == tags[pos_3][0]) { // get all tags at once double[] scores = ts.ScoresOf(tempTags, pos_3); // fill in the relevant windowScores for (int t = 0; t < tagNum[pos_3]; t++) { windowScore[pos_3][product + t * shift] = scores[t]; } } } } // loop over the classification spot for (int pos_4 = leftWindow; pos_4 < length + leftWindow; pos_4++) { // loop over window product types for (int product = 0; product < productSizes[pos_4]; product++) { if (pos_4 == leftWindow) { // all nodes in the first spot link to startState: int curTag = tags[pos_4][product % tagNum[pos_4]]; //System.err.printf("pos=%d, product=%d, tag=%d score=%.3f\n",pos,product,curTag,windowScore[pos][product]); DFSATransition <string, int> tr = new DFSATransition <string, int>(string.Empty, startState, graphStates[pos_4][product], classIndex.Get(curTag), string.Empty, -windowScore[pos_4][product]); startState.AddTransition(tr); } else { int sharedProduct = product / tagNum[pos_4 + rightWindow]; int factor = productSizes[pos_4] / tagNum[pos_4 + rightWindow]; for (int newTagNum = 0; newTagNum < tagNum[pos_4 - leftWindow - 1]; newTagNum++) { int predProduct = newTagNum * factor + sharedProduct; int predTag = tags[pos_4 - 1][predProduct % tagNum[pos_4 - 1]]; int curTag = tags[pos_4][product % tagNum[pos_4]]; //log.info("pos: "+pos); //log.info("product: "+product); //System.err.printf("pos=%d-%d, product=%d-%d, tag=%d-%d score=%.3f\n",pos-1,pos,predProduct,product,predTag,curTag, // windowScore[pos][product]); DFSAState <string, int> sourceState = graphStates[pos_4 - leftWindow][predTag]; DFSAState <string, int> destState = (pos_4 - leftWindow + 1 == graphStates.Length) ? endState : graphStates[pos_4 - leftWindow + 1][curTag]; DFSATransition <string, int> tr = new DFSATransition <string, int>(string.Empty, sourceState, destState, classIndex.Get(curTag), string.Empty, -windowScore[pos_4][product]); graphStates[pos_4 - leftWindow][predTag].AddTransition(tr); } } } } return(viterbiSearchGraph); }
private static Pair <int[], double> BestSequence(ISequenceModel ts, double[][] linearConstraints) { // Set up tag options int length = ts.Length(); int leftWindow = ts.LeftWindow(); int rightWindow = ts.RightWindow(); int padLength = length + leftWindow + rightWindow; if (linearConstraints != null && linearConstraints.Length != padLength) { throw new Exception("linearConstraints.length (" + linearConstraints.Length + ") does not match padLength (" + padLength + ") of SequenceModel" + ", length==" + length + ", leftW=" + leftWindow + ", rightW=" + rightWindow); } int[][] tags = new int[padLength][]; int[] tagNum = new int[padLength]; for (int pos = 0; pos < padLength; pos++) { if (Thread.Interrupted()) { // Allow interrupting throw new RuntimeInterruptedException(); } tags[pos] = ts.GetPossibleValues(pos); tagNum[pos] = tags[pos].Length; } int[] tempTags = new int[padLength]; // Set up product space sizes int[] productSizes = new int[padLength]; int curProduct = 1; for (int i = 0; i < leftWindow + rightWindow; i++) { curProduct *= tagNum[i]; } for (int pos_1 = leftWindow + rightWindow; pos_1 < padLength; pos_1++) { if (Thread.Interrupted()) { // Allow interrupting throw new RuntimeInterruptedException(); } if (pos_1 > leftWindow + rightWindow) { curProduct /= tagNum[pos_1 - leftWindow - rightWindow - 1]; } // shift off curProduct *= tagNum[pos_1]; // shift on productSizes[pos_1 - rightWindow] = curProduct; } // Score all of each window's options double[][] windowScore = new double[padLength][]; for (int pos_2 = leftWindow; pos_2 < leftWindow + length; pos_2++) { if (Thread.Interrupted()) { // Allow interrupting throw new RuntimeInterruptedException(); } windowScore[pos_2] = new double[productSizes[pos_2]]; Arrays.Fill(tempTags, tags[0][0]); for (int product = 0; product < productSizes[pos_2]; product++) { if (Thread.Interrupted()) { // Allow interrupting throw new RuntimeInterruptedException(); } int p = product; int shift = 1; for (int curPos = pos_2 + rightWindow; curPos >= pos_2 - leftWindow; curPos--) { tempTags[curPos] = tags[curPos][p % tagNum[curPos]]; p /= tagNum[curPos]; if (curPos > pos_2) { shift *= tagNum[curPos]; } } // Here now you get ts.scoresOf() for all classifications at a position at once, whereas the old code called ts.scoreOf() on each item. // CDM May 2007: The way this is done gives incorrect results if there are repeated values in the values of ts.getPossibleValues(pos) -- in particular if the first value of the array is repeated later. I tried replacing it with the modulo version, but that only worked for left-to-right, not bidirectional inference, but I still think that if you sorted things out, you should be able to do it with modulos and the result would be conceptually simpler and robust to repeated values. But in the meantime, I fixed the POS tagger to not give repeated values (which was a bug in the tagger). if (tempTags[pos_2] == tags[pos_2][0]) { // get all tags at once double[] scores = ts.ScoresOf(tempTags, pos_2); // fill in the relevant windowScores for (int t = 0; t < tagNum[pos_2]; t++) { windowScore[pos_2][product + t * shift] = scores[t]; } } } } // Set up score and backtrace arrays double[][] score = new double[padLength][]; int[][] trace = new int[padLength][]; for (int pos_3 = 0; pos_3 < padLength; pos_3++) { if (Thread.Interrupted()) { // Allow interrupting throw new RuntimeInterruptedException(); } score[pos_3] = new double[productSizes[pos_3]]; trace[pos_3] = new int[productSizes[pos_3]]; } // Do forward Viterbi algorithm // loop over the classification spot //log.info(); for (int pos_4 = leftWindow; pos_4 < length + leftWindow; pos_4++) { //log.info("."); // loop over window product types for (int product = 0; product < productSizes[pos_4]; product++) { if (Thread.Interrupted()) { // Allow interrupting throw new RuntimeInterruptedException(); } // check for initial spot if (pos_4 == leftWindow) { // no predecessor type score[pos_4][product] = windowScore[pos_4][product]; if (linearConstraints != null) { score[pos_4][product] += linearConstraints[pos_4][product % tagNum[pos_4]]; } trace[pos_4][product] = -1; } else { // loop over possible predecessor types score[pos_4][product] = double.NegativeInfinity; trace[pos_4][product] = -1; int sharedProduct = product / tagNum[pos_4 + rightWindow]; int factor = productSizes[pos_4] / tagNum[pos_4 + rightWindow]; for (int newTagNum = 0; newTagNum < tagNum[pos_4 - leftWindow - 1]; newTagNum++) { int predProduct = newTagNum * factor + sharedProduct; double predScore = score[pos_4 - 1][predProduct] + windowScore[pos_4][product]; if (linearConstraints != null) { predScore += linearConstraints[pos_4][product % tagNum[pos_4]]; } if (predScore > score[pos_4][product]) { score[pos_4][product] = predScore; trace[pos_4][product] = predProduct; } } } } } // Project the actual tag sequence double bestFinalScore = double.NegativeInfinity; int bestCurrentProduct = -1; for (int product_1 = 0; product_1 < productSizes[leftWindow + length - 1]; product_1++) { if (score[leftWindow + length - 1][product_1] > bestFinalScore) { bestCurrentProduct = product_1; bestFinalScore = score[leftWindow + length - 1][product_1]; } } int lastProduct = bestCurrentProduct; for (int last = padLength - 1; last >= length - 1 && last >= 0; last--) { tempTags[last] = tags[last][lastProduct % tagNum[last]]; lastProduct /= tagNum[last]; } for (int pos_5 = leftWindow + length - 2; pos_5 >= leftWindow; pos_5--) { int bestNextProduct = bestCurrentProduct; bestCurrentProduct = trace[pos_5 + 1][bestNextProduct]; tempTags[pos_5 - leftWindow] = tags[pos_5 - leftWindow][bestCurrentProduct / (productSizes[pos_5] / tagNum[pos_5 - leftWindow])]; } return(new Pair <int[], double>(tempTags, bestFinalScore)); }