package quickml.supervised.predictiveModelOptimizer.fieldValueRecommenders;
import quickml.supervised.predictiveModelOptimizer.FieldValueRecommender;
import java.util.*;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Created by ian on 4/12/14.
*/
public class MonotonicConvergenceRecommender implements FieldValueRecommender {
private static final int MIN_VALUES_TO_TEST = 2; // Must be at least 2
private static final double DEFAULT_TOLERANCE = 0.02;
private final List<? extends Number> values;
private final double tolerance;
public MonotonicConvergenceRecommender(List<? extends Number> values) {
this(values, DEFAULT_TOLERANCE);
}
public MonotonicConvergenceRecommender(List<? extends Number> values, double tolerance) {
checkArgument(values.size() > 0, "Must include at least one value");
this.values = values;
this.tolerance = tolerance;
sortValues();
}
@Override
public List<? extends Number> getValues() {
return values;
}
@Override
public Number first() {
return values.get(0);
}
//TODO[mk] - this could do with a rethink
@Override
public boolean shouldContinue(List<Double> losses) {
if (losses.size() < MIN_VALUES_TO_TEST)
return true;
double mostRecentLoss = losses.get(losses.size() - 1);
double secondMostRecentLoss = losses.get(losses.size() - 2);
//if we have tried at least min values and we aren't within tolerance give up
return notWithinTolerance(mostRecentLoss, secondMostRecentLoss);
}
private boolean notWithinTolerance(double mostRecentLoss, double secondMostRecentLoss) {
return Math.abs(mostRecentLoss - secondMostRecentLoss) / secondMostRecentLoss > tolerance;
}
private void sortValues() {
Collections.sort(values, new Comparator<Number>() {
@Override
public int compare(Number o1, Number o2) {
return Double.compare(o1.doubleValue(), o2.doubleValue());
}
});
}
}