public void LearnTest1()
        {
            // Create a Continuous density Hidden Markov Model Sequence Classifier
            // to detect a univariate sequence and the same sequence backwards.
            double[][] sequences = new double[][] 
            {
                new double[] { 0,1,2,3,4 }, // This is the first  sequence with label = 0
                new double[] { 4,3,2,1,0 }, // This is the second sequence with label = 1
            };

            // Labels for the sequences
            int[] labels = { 0, 1 };

            // Creates a sequence classifier containing 2 hidden Markov Models
            //  with 2 states and an underlying Normal distribution as density.
            NormalDistribution density = new NormalDistribution();
            var classifier = new HiddenMarkovClassifier<NormalDistribution, double>(2, new Ergodic(2), density);

            // Configure the learning algorithms to train the sequence classifier
            var teacher = new HiddenMarkovClassifierLearning<NormalDistribution, double>(classifier)
            {
                // Train each model until the log-likelihood changes less than 0.001
                Learner = modelIndex => new BaumWelchLearning<NormalDistribution, double>(classifier.Models[modelIndex])
                {
                    Tolerance = 0.0001,
                    Iterations = 0
                }
            };

            // Train the sequence classifier using the algorithm
            teacher.Learn(sequences, labels);
            double logLikelihood = teacher.LogLikelihood;


            // Calculate the probability that the given
            //  sequences originated from the model
            double likelihood1, likelihood2;

            // Try to classify the first sequence (output should be 0)
            int c1 = classifier.Decide(sequences[0]);
            likelihood1 = classifier.Probability(sequences[0]);

            // Try to classify the second sequence (output should be 1)
            int c2 = classifier.Decide(sequences[1]);
            likelihood2 = classifier.Probability(sequences[1]);

            Assert.AreEqual(0, c1);
            Assert.AreEqual(1, c2);


            Assert.AreEqual(-13.271981026832929, logLikelihood, 1e-10);
            Assert.AreEqual(0.99999791320102149, likelihood1, 1e-10);
            Assert.AreEqual(0.99999791320102149, likelihood2, 1e-10);
        }
        private static double testThresholdModel(int[][] inputs, int[] outputs, HiddenMarkovClassifier<GeneralDiscreteDistribution, int> classifier, double likelihood)
        {
            var threshold = classifier.Threshold;

            Assert.AreEqual(classifier.Models[0].LogTransitions[0][0], threshold.LogTransitions[0][0], 1e-10);
            Assert.AreEqual(classifier.Models[0].LogTransitions[1][1], threshold.LogTransitions[1][1], 1e-10);
            Assert.AreEqual(classifier.Models[0].LogTransitions[2][2], threshold.LogTransitions[2][2], 1e-10);

            Assert.AreEqual(classifier.Models[1].LogTransitions[0][0], threshold.LogTransitions[3][3], 1e-10);
            Assert.AreEqual(classifier.Models[1].LogTransitions[1][1], threshold.LogTransitions[4][4], 1e-10);
            Assert.AreEqual(classifier.Models[1].LogTransitions[2][2], threshold.LogTransitions[5][5], 1e-10);

            for (int i = 0; i < 3; i++)
                for (int j = 3; j < 6; j++)
                    Assert.AreEqual(Double.NegativeInfinity, threshold.LogTransitions[i][j]);

            for (int i = 3; i < 6; i++)
                for (int j = 0; j < 3; j++)
                    Assert.AreEqual(Double.NegativeInfinity, threshold.LogTransitions[i][j]);

            Assert.IsFalse(Matrix.HasNaN(threshold.LogTransitions));


            classifier.Sensitivity = 0.5;

            // Will assert the models have learned the sequences correctly.
            for (int i = 0; i < inputs.Length; i++)
            {
                int expected = outputs[i];
                int actual = classifier.Decide(inputs[i]);
                likelihood = classifier.Probability(inputs[i]);
                Assert.AreEqual(expected, actual);
            }


            int[] r0 = new int[] { 1, 1, 0, 0, 2 };


            double logRejection;
            int c = classifier.Decide(r0);
            logRejection = classifier.Probability(r0);

            Assert.AreEqual(-1, c);
            Assert.AreEqual(0.99993993054384978, logRejection);

            logRejection = threshold.LogLikelihood(r0);
            Assert.AreEqual(-5.6367018741984483, logRejection);
            Assert.IsFalse(double.IsNaN(logRejection));

            threshold.Decode(r0, out logRejection);
            Assert.AreEqual(-8.1618027917853073, logRejection);
            Assert.IsFalse(double.IsNaN(logRejection));

            foreach (var model in classifier.Models)
            {
                double[,] A = model.LogTransitions.ToMatrix();

                for (int i = 0; i < A.GetLength(0); i++)
                {
                    double[] row = A.Exp().GetRow(i);
                    double sum = row.Sum();
                    Assert.AreEqual(1, sum, 1e-10);
                }
            }
            {
                double[,] A = classifier.Threshold.LogTransitions.ToMatrix();

                for (int i = 0; i < A.GetLength(0); i++)
                {
                    double[] row = A.GetRow(i);
                    double sum = row.Exp().Sum();
                    Assert.AreEqual(1, sum, 1e-6);
                }
            }
            return likelihood;
        }
        public void LearnTest7()
        {
            // Create a Continuous density Hidden Markov Model Sequence Classifier
            // to detect a multivariate sequence and the same sequence backwards.

            double[][][] sequences = new double[][][]
            {
                new double[][] 
                { 
                    // This is the first  sequence with label = 0
                    new double[] { 0, 1 },
                    new double[] { 1, 2 },
                    new double[] { 2, 3 },
                    new double[] { 3, 4 },
                    new double[] { 4, 5 },
                }, 

                new double[][]
                {
                        // This is the second sequence with label = 1
                    new double[] { 4,  3 },
                    new double[] { 3,  2 },
                    new double[] { 2,  1 },
                    new double[] { 1,  0 },
                    new double[] { 0, -1 },
                }
            };

            // Labels for the sequences
            int[] labels = { 0, 1 };


            var initialDensity = new MultivariateNormalDistribution(2);

            // Creates a sequence classifier containing 2 hidden Markov Models with 2 states
            // and an underlying multivariate mixture of Normal distributions as density.
            var classifier = new HiddenMarkovClassifier<MultivariateNormalDistribution, double[]>(
                classes: 2, topology: new Forward(2), initial: initialDensity);

            // Configure the learning algorithms to train the sequence classifier
            var teacher = new HiddenMarkovClassifierLearning<MultivariateNormalDistribution, double[]>(classifier)
            {
                // Train each model until the log-likelihood changes less than 0.0001
                Learner = modelIndex => new BaumWelchLearning<MultivariateNormalDistribution, double[], NormalOptions>(classifier.Models[modelIndex])
                {
                    Tolerance = 0.0001,
                    Iterations = 0,

                    FittingOptions = new NormalOptions()
                    {
                        Diagonal = true,      // only diagonal covariance matrices
                        Regularization = 1e-5 // avoid non-positive definite errors
                    }
                }
            };

            // Train the sequence classifier using the algorithm
            teacher.Learn(sequences, labels);
            double logLikelihood = teacher.LogLikelihood;


            // Calculate the probability that the given
            //  sequences originated from the model
            double likelihood, likelihood2;

            int c1 = classifier.Decide(sequences[0]);
            likelihood = classifier.Probability(sequences[0]);

            // Try to classify the second sequence (output should be 1)
            int c2 = classifier.Decide(sequences[1]);
            likelihood2 = classifier.Probability(sequences[1]);


            Assert.AreEqual(0, c1);
            Assert.AreEqual(1, c2);

            Assert.AreEqual(-24.560663315259973, logLikelihood, 1e-10);
            Assert.AreEqual(0.99999999998805045, likelihood, 1e-10);
            Assert.AreEqual(0.99999999998805045, likelihood2, 1e-10);

            Assert.IsFalse(double.IsNaN(logLikelihood));
            Assert.IsFalse(double.IsNaN(likelihood));
            Assert.IsFalse(double.IsNaN(likelihood2));
        }
        public void LearnTest6()
        {
            // Create a Continuous density Hidden Markov Model Sequence Classifier
            // to detect a multivariate sequence and the same sequence backwards.
            double[][][] sequences = new double[][][]
            {
                new double[][] 
                { 
                    // This is the first  sequence with label = 0
                    new double[] { 0, 1 },
                    new double[] { 1, 2 },
                    new double[] { 2, 3 },
                    new double[] { 3, 4 },
                    new double[] { 4, 5 },
                }, 

                new double[][]
                {
                        // This is the second sequence with label = 1
                    new double[] { 4,  3 },
                    new double[] { 3,  2 },
                    new double[] { 2,  1 },
                    new double[] { 1,  0 },
                    new double[] { 0, -1 },
                }
            };

            // Labels for the sequences
            int[] labels = { 0, 1 };


            var density = new MultivariateNormalDistribution(2);

            try
            {
                new HiddenMarkovClassifier<MultivariateNormalDistribution>(
                    2, new Custom(new double[2, 2], new double[2]), density);

                Assert.Fail();
            }
            catch (ArgumentException)
            {
            }

            var topology = new Custom(
                new[,] { { 1 / 2.0, 1 / 2.0 }, { 1 / 2.0, 1 / 2.0 } },
                new[] { 1.0, 0.0 });

            Array.Clear(topology.Initial, 0, topology.Initial.Length);
            Array.Clear(topology.Transitions, 0, topology.Transitions.Length);

            // Creates a sequence classifier containing 2 hidden Markov Models with 2 states
            // and an underlying multivariate mixture of Normal distributions as density.
            var classifier = new HiddenMarkovClassifier<MultivariateNormalDistribution, double[]>(
                2, topology, density);


            // Configure the learning algorithms to train the sequence classifier
            var teacher = new HiddenMarkovClassifierLearning<MultivariateNormalDistribution, double[]>(classifier)
            {
                // Train each model until the log-likelihood changes less than 0.0001
                Learner = modelIndex => new BaumWelchLearning<MultivariateNormalDistribution, double[]>(classifier.Models[modelIndex])
                {
                    Tolerance = 0.0001,
                    Iterations = 0,

                    FittingOptions = new NormalOptions() { Diagonal = true }
                }
            };

            // Train the sequence classifier using the algorithm
            teacher.Learn(sequences, labels);
            double logLikelihood = teacher.LogLikelihood;


            // Calculate the probability that the given
            //  sequences originated from the model
            double response1, response2;

            // Try to classify the first sequence (output should be 0)
            int c1 = classifier.Decide(sequences[0]);
            response1 = classifier.Probability(sequences[0]);

            // Try to classify the second sequence (output should be 1)
            int c2 = classifier.Decide(sequences[1]);
            response2 = classifier.Probability(sequences[1]);

            Assert.AreEqual(double.NegativeInfinity, logLikelihood);
            Assert.AreEqual(0, response1);
            Assert.AreEqual(0, response2);
        }
        public void LearnTest5()
        {
            // Create a Continuous density Hidden Markov Model Sequence Classifier
            // to detect a multivariate sequence and the same sequence backwards.
            double[][][] sequences = new double[][][]
            {
                new double[][] 
                { 
                    // This is the first  sequence with label = 0
                    new double[] { 0, 1 },
                    new double[] { 1, 2 },
                    new double[] { 2, 3 },
                    new double[] { 3, 4 },
                    new double[] { 4, 5 },
                }, 

                new double[][]
                {
                        // This is the second sequence with label = 1
                    new double[] { 4,  3 },
                    new double[] { 3,  2 },
                    new double[] { 2,  1 },
                    new double[] { 1,  0 },
                    new double[] { 0, -1 },
                }
            };

            // Labels for the sequences
            int[] labels = { 0, 1 };


            var density = new MultivariateNormalDistribution(2);

            // Creates a sequence classifier containing 2 hidden Markov Models with 2 states
            // and an underlying multivariate mixture of Normal distributions as density.
            var classifier = new HiddenMarkovClassifier<MultivariateNormalDistribution, double[]>(
                2, new Ergodic(2), density);

            // Configure the learning algorithms to train the sequence classifier
            var teacher = new HiddenMarkovClassifierLearning<MultivariateNormalDistribution, double[]>(classifier)
            {
                // Train each model until the log-likelihood changes less than 0.0001
                Learner = modelIndex => new BaumWelchLearning<MultivariateNormalDistribution, double[]>(classifier.Models[modelIndex])
                {
                    Tolerance = 0.0001,
                    Iterations = 0,

                    FittingOptions = new NormalOptions() { Diagonal = true }
                }
            };

            // Train the sequence classifier using the algorithm
            teacher.Learn(sequences, labels);
            double logLikelihood = teacher.LogLikelihood;


            // Calculate the probability that the given
            //  sequences originated from the model
            double logLikelihood1, logLikelihood2;
            int c1, c2;

            // Try to classify the 1st sequence (output should be 0)
            logLikelihood1 = classifier.Probability(sequences[0], out c1);

            // Try to classify the 2nd sequence (output should be 1)
            logLikelihood2 = classifier.Probability(sequences[1], out c2);


            Assert.AreEqual(0, c1);
            Assert.AreEqual(1, c2);

            Assert.AreEqual(-24.560599651649841, logLikelihood, 1e-10);
            Assert.AreEqual(0.99999999998806466, logLikelihood1, 1e-10);
            Assert.AreEqual(0.99999999998806466, logLikelihood2, 1e-10);
        }
        public void LearnTest4()
        {
            // Create a Continuous density Hidden Markov Model Sequence Classifier
            // to detect a multivariate sequence and the same sequence backwards.
            double[][][] sequences = new double[][][]
            {
                new double[][] 
                { 
                    // This is the first  sequence with label = 0
                    new double[] { 0 },
                    new double[] { 1 },
                    new double[] { 2 },
                    new double[] { 3 },
                    new double[] { 4 },
                }, 

                new double[][]
                {
                        // This is the second sequence with label = 1
                    new double[] { 4 },
                    new double[] { 3 },
                    new double[] { 2 },
                    new double[] { 1 },
                    new double[] { 0 },
                }
            };

            // Labels for the sequences
            int[] labels = { 0, 1 };


            // Create a mixture of two 1-dimensional normal distributions (by default,
            // initialized with zero mean and unit covariance matrices).
            var density = new MultivariateMixture<MultivariateNormalDistribution>(
                new MultivariateNormalDistribution(1),
                new MultivariateNormalDistribution(1));

            // Creates a sequence classifier containing 2 hidden Markov Models with 2 states
            // and an underlying multivariate mixture of Normal distributions as density.
            var classifier = new HiddenMarkovClassifier<MultivariateMixture<MultivariateNormalDistribution>, double[]>(
                2, new Ergodic(2), density);

            // Configure the learning algorithms to train the sequence classifier
            var teacher = new HiddenMarkovClassifierLearning<MultivariateMixture<MultivariateNormalDistribution>, double[]>(classifier)
            {
                // Train each model until the log-likelihood changes less than 0.0001
                Learner = modelIndex => new BaumWelchLearning<MultivariateMixture<MultivariateNormalDistribution>, double[]>(classifier.Models[modelIndex])
                {
                    Tolerance = 0.0001,
                    Iterations = 0,
                }
            };

            // Train the sequence classifier using the algorithm
            teacher.Learn(sequences, labels);
            double logLikelihood = teacher.LogLikelihood;


            // Calculate the probability that the given
            //  sequences originated from the model
            double likelihood1, likelihood2;
            int c1, c2;

            // Try to classify the 1st sequence (output should be 0)
            likelihood1 = classifier.Probability(sequences[0], out c1);

            // Try to classify the 2nd sequence (output should be 1)
            likelihood2 = classifier.Probability(sequences[1], out c2);


            Assert.AreEqual(0, c1);
            Assert.AreEqual(1, c2);

            Assert.AreEqual(-13.271981026832933, logLikelihood, 1e-10);
            Assert.AreEqual(0.99999791320102149, likelihood1, 1e-10);
            Assert.AreEqual(0.99999791320102149, likelihood2, 1e-10);
        }