/// <summary>
        ///   Computes Forward probabilities for a given hidden Markov model and a set of observations.
        /// </summary>
        ///
        public static void LogForward <TObservation>(FactorPotential <TObservation> function,
                                                     TObservation[] observations, int output, double[,] lnFwd)
        {
            int states = function.States;

            int T = observations.Length;

            // Ensures minimum requirements
            Accord.Diagnostics.Debug.Assert(lnFwd.GetLength(0) >= T);
            Accord.Diagnostics.Debug.Assert(lnFwd.GetLength(1) == states);
            Array.Clear(lnFwd, 0, lnFwd.Length);


            // 1. Initialization
            for (int i = 0; i < states; i++)
            {
                lnFwd[0, i] = function.Compute(-1, i, observations, 0, output);
            }

            // 2. Induction
            for (int t = 1; t < T; t++)
            {
                for (int i = 0; i < states; i++)
                {
                    double sum = Double.NegativeInfinity;
                    for (int j = 0; j < states; j++)
                    {
                        sum = Special.LogSum(sum, lnFwd[t - 1, j] + function.Compute(j, i, observations, t, output));
                    }
                    lnFwd[t, i] = sum;
                }
            }
        }
        /// <summary>
        ///   Computes Forward probabilities for a given potential function and a set of observations.
        /// </summary>
        ///
        public static double[,] Forward <TObservation>(FactorPotential <TObservation> function,
                                                       TObservation[] observations, int output = 0)
        {
            int states = function.States;

            int T = observations.Length;

            double[,] fwd = new double[T, states];

            // 1. Initialization
            for (int i = 0; i < states; i++)
            {
                fwd[0, i] = Math.Exp(function.Compute(-1, i, observations, 0, output));
            }

            // 2. Induction
            for (int t = 1; t < T; t++)
            {
                for (int i = 0; i < states; i++)
                {
                    double sum = 0.0;
                    for (int j = 0; j < states; j++)
                    {
                        sum += fwd[t - 1, j] * Math.Exp(function.Compute(j, i, observations, t, output));
                    }
                    fwd[t, i] = sum;
                }
            }

            return(fwd);
        }
        /// <summary>
        ///   Computes Forward probabilities for a given hidden Markov model and a set of observations.
        /// </summary>
        ///
        public static void Forward <TObservation>(FactorPotential <TObservation> function,
                                                  TObservation[] observations, int output, double[] scaling, double[,] fwd)
        {
            int states = function.States;

            int    T = observations.Length;
            double s = 0;

            // Ensures minimum requirements
            Accord.Diagnostics.Debug.Assert(fwd.GetLength(0) >= T);
            Accord.Diagnostics.Debug.Assert(fwd.GetLength(1) == states);
            Accord.Diagnostics.Debug.Assert(scaling.Length >= T);
            Array.Clear(fwd, 0, fwd.Length);


            // 1. Initialization
            for (int i = 0; i < states; i++)
            {
                s += fwd[0, i] = Math.Exp(function.Compute(-1, i, observations, 0, output));
            }
            scaling[0] = s;

            if (s != 0) // Scaling
            {
                for (int i = 0; i < states; i++)
                {
                    fwd[0, i] /= s;
                }
            }


            // 2. Induction
            for (int t = 1; t < T; t++)
            {
                s = 0;

                for (int i = 0; i < states; i++)
                {
                    double sum = 0.0;
                    for (int j = 0; j < states; j++)
                    {
                        sum += fwd[t - 1, j] * Math.Exp(function.Compute(j, i, observations, t, output));
                    }
                    fwd[t, i] = sum;

                    s += fwd[t, i]; // scaling coefficient
                }

                scaling[t] = s;

                if (s != 0) // Scaling
                {
                    for (int i = 0; i < states; i++)
                    {
                        fwd[t, i] /= s;
                    }
                }
            }
        }
        /// <summary>
        ///   Computes Backward probabilities for a given potential function and a set of observations.
        /// </summary>
        ///
        public static double[,] Backward <TObservation>(FactorPotential <TObservation> function,
                                                        TObservation[] observations, int output = 0)
        {
            int states = function.States;

            int T = observations.Length;

            double[,] bwd = new double[T, states];

            // 1. Initialization
            for (int i = 0; i < states; i++)
            {
                bwd[T - 1, i] = 1.0;
            }

            // 2. Induction
            for (int t = T - 2; t >= 0; t--)
            {
                for (int i = 0; i < states; i++)
                {
                    double sum = 0;
                    for (int j = 0; j < states; j++)
                    {
                        sum += bwd[t + 1, j] * Math.Exp(function.Compute(i, j, observations, t + 1, output));
                    }
                    bwd[t, i] += sum;
                }
            }

            return(bwd);
        }
        /// <summary>
        ///   Computes Backward probabilities for a given potential function and a set of observations.
        /// </summary>
        ///
        public static void Backward <TObservation>(FactorPotential <TObservation> function,
                                                   TObservation[] observations, int output, double[] scaling, double[,] bwd)
        {
            int states = function.States;
            int T      = observations.Length;

            // Ensures minimum requirements
            Accord.Diagnostics.Debug.Assert(bwd.GetLength(0) >= T);
            Accord.Diagnostics.Debug.Assert(bwd.GetLength(1) == states);
            Array.Clear(bwd, 0, bwd.Length);

            // For backward variables, we use the same scale factors
            //   for each time t as were used for forward variables.

            // 1. Initialization
            for (int i = 0; i < states; i++)
            {
                bwd[T - 1, i] = 1.0 / scaling[T - 1];
            }

            // 2. Induction
            for (int t = T - 2; t >= 0; t--)
            {
                for (int i = 0; i < states; i++)
                {
                    double sum = 0;
                    for (int j = 0; j < states; j++)
                    {
                        sum += bwd[t + 1, j] * Math.Exp(function.Compute(i, j, observations, t + 1, output));
                    }
                    bwd[t, i] += sum / scaling[t];
                }
            }
        }
        /// <summary>
        ///   Computes Backward probabilities for a given potential function and a set of observations.
        /// </summary>
        ///
        public static void LogBackward <TObservation>(FactorPotential <TObservation> function,
                                                      TObservation[] observations, int output, double[,] lnBwd)
        {
            int states = function.States;
            int T      = observations.Length;

            // Ensures minimum requirements
            System.Diagnostics.Debug.Assert(lnBwd.GetLength(0) >= T);
            System.Diagnostics.Debug.Assert(lnBwd.GetLength(1) == states);
            Array.Clear(lnBwd, 0, lnBwd.Length);

            // 1. Initialization
            for (int i = 0; i < states; i++)
            {
                lnBwd[T - 1, i] = 0;
            }

            // 2. Induction
            for (int t = T - 2; t >= 0; t--)
            {
                for (int i = 0; i < states; i++)
                {
                    double sum = Double.NegativeInfinity;
                    for (int j = 0; j < states; j++)
                    {
                        sum = Special.LogSum(sum, lnBwd[t + 1, j] + function.Compute(i, j, observations, t + 1, output));
                    }
                    lnBwd[t, i] += sum;
                }
            }
        }
        /// <summary>
        ///   Computes Backward probabilities for a given potential function and a set of observations(no scaling).
        /// </summary>
        public static double[,] LogBackward <TObservation>(FactorPotential <TObservation> function,
                                                           TObservation[] observations, int output, out double logLikelihood)
        {
            int states = function.States;

            var lnBwd = LogBackward(function, observations, output);

            logLikelihood = double.NegativeInfinity;
            for (int i = 0; i < states; i++)
            {
                logLikelihood = Special.LogSum(logLikelihood, lnBwd[0, i] + function.Compute(-1, i, observations, 0, output));
            }

            return(lnBwd);
        }
        /// <summary>
        ///   Computes Backward probabilities for a given potential function and a set of observations(no scaling).
        /// </summary>
        public static double[,] Backward <TObservation>(FactorPotential <TObservation> function,
                                                        TObservation[] observations, int output, out double logLikelihood)
        {
            int states = function.States;

            var bwd = Backward(function, observations, output);

            double likelihood = 0;

            for (int i = 0; i < states; i++)
            {
                likelihood += bwd[0, i] * Math.Exp(function.Compute(-1, i, observations, 0, output));
            }
            logLikelihood = Math.Log(likelihood);

            return(bwd);
        }
        /// <summary>
        ///   Computes the probability of occurance of this
        ///   feature given a sequence of observations.
        /// </summary>
        ///
        /// <param name="fwd">The matrix of forward state probabilities.</param>
        /// <param name="bwd">The matrix of backward state probabilties.</param>
        /// <param name="x">The observation sequence.</param>
        /// <param name="y">The output class label for the sequence.</param>
        ///
        /// <returns>The probability of occurance of this feature.</returns>
        ///
        public override double Marginal(double[,] fwd, double[,] bwd, T[] x, int y)
        {
            // Assume the simplifying structure that each
            // factor is responsible for single output y.
            if (y != OwnerFactorIndex)
            {
                return(0);
            }

            FactorPotential <T> owner = Owner.Factors[OwnerFactorIndex];

            double marginal = 0;

            for (int t = 0; t < x.Length - 1; t++)
            {
                marginal += fwd[t, previous] * bwd[t + 1, current]
                            * Math.Exp(owner.Compute(previous, current, x, t + 1, y));
            }

            return(marginal);
        }
        /// <summary>
        ///   Computes the log-probability of occurance of this
        ///   feature given a sequence of observations.
        /// </summary>
        ///
        /// <param name="lnFwd">The matrix of forward state log-probabilities.</param>
        /// <param name="lnBwd">The matrix of backward state log-probabilties.</param>
        /// <param name="x">The observation sequence.</param>
        /// <param name="y">The output class label for the sequence.</param>
        ///
        /// <returns>The probability of occurance of this feature.</returns>
        ///
        public override double LogMarginal(double[,] lnFwd, double[,] lnBwd, T[] x, int y)
        {
            // Assume the simplifying structure that each
            // factor is responsible for single output y.
            if (y != OwnerFactorIndex)
            {
                return(Double.NegativeInfinity);
            }

            FactorPotential <T> owner = Owner.Factors[OwnerFactorIndex];

            double marginal = double.NegativeInfinity;

            for (int t = 0; t < x.Length - 1; t++)
            {
                marginal = Special.LogSum(marginal, lnFwd[t, previous] + lnBwd[t + 1, current]
                                          + owner.Compute(previous, current, x, t + 1, y));
            }

            return(marginal);
        }