/// <summary>
        /// Runs the training algorithm on multiple batches of instances.
        /// </summary>
        /// <param name="instanceSource">The source of instances.</param>
        /// <param name="labelSource">An optional source of labels.</param>
        private void TrainOnMultipleBatches(TInstanceSource instanceSource, TLabelSource labelSource)
        {
            double logEvidence = 0.0;

            // Retrieve data properties through the mapping
            bool isSparse     = this.Mapping.IsSparse(instanceSource);
            int  featureCount = this.Mapping.GetFeatureCountSafe(instanceSource);

            // Create the inference algorithms, if required
            if (!this.IsTrained)
            {
                this.InferenceAlgorithms = this.CreateInferenceAlgorithms(isSparse, featureCount);
            }

            // Set the number of training data batches and reset per-batch constraint distributions
            this.InferenceAlgorithms.SetBatchCount(this.Settings.Training.BatchCount);

            for (int iteration = 1; iteration <= this.Settings.Training.IterationCount; iteration++)
            {
                for (int batch = 0; batch < this.Settings.Training.BatchCount; batch++)
                {
                    // Retrieve the feature values, indexes and labels through the mapping
                    double[][] featureValues  = this.Mapping.GetAllFeatureVectorValuesSafe(instanceSource, batch);
                    int[][]    featureIndexes = this.Mapping.GetAllFeatureVectorIndexesSafe(instanceSource, batch);
                    TLabel[]   labels         = this.Mapping.GetAllLabelsSafe(instanceSource, labelSource, batch);

                    // Check training data is self-consistent
                    this.CheckDataConsistency(isSparse, featureCount, featureValues, featureIndexes, labels);

                    // Check features are consistent with the inference algorithms
                    this.CheckFeatureAlgorithmConsistency(isSparse, featureCount);

                    // Run the training algorithm on the specified batch
                    int    batchedIterationCount = iteration < MaxBatchedIterationCount ? iteration : MaxBatchedIterationCount;
                    double logBatchEvidence      = this.InferenceAlgorithms.Train(
                        featureValues, featureIndexes, labels, batchedIterationCount, batch);

                    // Last iteration: compute evidence on the specified batch
                    if (iteration == this.Settings.Training.IterationCount)
                    {
                        logEvidence += logBatchEvidence;
                    }
                }

                // Raise IterationChanged event
                this.OnBatchedIterationChanged(iteration);
            }

            this.logModelEvidence += logEvidence;

            // Mark classifier as trained
            if (this.IsTrained)
            {
                this.isIncrementallyTrained = true;
            }

            this.IsTrained = true;
        }
        /// <summary>
        /// Runs the training algorithm on a single batch of instances.
        /// </summary>
        /// <param name="instanceSource">The source of instances.</param>
        /// <param name="labelSource">An optional source of labels.</param>
        private void TrainOnSingleBatch(TInstanceSource instanceSource, TLabelSource labelSource)
        {
            // Retrieve the data through the mapping
            bool isSparse     = this.Mapping.IsSparse(instanceSource);
            int  featureCount = this.Mapping.GetFeatureCountSafe(instanceSource);

            double[][] featureValues  = this.Mapping.GetAllFeatureVectorValuesSafe(instanceSource);
            int[][]    featureIndexes = this.Mapping.GetAllFeatureVectorIndexesSafe(instanceSource);
            TLabel[]   labels         = this.Mapping.GetAllLabelsSafe(instanceSource, labelSource);

            // Check training data is self-consistent
            this.CheckDataConsistency(isSparse, featureCount, featureValues, featureIndexes, labels);

            // Create the inference algorithms, if required
            if (!this.IsTrained)
            {
                this.InferenceAlgorithms = this.CreateInferenceAlgorithms(isSparse, featureCount);
            }

            // Set the number of training data batches and reset per-batch output messages
            this.InferenceAlgorithms.SetBatchCount(this.Settings.Training.BatchCount);

            // Check features are consistent with the inference algorithms
            this.CheckFeatureAlgorithmConsistency(isSparse, featureCount);

            // Subscribe to the IterationChanged event on the training algorithm
            bool subscribed = this.TrySubscribeToIterationChangedEvent();

            // Run the training algorithm
            this.logModelEvidence = this.InferenceAlgorithms.Train(
                featureValues, featureIndexes, labels, this.Settings.Training.IterationCount);

            // Unsubscribe from IterationChanged event
            this.UnsubscribeFromIterationChangedEvent(subscribed);

            // Mark classifier as trained
            if (this.IsTrained)
            {
                this.isIncrementallyTrained = true;
            }

            this.IsTrained = true;
        }