public Path viterbi()
    {
        int nPos = sequence.length();
        int[][] backpointer = new int[nPos][numLabels];

        double [] scores = new double[numLabels];
        double[] us = new double[numLabels];
        double[] bs = new double[numLabels];

        System.arraycopy(u[0], 0, scores, 0, numLabels);
        for (int pos = 1; pos < nPos; ++pos)
        {
            System.arraycopy(u[pos], 0, us, 0, numLabels);

            for (int yi = 0; yi < numLabels; ++yi)
            {
                // bs = b[pos-1][yi] + scores
                CollectionsManip.copyAdd(bs, b[pos - 1][yi], scores);

                double best = bs[0];
                int bestj = 0;
                for (int yj = 1; yj < numLabels; ++yj)
                {
                    if (bs[yj] > best)
                    {
                        best = bs[yj];
                        bestj = yj;
                    }
                }

                backpointer[pos][yi] = bestj;
                us[yi] += best;
            }
            System.arraycopy(us, 0, scores, 0, us.length);

        }

        int bestj = 0;
        double best = scores[0];

        for (int yj = 1; yj < numLabels; ++yj)
        {
            // Find the maximum score at yj
            if (scores[yj] > best)
            {
                best = scores[yj];
                bestj = yj;
            }
        }
        Path path = new Path(nPos, best);
        for (int pos = nPos - 1; pos >= 0; pos--)
        {
            path.set(pos, bestj);
            bestj = backpointer[pos][bestj];
        }

        return path;
    }