public interface IUpdateRule {
	//the arguments would be different from update to update
	Vector update(Vector currentWeights, Example example, ClassifierData classifierData);
	void init(ArrayList<Double> args);
} public interface IInference {
    PredictedLabels predictForTrain(Example vector, Vector W, String realClass, ClassifierData classifierData, double epsilonArgMax);
    PredictedLabels predictForTest(Example vector, Vector W, String realClass, ClassifierData classifierData, int returnAll);
} public interface IKernel {
    Vector convertVector(Vector vector, int vectorSize);
} public interface ITaskLoss {
	double computeTaskLoss(String predictClass, String actualClass, List<Double> params);
} public abstract class Example {

    public String path;
    private String label;
    private int fold;
    public int sizeOfVector;

	//C'tor
	public Example() {
        label = "";
        sizeOfVector = 0;
	}

	//===========GETTERS AND SETTERS=========//
	public Vector getFeatures(){return null;}
	public void setFeatures(Vector features){}

    public ArrayList<Vector> getFeatures2D(){return null;}
    public void setFeatures2D(ArrayList<Vector> features){}
    public ArrayList<Integer> getLabels2D(){return null;}
    public void setLabels2D(ArrayList<Integer> rankLabels){}

    public String getLabel() {
        return label;
    }
    public void setLabel(String label) {
        this.label = label;
    }

    public int getFold() { return fold; }
    public void setFold(int fold) { this.fold = fold; }
} 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 interface IFeatureFunctions {
	Example convert(Example vector, String label, IKernel kernel);
	int getSizeOfVector();
} 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 ModelHandler {

    /**
     * Set weights for a model from a given file
     * This function either sets the new vector to be zeros or reads the data from a given path
     * @param type - if this parameter is set to be 0 than the returned vector will be zeros, if 1 than it will populate the vector with the file's data
     * @param path - the path to the file that contains the vecotr's data
     * @return the new vector
     */
    public static Vector setWeights(int type, String path) {

        Vector W = new Vector();
        try{
            Reader reader = Factory.getReader(0);
            if(type == 0)
                W.put(0, 0.0);
            else if(type == 1){
                ArrayList<ArrayList<String>> wData = reader.readFile(path, Consts.SPACE);
                W = convert2Weights(wData);
            }
        } catch (Exception e){
            e.printStackTrace();
            return null;
        }
        return W;
    }

    /**
     * Converts an array list of strings into a vector of double
     * this function can be used after reading a vector from a file
     * @param data the data 2D array but we treat tim as a 1D array
     * @return a vector that contains the model weights
     * @throws ParseException
     */
    public static Vector convert2Weights(ArrayList<ArrayList<String>> data) throws ParseException
    {
        Vector result = new Vector();
        for(int i=0 ; i<data.size() ; i++)
        {
            for(int j=0 ; j<data.get(i).size() ; j++){
                String values[] = data.get(i).get(j).split(Consts.COLON_SPLITTER);
                result.put(Integer.parseInt(values[0]), Double.parseDouble(values[1]));
            }
        }
        return result;
    }

    /**
     * preforming random shuffle on the data instances
     * @param instances
     * @return
     */
    public static InstancesContainer randomShuffle(InstancesContainer instances)
    {
        Random rnd = new Random();
        rnd.setSeed(1234);

        // random shuffle the paths
        if(instances.getPaths().size()!=0) {
            for (int i = instances.getPaths().size() - 1; i > 0; i--) {
                int index = rnd.nextInt(i + 1);
                // Simple swap
                ArrayList<String> tmp = instances.getPaths().get(index);
                instances.getPaths().set(index, instances.getPaths().get(i));
                instances.getPaths().set(i, tmp);
            }
        }

        // random shuffle the instances
        for (int i = instances.getInstances().size()-1 ; i>0 ; i--)
        {
            int index = rnd.nextInt(i + 1);
            // Simple swap
            Example tmp = instances.getInstances().get(index);
            instances.getInstances().set(index, instances.getInstances().get(i));
            instances.getInstances().set(i, tmp);
        }
        return instances;
    }

    public static HashMap<Integer,Double> printMul(Vector v1, Vector v2)
    {
        HashMap<Integer,Double> result = new HashMap<Integer,Double>();
        for(Map.Entry<Integer,Double> entry : v1.entrySet()){
            if(v2.containsKey(entry.getKey())){
                result.put(entry.getKey(), entry.getValue()*v2.get(entry.getKey()));
            }
        }

        return result;
    }

    public static TreeMap<Integer,Double> sortMul(Vector v1, Vector v2)
    {
        TreeMap<Integer,Double> result = new TreeMap<Integer,Double>();
        for(Map.Entry<Integer,Double> entry : v1.entrySet()){
            if(v2.containsKey(entry.getKey())){
                result.put(entry.getKey(), entry.getValue()*v2.get(entry.getKey()));
            }
        }

        return result;
    }
} public class Graph {

    final String TITLE = "Validation Error";
    final String Y_TITLE = "X";
    final String X_TITLE = "Y";
    final String SERIES_NAME = "y(x)";
    final String IMG_PATH = "img/validation_error";
    final String HIT_MAP_PATH = "img/heat_map.png";
    final String IMG_DIR = "img/";


    /**
     * Draw a graph of the loss values as a functions of the iterations
     * @param scores an array list of the loss values
     * @param save a flag indicates whether or not to save the img
     */
    public void drawGraph(ArrayList<Double> scores, boolean save) {
        try {
            // setting the data fit for the plotting
            double[] xData = new double[scores.size()];
            double[] yData = new double[scores.size()];

            for(int i=0 ; i<scores.size() ; i++){
                yData[i] = scores.get(i);
                xData[i] = i+1;
            }

            // Create Chart
            Chart chart = QuickChart.getChart(TITLE, X_TITLE, Y_TITLE, SERIES_NAME, xData, yData);
            // Show it
            new SwingWrapper(chart).displayChart();
            if (save) {
                // or save it in high-res
                Logger.info("=========================");
                Logger.info("Saving image: " + IMG_PATH);
                Logger.info("=========================");

                File imgDir = new File(IMG_DIR);
                // if the directory does not exist, create it
                if (!imgDir.exists()) {
                    Logger.info("creating directory: img/");
                    boolean result = false;
                    try{
                        result = imgDir.mkdir();
                    }
                    catch(SecurityException se){
                        Logger.error("There is no permission to write the error image.");
                    }
                    if(result) {
                        Logger.info("DIR created");
                        BitmapEncoder.saveBitmapWithDPI(chart, IMG_PATH, BitmapEncoder.BitmapFormat.PNG.PNG, 300);
                    }
                }
                BitmapEncoder.saveBitmapWithDPI(chart, IMG_PATH, BitmapEncoder.BitmapFormat.PNG.PNG, 300);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }


    /**
     * Draw heat map - used in the OCR tutorial
     * This function saves the heat map under: img/heat_map.png
     * @param model - the trained model
     * @param start - the index from where to start and build the heat map
     * @param jumps - the row size of the matrix
     * @throws IOException
     */
    public void drawHeatMap(StructEDModel model, int start, int jumps) throws IOException {

        Vector w = model.getWeights();
        double[][] data = new double[jumps][jumps];
        Character[] eng_chars = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
                's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};

        for(int i = 0; i<jumps ; i++){
            for(int j = 0; j<jumps ; j++){
                data[i][j] = w.containsKey(start + i*jumps + j) ? w.get(start + i*jumps + j) : 0.0;
            }
        }
        
        HeatChart map = new HeatChart(data);

        map.setTitle("Characters Transition Parameters");
        map.setXValues(eng_chars);
        map.setYValues(eng_chars);
        map.getChartImage();

        File imgDir = new File(IMG_DIR);
        boolean result = true;
        // if the directory does not exist, create it
        if (!imgDir.exists()) {
            Logger.info("creating directory: img/");
            try {
                result = imgDir.mkdir();
            } catch (SecurityException se) {
                Logger.error("There is no permission to write the error image.");
            }
        }
        if (result)
            map.saveToFile(new File(HIT_MAP_PATH));
    }
}
import com.structed.data.entities.Example;
import com.structed.data.entities.PredictedLabels;
import com.structed.data.entities.Vector;
import com.structed.data.featurefunctions.IFeatureFunctions;
import com.structed.data.InstancesContainer;
import com.structed.data.Logger;
import com.structed.utils.ModelHandler;
import com.structed.view.Graph;
import java.io.*;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import com.structed.models.algorithms.IUpdateRule;
import com.structed.models.inference.IInference;
import com.structed.models.kernels.IKernel;
import com.structed.models.loss.ITaskLoss;
/*
 * 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.models;



/**
 * Created by yossiadi on 6/28/15.
 * The basic model class.
 */

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){