package bsearch.evaluation; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import org.nlogo.util.MersenneTwisterFast; import bsearch.app.BehaviorSearchException; import bsearch.app.SearchProtocol; import bsearch.nlogolink.ModelRunResult; import bsearch.representations.Chromosome; import bsearch.representations.DummyChromosome; /** * This class is a work-in-progress. Currently unused... */ public strictfp class DerivativeFitnessFunction implements FitnessFunction { private final SearchProtocol protocol; private final String paramName; private final double deltaDistance; private final MersenneTwisterFast rng; private List<Chromosome> SPECIAL_MUTATE_pointsqueue = new LinkedList<Chromosome>(); private List<Chromosome> SPECIAL_MUTATE_neighborsqueue = new LinkedList<Chromosome>(); public DerivativeFitnessFunction(SearchProtocol protocol, MersenneTwisterFast rng) throws BehaviorSearchException { this.protocol = protocol; this.paramName = protocol.fitnessDerivativeParameter; this.deltaDistance = protocol.fitnessDerivativeDelta; this.rng = rng; if (deltaDistance == 0) { throw new BehaviorSearchException("When taking the 'derivative' of the fitness function with respect to parameter X, the delta value (change in X) cannot be 0!"); } } private Chromosome getPointDeltaNearby(Chromosome point) throws BehaviorSearchException { //Special case: if we set paramName to "@MUTATE@" then it chooses a neighboring point // in the search space by using mutation (with the mutation rate specified by deltaDistance) // from the current point. if (paramName.equals("@MUTATE@")) { double mutRate = deltaDistance; int failedMutationCounter = 0; final int MAX_MUTATION_ATTEMPTS = 1000000; Chromosome neighbor = point.mutate(mutRate, rng); while (neighbor.equals(point) && failedMutationCounter < MAX_MUTATION_ATTEMPTS ) { neighbor = point.mutate(mutRate, rng); } if (failedMutationCounter == MAX_MUTATION_ATTEMPTS) { throw new BehaviorSearchException("An extremely large number of mutation attempts all resulted in no mutation - perhaps your mutation-rate is too low?"); } SPECIAL_MUTATE_pointsqueue.add(point); SPECIAL_MUTATE_neighborsqueue.add(neighbor); return neighbor; } LinkedHashMap<String, Object> newParamSettings = new LinkedHashMap<String,Object>(point.getParamSettings()); Object curVal = newParamSettings.get(paramName); if (curVal instanceof Number) { double val = ((Number) curVal).doubleValue(); double newVal = (val - deltaDistance); newParamSettings.put(paramName, newVal); } else { throw new BehaviorSearchException("Derivative-based fitness measurements currently only work with numerical parameters!"); } return new DummyChromosome(point.getSearchSpace(), newParamSettings); } public HashMap<Chromosome, Integer> getRunsNeeded(Chromosome point, int repetitionsRequested, ResultsArchive archive) throws BehaviorSearchException { LinkedHashMap<Chromosome, Integer> map = new LinkedHashMap<Chromosome,Integer>(1); map.put(point, StrictMath.max(0, repetitionsRequested - archive.getResultsCount(point))); Chromosome deltaComparePoint = getPointDeltaNearby(point); map.put(deltaComparePoint, StrictMath.max(0, repetitionsRequested - archive.getResultsCount(deltaComparePoint))); return map; } public int getMaximumRunsThatCouldBeNeeded(int repetitionsRequested) { return 2 * repetitionsRequested; } public double evaluate(Chromosome point, ResultsArchive archive) throws BehaviorSearchException { List<ModelRunResult> resultsSoFar = archive.getResults( point ); LinkedList<Double> condensedResults = new LinkedList<Double>(); for (ModelRunResult result: resultsSoFar) { List<Double> singleRunHistory = result.getPrimaryTimeSeries(); double dResult = protocol.fitnessCollecting.collectFrom(singleRunHistory); condensedResults.add(dResult); } double pointVal = protocol.fitnessCombineReplications.combine(condensedResults); Chromosome neighbor; if (paramName.equals("@MUTATE@")) { int index = SPECIAL_MUTATE_pointsqueue.indexOf(point); neighbor = SPECIAL_MUTATE_neighborsqueue.get(index); SPECIAL_MUTATE_pointsqueue.remove(index); SPECIAL_MUTATE_neighborsqueue.remove(index); } else { neighbor = getPointDeltaNearby( point ); } resultsSoFar = archive.getResults( neighbor ); condensedResults = new LinkedList<Double>(); for (ModelRunResult result: resultsSoFar) { List<Double> singleRunHistory = result.getPrimaryTimeSeries(); double dResult = protocol.fitnessCollecting.collectFrom(singleRunHistory); condensedResults.add(dResult); } double deltaComparePointVal = protocol.fitnessCombineReplications.combine(condensedResults); double denominator = deltaDistance; // Special case, to see how much fitness varies between neighbors in the search space if (paramName.equals("@MUTATE@")) { denominator = 1; } if (protocol.fitnessDerivativeUseAbs) { return StrictMath.abs((pointVal - deltaComparePointVal) / denominator); } else { return (pointVal - deltaComparePointVal) / denominator; } } public double compare(double v1, double v2) { if (protocol.fitnessMinimized) { return v2 - v1 ; } else { return v1 - v2 ; } } public boolean strictlyBetterThan(double v1, double v2) { return compare(v1,v2) > 0.0; } public double getWorstConceivableFitnessValue() { return protocol.fitnessMinimized?Double.POSITIVE_INFINITY:Double.NEGATIVE_INFINITY; } public double getBestConceivableFitnessValue() { return protocol.fitnessMinimized?Double.NEGATIVE_INFINITY:Double.POSITIVE_INFINITY; } public boolean reachedStopGoalFitness(double fitness) { return false; //TODO: not implemented (see comment in StandardFitnessFunction) /*if (!hasStopGoal) { return false; } else { if (protocol.fitnessMinimized) { return fitness <= stopGoalFitness; } else { return fitness >= stopGoalFitness; } }*/ } }