public class Consts {

    // GENERALS
	public static final String SPACE = " ";
    public static final String TAB = "\t";
    public static final String COMMA_NOTE = ",";
	public static final String CLASSIFICATION_SPLITTER = "-";
	public static final String COLON_SPLITTER = ":";
    public static final String PARAMS_SPLITTER = ";";
	public static final String NEW_LINE = "line.separator";
    public static final int ERROR_NUMBER = -1;

    // DUMMY
    public static final int MIN_GAP_START_DUMMY = 3;
    public static final int MIN_GAP_END_DUMMY = 3;

    // VOWEL DURATION
    public static final int MIN_VOWEL = 9;  //min training set: 45 frames
	public static final int MAX_VOWEL = 92; //max training set: 459 frames
    public static final int MIN_GAP_START = 10;
    public static final int MIN_GAP_END = 10;

    public static final double MEAN_VOWEL_LENGTH = 41.787; //pre calculated from the training set
    public static final double STD_VOWEL_LENGTH = 12.918; //pre calculated from the training set
    public static final double MAX_VOWEL_LENGTH = 38; //pre calculated from the training set

    // MODELS NUMBER OF PARAMETERS
    public static final int PA_PARAMS_SIZE = 1; //The number params 4 PA
    public static final int SVM_PARAMS_SIZE = 2; //The number params 4 SVM
    public static final int DL_PARAMS_SIZE = 2; //The number params 4 Direct Loss
    public static final int CRF_PARAMS_SIZE = 2; //The number params 4 CRF
    public static final int RL_PARAMS_SIZE = 2; //The number params 4 Ramp Loss
    public static final int PL_PARAMS_SIZE = 6; //The number params 4 Probit Loss
    public static final int SP_PARAMS_SIZE = 0; //The number params 0 Perceptron
    public static final int ORBIT_PARAMS_SIZE = 2; //The number params 4 orbit
    public static final int MULTICLASS_REGECT_SIZE = 4; //The number params 4 multiclass regect

    // KERNEL DEFAULT PARAMETERS
    public static double SIGMA = 1;

    //OCR
    public static final String END_NOTE = "$";
} public interface Reader {
    ArrayList<ArrayList<String>> readFile(String path, String spliter);
    InstancesContainer readData(String path, String dataSpliter, String valueSpliter);
} public class InstancesContainer {

    ArrayList<Example> instances;
    ArrayList<ArrayList<String>> paths;
    int size;

    //C'tor
    public InstancesContainer() {
        instances = new ArrayList<Example>();
        paths = new ArrayList<ArrayList<String>>();
        size = 0;
    }

    //get the requested example
    public Example getInstance(int index)
    {
        try{
            return instances.get(index);
        } catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    //======Setters and getters=====//
    public ArrayList<Example> getInstances() {
        return instances;
    }

    public void setInstances(ArrayList<Example> instances) {
        this.instances = instances;
        this.size = instances.size();
    }

    public ArrayList<ArrayList<String>> getPaths() {
        return paths;
    }

    public void setPaths(ArrayList<ArrayList<String>> paths) {
        this.paths = paths;
        this.size = paths.size();
    }


    public int getSize() {
        if(this.size != 0)
            return this.size;
        this.size = (instances.size() != 0) ? instances.size() : paths.size();
        return this.size;
    }
} public class Logger {

    private static final String directory = "log";
    private static final String log_file = "files.log";

    /**
     * Write the message into the standard output
     * @param message - the message to be written
     */
    public static void info(String message){
        System.out.print(System.getProperty(Consts.NEW_LINE) + message);
    }

    /**
     * Write the message into the standard output without new line at the end
     * @param message - the message to be written
     */
    public static void info_no_new_line(String message){
        System.out.print(message);
    }

    /**
     * Write the message into the standard output with the data and time attached
     * @param message - the message to be written
     */
    public static void infoTime(String message){
        DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        //get current date time with Date()
        Date date = new Date();
        if(message.isEmpty() || message.equals(""))
            message = "None,";
        System.out.print(System.getProperty(Consts.NEW_LINE) + message + " " + dateFormat.format(date));
    }

    /**
     * Writes only the data and time to the standard output
     */
    public static void time(){
        DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        //get current date time with Date()
        Date date = new Date();
        System.out.print(System.getProperty(Consts.NEW_LINE)  + dateFormat.format(date));
    }

    /**
     * Write the message into the standard output with the data and time attached
     * @param msg - the message to be written
     * @param example - a running id
     */
    public static void timeExampleStandard(String msg, int example){
        DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        //get current date time with Date()
        Date date = new Date();
        System.out.print(System.getProperty(Consts.NEW_LINE) + msg+example+", "+dateFormat.format(date));
    }

    /**
     * Write the message into the standard output as a progress bar
     * @param msg - the message to be written
     */
    public static void progressMessage(String msg){
        System.out.print("\r" + msg + " ");
    }

    /**
     * Write the message into the standard error
     * @param message - the message to be written
     */
    public static void error(String message){
        System.err.println("Error: "+message);
    }



    /**
     * Write the message into the files.log in the lod directory(if doesn't exists the program creates the dir)
     * @param message - tha message to ve written
     * @throws IOException
     */
    public static void log2File(String message) throws IOException
    {
        // create the log directory if does noe exists
        File dir = new File(directory);
        if(!dir.exists())
            dir.mkdir();

        // Create file stream
        File file = new File(directory+"/"+log_file);
        FileWriter fstream = new FileWriter(file,true);
        BufferedWriter out = new BufferedWriter(fstream);
        //writes the message
        out.write(message);
        out.write(System.getProperty(Consts.NEW_LINE));
        //close all the file descriptors
        out.close();
        fstream.close();
    }

    /**
     * clean the log directory
     */
    public static void clean(){
        File dir = new File(directory);
        if(dir.exists() && dir.isDirectory()) {
            String[]entries = dir.list();
            for(String s: entries){
                File currentFile = new File(dir.getPath(),s);
                currentFile.delete();
            }
            dir.delete();
        }

        dir.mkdir();
    }
} public class PredictedLabels extends TreeMap<String, Double>{

    //default c'tor
    public PredictedLabels(){
    }

    public PredictedLabels(MapValueComparatorAscending vc){
        super(vc);
    }

    public PredictedLabels(MapValueComparatorDescending vc){
        super(vc);
    }

    public PredictedLabels(MapKeyComparatorAscending vc){
        super(vc);
    }
} public class Vector extends HashMap<Integer, Double> implements Serializable{

    private static final long serialVersionUID = 1L;
    private int maxIndex;
    public Vector(){this.maxIndex = -1;}
    public Vector(Vector v){
        super(v);
    }

    @Override
    public Double put(Integer key, Double value){
        super.put(key, value);
        if(maxIndex < key) maxIndex = key;
        return value;
    }

    // getter
    public int getMaxIndex() {
        return maxIndex;
    }
    public void resetMaxIndex(){
        this.maxIndex = -1;
    }
} public class FeatureFunctionsSparse implements IFeatureFunctions {

    // data members
    private int maxFeatures;
    private int numOfClasses;
    private int sizeOfVector;

    /**
     * Constructor
     * @param numOfClasses - the number target of classes
     * @param maxNumFeatures - the number of features
     */
    public FeatureFunctionsSparse(int numOfClasses, int maxNumFeatures){
        this.numOfClasses = numOfClasses;
        this.maxFeatures = maxNumFeatures;
        this.sizeOfVector = this.maxFeatures*this.numOfClasses;
    }

	@Override
	public Example convert(Example vector, String label, IKernel kernel) {
        try{
            //parse the label
            int intLabel = Integer.parseInt(label);
            Example newVector = Factory.getExample(0);
            newVector.sizeOfVector = maxFeatures;
            Vector tmpVector = new Vector();

            //run the phi function
            for(Integer feature : vector.getFeatures().keySet())
                tmpVector.put(feature+intLabel*maxFeatures,vector.getFeatures().get(feature));

            if(kernel !=null)
                tmpVector = kernel.convertVector(tmpVector, vector.sizeOfVector);

            newVector.setFeatures(tmpVector);

            return newVector;

        } catch (Exception e){
            e.printStackTrace();
            return null;
        }
	}

    @Override
    public int getSizeOfVector() {
        return this.sizeOfVector;
    }
} public class StructEDModel implements Serializable{

    // data members
    private Classifier classifier;
    private Vector W;
    private double cumulative_loss = 0;
    private boolean isShuffle;

    // C'tor
    public StructEDModel(Vector init_weights, IUpdateRule updateRule, ITaskLoss taskLoss, IInference inference,
                         IKernel kernel, IFeatureFunctions phi, ArrayList<Double> args){
        classifier = new Classifier();
        classifier.classifierData = new ClassifierData();
        updateRule.init(args);
        classifier.classifierData.updateRule = updateRule;
        classifier.classifierData.taskLoss = taskLoss;
        classifier.classifierData.inference = inference;
        classifier.classifierData.kernel = kernel;
        classifier.classifierData.phi = phi;
        W = init_weights;
        this.isShuffle = true;
    }
    // override constructor to support enable/disable shuffling
    public StructEDModel(Vector init_weights, IUpdateRule updateRule, ITaskLoss taskLoss, IInference inference,
                         IKernel kernel, IFeatureFunctions phi, ArrayList<Double> args, boolean isShuffle){
        classifier = new Classifier();
        classifier.classifierData = new ClassifierData();
        updateRule.init(args);
        classifier.classifierData.updateRule = updateRule;
        classifier.classifierData.taskLoss = taskLoss;
        classifier.classifierData.inference = inference;
        classifier.classifierData.kernel = kernel;
        classifier.classifierData.phi = phi;
        W = init_weights;
        this.isShuffle = isShuffle;
    }

    /**
     * Train the model on the train instances
     * @param epoch - the number of epochs to run over the data
     * @param trainInstances - the training instances(train set)
     * @param task_loss_params - the task loss parameters, if there are no parameters set this to null
     * @param developInstances - the develop instances(dev set), set this to null if there is no validation set
     * @param isAvg - an indicator whether or not to average the weights
     * @throws Exception
     */
    public void train(InstancesContainer trainInstances, ArrayList<Double> task_loss_params, InstancesContainer developInstances, int epoch, int isAvg, boolean verbose) throws Exception {
        //#############################################################################//
        //================================TRAIN========================================//
        //#############################################################################//
        //===================================//
        //print the start time of the program//
        Logger.infoTime("Start Training:");
        Logger.time();
        Logger.info("");
        long startTime = System.currentTimeMillis();
        //===================================//

        //loop over the training set epoch time
        for(int size = 0 ; size<epoch ; size++)
        {
            //===================================//
            //print the start time of the program//
            Logger.info("");
            Logger.info("==================================");
            Logger.timeExampleStandard("Epoch: ", (size + 1));
            Logger.info("==================================");
            Logger.info("");
            //===================================//
            if(isShuffle)
                //preform random shuffle for the next epoch
                trainInstances = ModelHandler.randomShuffle(trainInstances);

            //train the algorithm
            if (classifier == null) throw new AssertionError();
            W = classifier.train(W, trainInstances, task_loss_params, developInstances, isAvg, verbose);
            if(W == null)
                return;
        }

        if(isAvg == 1)
            this.W = classifier.getAvgWeights();
        //===================================//
        //print the start time of the program//
        Logger.time();
        long endTime   = System.currentTimeMillis();
        long totalTime = endTime - startTime;
        long seconds = TimeUnit.MILLISECONDS.toSeconds(totalTime);
        Logger.info("Running time in seconds: " + seconds);
        //===================================//
    }

    /**
     * predict based on the model and return the scores of the best matches
     * @param instances - the test set
     * @param task_loss_params - the parameters for the task loss function, set to null if there are no task loss parameters
     * @param numPredictions2Return - the number of examples to return in the scores
     * @return ArrayList<PredictedLabels> - return the predicted labels as array
     */
    public ArrayList<PredictedLabels> predict(InstancesContainer instances, ArrayList<Double> task_loss_params, int numPredictions2Return, boolean verbose){
        Logger.info("");
        Logger.infoTime("Start Predicting:");
        Logger.time();
        Logger.info("");
        cumulative_loss = 0;
        ArrayList<PredictedLabels> scores = new ArrayList<PredictedLabels>();
        for(int i=0 ; i<instances.getSize() ; i++) {
            Example x = instances.getInstance(i);
            if (x == null)
                continue;
            PredictedLabels score = classifier.test(W, x, numPredictions2Return);

            String y_hat = score.firstKey();
            String y = x.getLabel();
            cumulative_loss += classifier.classifierData.taskLoss.computeTaskLoss(y, y_hat, task_loss_params);
            scores.add(score);
            if(verbose)
                Logger.info("file = "+x.path+", Y = "+y+", Y_Hat = "+y_hat);
        }
        cumulative_loss /= (double) (instances.getSize());
        Logger.info("Cumulative task loss: " + (cumulative_loss));
        Logger.info("");
        return scores;
    }

    /**
     * Plotting the error graph for the validation set
     * @param save flag which indicates whether to write the image or not
     */
    public void plotValidationError(boolean save){
        if(classifier.validationCumulativeLoss != null && classifier.validationCumulativeLoss.size() != 0) {
            Graph graph = new Graph();
            graph.drawGraph(classifier.validationCumulativeLoss, save);
        }
    }

    /**
     * save the model, notice: we save only the weight vector, the rest of the configuration should be predefined
     * @param path - the path to save the model
     * @throws IOException
     */
    public void saveModel(String path) throws IOException {
        FileOutputStream fout = new FileOutputStream(path);
        ObjectOutputStream oos = new ObjectOutputStream(fout);
        oos.writeObject(W);
        oos.close();
    }

    /**
     * load the model, notice: we load only the weight vector, the rest of the configuration should be predefined
     * @param path - the model's path
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public void loadModel(String path) throws IOException, ClassNotFoundException {
        FileInputStream fin = new FileInputStream(path);
        ObjectInputStream ois = new ObjectInputStream(fin);
        W = (Vector) ois.readObject();
        ois.close();
    }

    // Setters and Getters
    public Vector getWeights(){
        return this.W;
    }
    public double getCumulative_loss() {return cumulative_loss;}

    public void setUpdateRule(IUpdateRule updateRule){
        this.classifier.classifierData.updateRule = updateRule;
    }
    public void setTaskLoss(ITaskLoss taskLoss){
        this.classifier.classifierData.taskLoss = taskLoss;
    }
    public void setKernel(IKernel kernel){
        this.classifier.classifierData.kernel = kernel;
    }
    public void setInference(IInference inference){
        this.classifier.classifierData.inference = inference;
    }
    public void setFeatureFunctions(IFeatureFunctions phi){ this.classifier.classifierData.phi = phi; }
    public void setReShuffle(boolean isShuffle) { this.isShuffle = isShuffle; }
} public class PassiveAggressive implements IUpdateRule {
    
    //data members
    double cValue;

    @Override
    public void init(ArrayList<Double> args){
        if(args.size() != Consts.PA_PARAMS_SIZE){
            Logger.error(ErrorConstants.UPDATE_ARGUMENTS_ERROR);
            return;
        }
        //initialize the parameters
        this.cValue = args.get(0);
    }

	/**
	 * Implementation of the update rule
	 * @param currentWeights - the current weights
	 * @param example - a single example
	 * @param classifierData - all the additional data that needed such as: loss function, inference, etc.
	 * @return the new set of weights
	 */
    @Override
	//the first cell of the arguments attribute would be the C value
	public Vector update(Vector currentWeights, Example example, ClassifierData classifierData) {
		
		try{
			//get the prediction
			String prediction = classifierData.inference.predictForTrain(example, currentWeights, example.getLabel(), classifierData,1).firstKey();

            Example phiRealLabel = classifierData.phi.convert(example, example.getLabel(), classifierData.kernel);
            Example phiPrediction = classifierData.phi.convert(example, prediction, classifierData.kernel);
			
			//compute the phi difference
            Vector phiDifference = MathHelpers.subtract2Vectors(phiRealLabel.getFeatures(), phiPrediction.getFeatures());
			
			double taskLossValue = classifierData.taskLoss.computeTaskLoss(prediction, example.getLabel(), classifierData.arguments);
			double multipleVectors = MathHelpers.multipleVectors(currentWeights , phiDifference);
			double denominator = MathHelpers.multipleVectors(phiDifference, phiDifference);

			double tau = 0;
			if(denominator != 0){
				tau = (taskLossValue - multipleVectors)/denominator;
			}

			if(tau > cValue)
				tau = cValue;

			classifierData.verbose = String.format(", Tau = %.3f", tau);

            if(tau < 0) {
				Logger.error("Y: "+example.getLabel()+" ,Y_hat: "+prediction);
				Logger.error("Loss < 0!, prediction search can't get to the target label.");
			}

            Vector updateVector = MathHelpers.mulScalarWithVectors(phiDifference, tau);
            Vector result = MathHelpers.add2Vectors(currentWeights, updateVector);

			return result;

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
} public class SVM implements IUpdateRule {

    //data members
    double eta;
    double lambda;

    @Override
    public void init(ArrayList<Double> args) {
        if(args.size() != Consts.SVM_PARAMS_SIZE){
            Logger.error(ErrorConstants.UPDATE_ARGUMENTS_ERROR);
            return;
        }
        //initialize the parameters
        this.eta = args.get(0);
        this.lambda = args.get(1);
    }

    /**
     * Implementation of the update rule
     * @param currentWeights - the current weights
     * @param example - a single example
     * @param classifierData - all the additional data that needed such as: loss function, inference, etc.
     * @return the new set of weights
     */

    @Override
	//in SVM the lambda value would be in the first cell of the arguments attribute
	//the second cell of the arguments attribute would be the eta 
	public Vector update(Vector currentWeights, Example example, ClassifierData classifierData) {
		
		try{
            double algorithmIteration = classifierData.iteration;

			//get the prediction
            String prediction;

            //if there's a problem with the predict return the previous weights
            try{
			    prediction = classifierData.inference.predictForTrain(example,currentWeights,example.getLabel(),classifierData,1).firstKey();
            } catch (Exception e){
                return currentWeights;
            }

            Example phiRealLabel = classifierData.phi.convert(example,example.getLabel(), classifierData.kernel);
            Example phiPrediction = classifierData.phi.convert(example,prediction, classifierData.kernel);

			//compute the phi difference
            Vector phiDifference = MathHelpers.subtract2Vectors(phiRealLabel.getFeatures(), phiPrediction.getFeatures());
			
            double newEta = eta/Math.sqrt(algorithmIteration);

			double coefficientFirstArgument = (1-(lambda*newEta));

            Vector firstArgument = MathHelpers.mulScalarWithVectors(currentWeights, coefficientFirstArgument);
            Vector secondArgument = MathHelpers.mulScalarWithVectors(phiDifference, newEta);

            Vector result = MathHelpers.add2Vectors(firstArgument, secondArgument);

			return result;
			
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
} public class InferenceMultiClass implements IInference {

    private int numOfClass = 10; //default for MNIST

    /**
     * Constructor
     * @param numOfClasses - the number of classes
     */
    public InferenceMultiClass(int numOfClasses){
        this.numOfClass = numOfClasses;
    }

    @Override
    public PredictedLabels predictForTrain(Example vector, Vector W, String realClass, ClassifierData classifierData, double epsilonArgMax) {
        try{
            //validation
            if(vector.sizeOfVector<=0) {
                Logger.error(ErrorConstants.PHI_VECTOR_DATA);
                return null;
            }

            if(this.numOfClass > 0) {
                int maxFeatures = classifierData.phi.getSizeOfVector() / this.numOfClass;
                double[] scores = new double[this.numOfClass];
                for(Integer key : vector.getFeatures().keySet()){
                    for(int i=0 ; i<this.numOfClass ; i++){
                        Double wVal = W.get(key + i * maxFeatures);
                        if(wVal != null)
                            scores[i] += wVal * vector.getFeatures().get(key);
                    }
                }

                String maxLabel = "0";
                double maxScore = scores[0];
                if (epsilonArgMax != 0)
                    maxScore += epsilonArgMax * classifierData.taskLoss.computeTaskLoss(String.valueOf("0"), realClass, classifierData.arguments);

                for(int i=1 ; i<this.numOfClass ; i++) {
                    double tmp = scores[i];

                    if (epsilonArgMax != 0)
                        //add the task loss
                        tmp += epsilonArgMax * classifierData.taskLoss.computeTaskLoss(String.valueOf(i), realClass, classifierData.arguments);

                    if (tmp > maxScore) {
                        maxScore = tmp;
                        maxLabel = String.valueOf(i);
                    }
                }

                PredictedLabels result = new PredictedLabels();
                result.put(maxLabel, maxScore);

                return result;
            }
            return null;
        } catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public PredictedLabels predictForTest(Example vector, Vector W, String realClass, ClassifierData classifierData, int returnAll) {
        if(returnAll != Consts.ERROR_NUMBER) {
            try{
                //validation
                if(vector.sizeOfVector<=0) {
                    Logger.error(ErrorConstants.PHI_VECTOR_DATA);
                    return null;
                }

                PredictedLabels tree = new PredictedLabels();

                if(this.numOfClass > 0) {
                    int maxFeatures = classifierData.phi.getSizeOfVector() / this.numOfClass;
                    double[] scores = new double[this.numOfClass];
                    for(Integer key : vector.getFeatures().keySet()){
                        for(int i=0 ; i<this.numOfClass ; i++){
                            Double wVal = W.get(key + i * maxFeatures);
                            if(wVal != null)
                                scores[i] += wVal * vector.getFeatures().get(key);
                        }
                    }

                    for(int i=0 ; i<this.numOfClass ; i++)
                        tree.put(String.valueOf(i), scores[i]);

                    MapValueComparatorDescending vc = new MapValueComparatorDescending(tree);
                    PredictedLabels result = new PredictedLabels(vc);
                    result.putAll(tree);

                    return result;
                }
                return null;
            } catch (Exception e){
                e.printStackTrace();
                return null;
            }
        } else
            return predictForTrain(vector, W, realClass, classifierData ,0);
    }
} public class TaskLossMultiClass implements ITaskLoss {

	@Override
	public double computeTaskLoss(String predictClass, String actualClass, List<Double> params) {
		if(predictClass.equalsIgnoreCase(actualClass))
			return 0;
		return 1;
	}
} public static Reader getReader(int type){
       switch(type){
           case 0:
	        return new StandardReader();
           case 1:
               return new RankReader();
           case 2:
               return new LazyReader();
           case 3:
               return new OcrReader();
           default:
               return new StandardReader();
       }
}
import com.structed.data.entities.PredictedLabels;
import com.structed.data.entities.Vector;
import com.structed.data.featurefunctions.FeatureFunctionsSparse;
import com.structed.models.StructEDModel;
import com.structed.models.algorithms.PassiveAggressive;
import com.structed.models.algorithms.SVM;
import com.structed.models.inference.InferenceMultiClass;
import com.structed.models.loss.TaskLossMultiClass;
import java.util.ArrayList;
import static com.structed.data.Factory.getReader;
import com.structed.constants.Consts;
import com.structed.dal.Reader;
import com.structed.data.InstancesContainer;
import com.structed.data.Logger;
/*
 * The MIT License (MIT)
 *
 * StructED - Machine Learning Package for Structured Prediction
 *
 * Copyright (c) 2015 Yossi Adi, E-Mail: yossiadidrum@gmail.com
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package com.structed.tutorials;




/**
 * Created by yossiadi on 6/29/15.
 * Tutorial about the multi-class classifications using MNIST and Iris datasets
 */
public class MultiClassTutorial {
    public static void main(String[] args) throws Exception {
        // ============================ MNIST DATA ============================ //
        Logger.info("MNIST data example.");
        // === PARAMETERS === //
        String trainPath = "tutorials-code/multiclass/data/MNIST/train.txt";
        String testPath = "tutorials-code/multiclass/data/MNIST/test.data.txt";
        String valPath = "tutorials-code/multiclass/data/MNIST/val.data.txt";

        int epochNum = 1;
        int readerType = 0;
        int isAvg = 1;
        int numExamples2Display = 3;
        int numOfClasses = 10;
        int maxFeatures = 784;
        Reader reader = getReader(readerType);
        // ================== //

        // load the data
        InstancesContainer mnistTrainInstances = reader.readData(trainPath, Consts.SPACE, Consts.COLON_SPLITTER);
        InstancesContainer mnistTestInstances = reader.readData(testPath, Consts.SPACE, Consts.COLON_SPLITTER);
        InstancesContainer mnistDevelopInstances = reader.readData(valPath, Consts.SPACE, Consts.COLON_SPLITTER);
        if (mnistTrainInstances.getSize() == 0) return;

        // ======= SVM ====== //
        Vector W = new Vector() {{
            put(0, 0.0);
        }}; // init the first weight vector
        ArrayList<Double> arguments = new ArrayList<Double>() {{
            add(1.0);
        }}; // model parameters

        StructEDModel mnist_model = new StructEDModel(W, new PassiveAggressive(), new TaskLossMultiClass(),