public class MapInteger extends HashMap<String, Integer> implements Comparator<String> {

	private static final long serialVersionUID = 1L;

	@Override
	public int compare(String o1, String o2) {
		return Double.compare(get(o1), get(o2));
	}

	public Integer get(String s) {
		if (containsKey(s))
			return super.get(s);
		put(s, 0);
		return 0;
	}

	public void increment(String a) {
		put(a, get(a) + 1);
	}

	public void increment(String a, int value) {
		put(a, get(a) + value);
	}

	public List<String> ordered() {
		List<String> values = Lists.newArrayList(this.keySet());
		Collections.sort(values, this);
		return values;
	}
} public class CosineSimilarity extends WeightedAlgorithm {

	private static final long serialVersionUID = 1L;

	@Override
	public EditDistanceAlgorithm clone() {
		return defaultCopy();
	}

	@Override
	public double distance(List<Annotation> ta, List<Annotation> ha, AnnotatedText t, AnnotatedText h, String pairID) {

		List<Annotation> vector = Lists.newArrayList();

		List<Double> v1 = Lists.newArrayList();
		List<Double> v2 = Lists.newArrayList();

		for (Annotation w : ha) {
			boolean found = false;
			for (int i = 0; i < vector.size() && !found; i++) {
				Annotation w2 = vector.get(i);
				if (getMatcher().match(w2, w, t, h, pairID) == 0) {
					found = true;
					v2.set(i, v2.get(i) + getWeightCalculator().weightH(w, t, h));
					break;
				}
			}
			if (!found) {
				vector.add(w);
				v1.add(0.0);
				v2.add(getWeightCalculator().weightH(w, t, h));
			}
		}

		for (Annotation w : ta) {
			boolean found = false;
			for (int i = 0; i < vector.size() && !found; i++) {
				Annotation w2 = vector.get(i);
				if (getMatcher().match(w2, w, t, h, pairID) == 0) {
					found = true;
					v1.set(i, v1.get(i) + getWeightCalculator().weightT(w, t, h));
					break;
				}
			}
			if (!found) {
				vector.add(w);
				v1.add(getWeightCalculator().weightT(w, t, h));
				v2.add(0.0);
			}
		}
		double dotproduct = 0;

		for (int i = 0; i < vector.size(); i++)
			dotproduct += v1.get(i) * v2.get(i);

		double magnitude1 = 0.0;
		double magnitude2 = 0.0;

		for (int i = 0; i < vector.size(); i++)
			magnitude1 += v1.get(i) * v1.get(i);

		for (int i = 0; i < vector.size(); i++)
			magnitude2 += v2.get(i) * v2.get(i);

		magnitude1 = Math.sqrt(magnitude1);
		magnitude2 = Math.sqrt(magnitude2);

		double magnitude = magnitude1 * magnitude2;

		double cosine = Math.sqrt(dotproduct) / magnitude;

		double distance = 1 - cosine;

		return distance;
	}
} public class OverlapDistance extends WeightedAlgorithm {

	private static final long serialVersionUID = 1L;

	@Override
	public EditDistanceAlgorithm clone() {
		return defaultCopy();
	}

	@Override
	public double distance(List<Annotation> ta, List<Annotation> ha, AnnotatedText t, AnnotatedText h, String pairID) {
		double d = 0.0;
		double sum = 0;

		for (Annotation w2 : ha) {
			double min = Double.POSITIVE_INFINITY;
			double x = getWeightCalculator().weightH(w2, t, h);
			sum += x;
			for (Annotation w1 : ta) {
				double score = getMatcher().match(w1, w2, t, h, pairID);
				if (score == 0) {
					min = 0;
					break;
				}
				if (score < min)
					min = score;
			}
			d = d + min * x;
		}
		if (sum == 0)
			return d == 0 ? 0 : 1;
		return d / sum;
	}

} public class TokenEditDistance extends WeightedAlgorithm {

	private static final long serialVersionUID = 1L;

	private static double minimum(double a, double b, double c) {
		double mi = a < b ? a : b;
		return c < mi ? c : mi;
	}

	@Override
	public EditDistanceAlgorithm clone() {
		return defaultCopy();
	}

	private double delete(Annotation node, AnnotatedText t, AnnotatedText h) {
		return getWeightCalculator().weightT(node, t, h);
	}

	@Override
	public double distance(List<Annotation> ta, List<Annotation> ha, AnnotatedText t, AnnotatedText h, String pairID) {

		double[][] d = new double[ta.size() + 1][ha.size() + 1];
		d[0][0] = 0.0;

		for (int i = 1; i < ta.size() + 1; i++) {
			d[i][0] = d[i - 1][0] + delete(ta.get(i - 1), t, h);
		}

		for (int j = 1; j < ha.size() + 1; j++)
			d[0][j] = d[0][j - 1] + insert(ha.get(j - 1), t, h);

		for (int i = 1; i < ta.size() + 1; i++) {
			for (int j = 1; j < ha.size() + 1; j++) {
				double a = d[i - 1][j] + delete(ta.get(i - 1), t, h);
				double b = d[i][j - 1] + insert(ha.get(j - 1), t, h);
				double c = d[i - 1][j - 1] + substitute(ta.get(i - 1), ha.get(j - 1), t, h, pairID);
				d[i][j] = minimum(a, b, c);
			}
		}
		double norm = d[d.length - 1][0] + d[0][d[0].length - 1];

		double dist = d[d.length - 1][d[0].length - 1];

		return init(dist, norm);
	}

	private double insert(Annotation node, AnnotatedText t, AnnotatedText h) {
		return getWeightCalculator().weightH(node, t, h);
	}

	private double substitute(Annotation node1, Annotation node2, AnnotatedText t, AnnotatedText h, String pairID) {
		return getMatcher().match(node1, node2, t, h, pairID) * (insert(node2, t, h) + delete(node1, t, h));
	}
} @Data
@XmlRootElement(name = "result")
@XmlAccessorType(XmlAccessType.FIELD)
public class EvaluationResult implements Comparator<EvaluationResult>, Serializable {

	private static final long serialVersionUID = 1L;
	@XmlElement
	private String assigned;
	@XmlElement
	private String benchmark;
	@XmlElement
	private double confidence;
	@XmlElement
	private String id;
	@XmlElement
	private double score;

	public EvaluationResult() {

	}

	public EvaluationResult(String id, String assigned, String benchmark, double score, double confidence) {

		this.id = id;
		this.assigned = assigned;
		this.benchmark = benchmark;
		this.score = score;
		this.confidence = confidence;
	}

	@Override
	public int compare(EvaluationResult o1, EvaluationResult o2) {
		return o1.getId().compareTo(o2.getId());
	}
} public class EvaluationStatistics implements Serializable {

	private static final long serialVersionUID = 1L;

	private static String clean(Double number) {
		return clean(number, 4);
	}

	private static String clean(Double number, int i) {
		if (number == null)
			return "";
		if (i == 0)
			return "" + Math.rint(number);
		double x = 1;
		for (int k = 0; k < i; k++)
			x = x * 10;
		return "" + Math.round(number * x) / x;
	}

	private static String symbols(int x, String string) {
		StringBuilder b = new StringBuilder();
		for (int i = 0; i < x; i++)
			b.append(string);
		return b.toString();
	}

	private static String tableToString(String[][] table) {

		Map<Integer, Integer> widths = new HashMap<Integer, Integer>();

		for (int i = 0; i < table.length; i++) {
			int max = 0;
			for (int j = 0; j < table[i].length; j++) {
				if (table[i][j].length() > max)
					max = table[i][j].length();
			}
			widths.put(i, max);
		}

		StringBuilder b = new StringBuilder();

		for (int j = 0; j < table[0].length; j++) {
			for (int i = 0; i < table.length; i++) {
				String s = table[i][j];
				int mw = widths.get(i);
				b.append(s);
				b.append(symbols(mw - s.length(), " "));
				if (i != table.length - 1)
					b.append("    ");
			}
			b.append("\n");
		}
		return b.toString();
	}

	private double accuracy;

	@Getter
	private List<String> classes;

	@Getter
	private int correct;
	private final MapInteger examples;

	private Map<String, Double> fmeasure;
	@Getter
	@Setter
	private String id;

	private final Map<String, MapInteger> omap;

	@Getter
	private final List<String[]> outputs;

	private Map<String, Double> precision;

	private Map<String, Double> recall;

	@Getter
	private final Map<String, EvaluationStatistics> subClasses;

	private int wrong;

	public EvaluationStatistics() {
		outputs = Lists.newArrayList();
		subClasses = Maps.newHashMap();
		omap = Maps.newHashMap();
		examples = new MapInteger();
	}

	public double accuracy() {
		return accuracy;
	}

	public void add(EvaluationStatistics statistics) {
		subClasses.put(statistics.getId(), statistics);
		outputs.addAll(statistics.getOutputs());
	}

	public void add(Map<String, MapInteger> map, String key, String key2) {
		MapInteger d = map.get(key);
		if (d == null) {
			d = new MapInteger();
			map.put(key, d);
		}
		d.increment(key2);
	}

	public synchronized void add(String benchmark, String assigned) {
		outputs.add(new String[] { benchmark, assigned });
	}

	public int count(String orig, String assigned) {
		return omap.get(orig).get(assigned);
	}

	public int examples(String cls) {
		return examples.get(cls);
	}

	public String exportToString() {
		StringBuilder b = new StringBuilder();
		for (String[] s : outputs) {
			b.append(s[0] + "\t" + s[1] + "\n");
		}
		return b.toString();
	}

	public double fmeasure(String t) {
		return fmeasure.get(t);
	}

	public void calculate() {
		Map<String, MapInteger> amap = Maps.newHashMap();
		for (String key[] : outputs) {
			String orig = key[0];
			examples.increment(orig);
			String assigned = key[1];
			add(omap, orig, assigned);
			add(amap, assigned, orig);
			if (!examples.containsKey(assigned))
				examples.put(assigned, 0);
		}

		classes = Lists.newArrayList(examples.keySet());
		Collections.sort(classes);

		for (String ex : examples.keySet()) {
			MapInteger i = omap.get(ex);
			if (i == null) {
				i = new MapInteger();
				omap.put(ex, i);
			}
			for (String esx : examples.keySet())
				if (!i.containsKey(esx))
					i.put(esx, 0);
			i = amap.get(ex);
			if (i == null) {
				i = new MapInteger();
				amap.put(ex, i);
			}
			for (String esx : examples.keySet())
				if (!i.containsKey(esx))
					i.put(esx, 0);
		}

		correct = 0;
		wrong = 0;
		for (String key : omap.keySet()) {
			for (String key2 : omap.get(key).keySet()) {
				if (key.equals(key2))
					correct += omap.get(key).get(key2);
				else
					wrong += omap.get(key).get(key2);
			}
		}
		fmeasure = Maps.newHashMap();
		precision = Maps.newHashMap();
		recall = Maps.newHashMap();
		accuracy = (double) correct / (double) (correct + wrong);

		for (String key : omap.keySet()) {
			double tp = omap.get(key).get(key);
			double fp = 0.0;
			if (amap.containsKey(key)) {
				for (String key2 : amap.get(key).keySet()) {
					if (!key2.equals(key))
						fp += amap.get(key).get(key2);
				}
			}
			double prec = 0;
			if (tp + fp > 0)
				prec = tp / (tp + fp);
			double fn = 0.0;
			for (String key2 : omap.get(key).keySet()) {
				if (!key2.equals(key))
					fn += omap.get(key).get(key2);
			}
			double rec = 0;
			if (tp + fn > 0)
				rec = tp / (tp + fn);
			double fme = 0;
			if (prec + rec > 0)
				fme = prec * rec * 2 / (prec + rec);
			precision.put(key, prec);
			recall.put(key, rec);
			fmeasure.put(key, fme);
		}
	}

	public double precision(String t) {
		return precision.get(t);
	}

	public double recall(String t) {
		return recall.get(t);
	}

	public EvaluationStatistics subclass(String name) {
		return subClasses.get(name);
	}

	@Override
	public String toString() {
		StringBuilder buff = new StringBuilder();
		int correct = getCorrect();
		int wrong = wrong();
		String[][] sv = new String[3][4];
		sv[0][0] = "Correctly Classified Examples";
		sv[1][0] = "" + correct;
		sv[2][0] = clean((double) correct / (double) (correct + wrong) * 100, 2) + "%";
		sv[0][1] = "Incorrectly Classified Examples";
		sv[1][1] = "" + wrong;
		sv[2][1] = clean((double) wrong / (double) (correct + wrong) * 100, 2) + "%";
		sv[0][2] = "";
		sv[1][2] = "";
		sv[2][2] = "";
		sv[0][3] = "Accuracy";
		sv[1][3] = "";
		sv[2][3] = clean(accuracy());
		buff.append(tableToString(sv) + "\n");
		buff.append("=== Detailed Performace Per Relation ===\n\n");
		sv = new String[5][getClasses().size() + 1];
		sv[0][0] = "Examples";
		sv[1][0] = "Precision";
		sv[2][0] = "Recall";
		sv[3][0] = "FMeasure";
		sv[4][0] = "Class";

		int z = 1;

		for (String cls : getClasses()) {
			sv[0][z] = "    " + examples(cls);
			sv[1][z] = clean(precision(cls));
			sv[2][z] = clean(recall(cls));
			sv[3][z] = clean(fmeasure(cls));
			sv[4][z] = cls.toString();
			z++;
		}

		buff.append(tableToString(sv) + "\n");

		buff.append("=== Confusion Matrix ===" + "\n\n");

		sv = new String[getClasses().size() + 1][getClasses().size() + 1];

		for (int i = 0; i < getClasses().size(); i++)
			sv[i][0] = getClasses().get(i);

		sv[getClasses().size()][0] = "<-- classified as";

		z = 1;
		for (String cls : getClasses()) {
			for (int i = 0; i < getClasses().size(); i++)
				sv[i][z] = "" + count(cls, getClasses().get(i));

			sv[getClasses().size()][z] = cls;
			z++;
		}

		buff.append(tableToString(sv));

		for (String key : getSubClasses().keySet()) {
			buff.append("=== " + key + " ===\n");
			buff.append(getSubClasses().get(key) + "\n");
		}

		return buff.toString();
	}

	public double value(OptimizationGoal goal) {
		if (Target.ACCURACY.equals(goal.getTarget()))
			return accuracy();
		if (Target.FMEASURE.equals(goal.getTarget()))
			return recall(goal.getRelation());
		if (Target.PRECISION.equals(goal.getTarget()))
			return precision(goal.getRelation());
		return fmeasure(goal.getRelation());
	}

	public int wrong() {
		return wrong;
	}
} public class ThreadExecutor extends EditsThread<AnnotatedEntailmentPair> {

	private final EntailmentEngine engine;
	private EvaluationStatistics stats;
	private Target<EvaluationResult> target;

	private final boolean training;

	public ThreadExecutor(EntailmentEngine engine_, boolean training_) {
		engine = engine_;
		training = training_;
		if (!training)
			stats = new EvaluationStatistics();
	}

	public ThreadExecutor(EntailmentEngine engine_, Target<EvaluationResult> target_) {
		engine = engine_;
		training = false;
		target = target_;
		stats = new EvaluationStatistics();
	}

	@Override
	public void process(AnnotatedEntailmentPair object) throws Exception {
		if (training)
			((ThreadTrainedEngine) engine).handle(object);
		else {
			EvaluationResult res = engine.evaluate(object);
			if (res.getBenchmark() == null)
				res.setBenchmark("UNKNOWN");
			stats.add(res.getBenchmark(), res.getAssigned());
			if (target != null)
				target.handle(engine.evaluate(object));
		}
	}

	public void run(List<AnnotatedEntailmentPair> pairs) {
		for (int i = 0; i < pairs.size(); i++)
			pairs.get(i).setOrder(i);
		start(pairs.iterator());
	}

	public EvaluationStatistics statistics() {
		stats.calculate();
		return stats;
	}
} public abstract class ThreadTrainedEngine extends EntailmentEngine {

	private static final long serialVersionUID = 1L;

	public abstract void handle(AnnotatedEntailmentPair p);

} @ToString
@EqualsAndHashCode(callSuper = false)
public class DistanceFeature extends Feature {

	private static final long serialVersionUID = 1L;

	@Getter
	private EditDistanceAlgorithm algorithm;
	private String name;

	@Getter
	@Setter
	private boolean reverseDistance;

	public DistanceFeature(EditDistanceAlgorithm algorithm2) {
		reverseDistance = false;
		algorithm = algorithm2;
		name = algorithm.getClass().getName();
	}

	@Override
	public Feature clone() {
		return new DistanceFeature(algorithm.clone());
	}

	@Override
	public double doubleValue(AnnotatedEntailmentPair pair) {
		double scoreFinal = -1;
		for (Object text : pair.getT()) {
			for (Object hypothesis : pair.getH()) {
				double score = -1;
				if (reverseDistance)
					score = algorithm.distance((AnnotatedText) hypothesis, (AnnotatedText) text, pair.getId());
				else
					score = algorithm.distance((AnnotatedText) text, (AnnotatedText) hypothesis, pair.getId());
				if (scoreFinal == -1 || score < scoreFinal) {
					scoreFinal = score;
				}
			}
		}
		return scoreFinal;
	}

	@Override
	public void init(List<AnnotatedEntailmentPair> training, OptimizationGoal goal) throws Exception {
	}

	@Override
	public boolean isNumeric() {
		return true;
	}

	@Override
	public String name() {
		return name;
	}

	public void setAlgorithm(EditDistanceAlgorithm algorithm) {
		this.algorithm = algorithm;
		name = algorithm.getClass().getName();
	}
} public abstract class Feature implements Serializable, Cloneable {

	private static final long serialVersionUID = 1L;

	@Override
	public abstract Feature clone();

	public double doubleValue(AnnotatedEntailmentPair pair) {
		throw new UnsupportedOperationException("@Override this method for  numeric features.");
	}

	public void init(List<AnnotatedEntailmentPair> training, OptimizationGoal goal) throws Exception {

	}

	public abstract boolean isNumeric();

	public abstract String name();

	public String stringValue(AnnotatedEntailmentPair pair) {
		throw new UnsupportedOperationException("@Override this method for non numeric features.");
	}

	public List<String> values() {
		throw new UnsupportedOperationException("@Override this method for non numeric features.");
	}

} public class NegationFeature extends Feature {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private final Set<String> negativeWords;

	public NegationFeature() {
		negativeWords = Sets.newHashSet();
		negativeWords.add("not");
		negativeWords.add("never");
		negativeWords.add("no");
	}

	@Override
	public Feature clone() {
		return new NegationFeature();
	}

	@Override
	public double doubleValue(AnnotatedEntailmentPair pair) {

		int hasNagationinH = 0;
		int hasNagationinT = 0;

		for (AnnotatedText a : pair.getH()) {
			for (Annotation h : a.getAnnotation()) {
				if (negativeWords.contains(h.getLemma()))
					hasNagationinH++;
			}
		}
		for (AnnotatedText a : pair.getT()) {
			for (Annotation t : a.getAnnotation()) {
				if (negativeWords.contains(t.getLemma()))
					hasNagationinT++;
			}
		}
		return hasNagationinH - hasNagationinT;
	}

	@Override
	public boolean isNumeric() {
		return true;
	}

	@Override
	public String name() {
		return "negetion";
	}

} @Data
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "AnnotatedEntailmentPair", propOrder = { "t", "h" })
public class AnnotatedEntailmentPair {

	public static AnnotatedEntailmentPair create(String t2, String h2, EditsTextAnnotator annotator) throws Exception {
		AnnotatedEntailmentPair px = new AnnotatedEntailmentPair();
		AnnotatedText at = new AnnotatedText();
		at.getAnnotation().addAll(annotator.annotate(t2));
		px.getT().add(at);
		at = new AnnotatedText();
		at.getAnnotation().addAll(annotator.annotate(h2));
		px.getH().add(at);
		return px;
	}

	@XmlTransient
	private Map<String, String> attributes;
	@XmlAttribute
	protected String entailment;
	@XmlElement
	protected List<AnnotatedText> h;
	@XmlAttribute
	protected String id;

	@XmlTransient
	private int order;

	@XmlElement
	protected List<AnnotatedText> t;

	@XmlAttribute
	protected String task;

	public AnnotatedEntailmentPair() {
		t = Lists.newArrayList();
		h = Lists.newArrayList();
		attributes = Maps.newHashMap();
	}

} @Data
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "EntailmentPair", propOrder = { "t", "h" })
public class EntailmentPair {

	@XmlAttribute
	protected String entailment;

	@XmlElement
	protected List<String> h;
	@XmlAttribute
	protected String id;
	@XmlElement
	protected List<String> t;

	@XmlAttribute
	protected String task;

	public EntailmentPair() {
		t = Lists.newArrayList();
		h = Lists.newArrayList();
	}

}
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.edits.MapInteger;
import org.edits.distance.algorithm.CosineSimilarity;
import org.edits.distance.algorithm.OverlapDistance;
import org.edits.distance.algorithm.TokenEditDistance;
import org.edits.engines.EvaluationResult;
import org.edits.engines.EvaluationStatistics;
import org.edits.engines.thread.ThreadExecutor;
import org.edits.engines.thread.ThreadTrainedEngine;
import org.edits.engines.weka.features.DistanceFeature;
import org.edits.engines.weka.features.Feature;
import org.edits.engines.weka.features.NegationFeature;
import org.edits.etaf.AnnotatedEntailmentPair;
import org.edits.etaf.EntailmentPair;
import weka.classifiers.AbstractClassifier;
import weka.classifiers.bayes.NaiveBayes;
import weka.classifiers.functions.SMO;
import weka.classifiers.meta.ThresholdSelector;
import weka.core.Attribute;
import weka.core.DenseInstance;
import weka.core.Instance;
import weka.core.Instances;
import com.google.common.collect.Lists;
/**
 * Edits - Edit Distance Textual Entailment Suite Copyright (C) 2011 Milen
 * Kouylekov This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 3.0 of the License,
 * or (at your option) any later version. This library is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 * the GNU Lesser General Public License for more details. You should have
 * received a copy of the GNU Lesser General Public License along with this
 * library; if not, write to the Free Software Foundation, Inc., 51 Franklin
 * Street, Fifth Floor, Boston, MA 02110-1301 USA
 */
package org.edits.engines.weka;






@Data
@EqualsAndHashCode(callSuper = false)
public class WekaEntailmentEngine extends ThreadTrainedEngine {

	private static final long serialVersionUID = 1L;

	private Instance[] calculatedInstances;

	private List<String> categories;
	private AbstractClassifier classifier;
	private List<Feature> features;
	private Instances instances;
	public Iterator<EntailmentPair> processor;

	public WekaEntailmentEngine() {
		features = Lists.newArrayList();
		features.add(new NegationFeature());
		features.add(new DistanceFeature(new TokenEditDistance()));