public boolean train(TimeSeriesClassificationData trainingData) {

        clear();

        if (trainingData.getNumSamples() == 0) {
            System.err.println("train_(TimeSeriesClassificationData &trainingData) - There are no training samples to train the HMM classifer!");
            return false;
        }

        if (trainingData.getNumDimensions() != 1) {
            System.err.println("train_(TimeSeriesClassificationData &trainingData) - The number of dimensions in the training data must be 1. If your training data is not 1 dimensional then you must quantize the training data using one of the GRT quantization algorithms");
            return false;
        }

        //Reset the HMM
        numInputDimensions = trainingData.getNumDimensions();
        numClasses = trainingData.getNumClasses();
        models.ensureCapacity(numClasses);
        classLabels = new int[numClasses];

        //Init the models
        for (int k = 0; k < numClasses; k++) {
            models.add(k, new HiddenMarkovModel());
            models.get(k).resetModel(numStates, numSymbols, modelType, delta);
            models.get(k).maxNumIter = maxNumIter;
            models.get(k).minImprovement = minImprovement;
        }

        //Train each of the models
        for (int k = 0; k < numClasses; k++) {
            //Get the class ID of this gesture
            int classID = trainingData.getClassTracker().get(k).classLabel;
            classLabels[k] = classID;

            //Convert this classes training data into a list of observation sequences
            TimeSeriesClassificationData classData = trainingData.getClassData(classID);
            int[][] observationSequences = null;
            if ((observationSequences = convertDataToObservationSequence(classData)) == null) {
                return false;
            }

            //Train the model
            if (!models.get(k).train(observationSequences)) {
                System.err.println("train_(TimeSeriesClassificationData &trainingData) - Failed to train HMM for class " + classID);
                return false;
            }
        }

        //Compute the rejection thresholds
        nullRejectionThresholds = new double[numClasses];

        for (int k = 0; k < numClasses; k++) {
            //Get the class ID of this gesture
            int classID = trainingData.getClassTracker().get(k).classLabel;
            classLabels[k] = classID;

            //Convert this classes training data into a list of observation sequences
            TimeSeriesClassificationData classData = trainingData.getClassData(classID);
            int[][] observationSequences = null;
            if ((observationSequences = convertDataToObservationSequence(classData)) == null) {
                return false;
            }

            //Test the model
            double loglikelihood = 0;
            double avgLoglikelihood = 0;
            for (int i = 0; i < observationSequences.length; i++) {
                loglikelihood = models.get(k).predict(observationSequences[i]);
                avgLoglikelihood += Math.abs(loglikelihood);
            }
            nullRejectionThresholds[k] = -(avgLoglikelihood / (double) observationSequences.length);
        }

        //Flag that the model has been trained
        trained = true;

        return true;
    }