package jcgp.backend.statistics; import java.util.ArrayList; /** * This is a utility class for logging experiment statistics when doing multiple runs. *

* Information about each run is added via the {@code logRun()} method. The many getters * can be used to obtain statistics about the logged runs, such as success rate and average * fitness. *

* {@code JCGP} uses this class to perform its logging and print out experiment data at the end. * * * @author Eduardo Pedroni * */ public class StatisticsLogger { // this list holds the logged entries private ArrayList runEntries; /** * Create a new statistics logger, use this when resetting is necessary. */ public StatisticsLogger() { runEntries = new ArrayList(); } /** * Log a new run. Calling any of the statistics getters will * now take this logged run into account as well as all previously * logged runs. * * @param generation the last generation when improvement occurred. * @param fitness the best fitness achieved in the run. * @param active the number of active nodes in the best chromosome found. * @param successful true if a perfect solution was found, false if otherwise. */ public void logRun(int generation, double fitness, int active, boolean successful) { runEntries.add(new RunEntry(generation, fitness, active, successful)); } /** * Averages the best fitness obtained in each run. * * @return the average fitness. */ public double getAverageFitness() { double average = 0; for (RunEntry runEntry : runEntries) { average += runEntry.getFitness() / runEntries.size(); } return average; } /** * Calculates the standard deviation of * the best fitness obtained in each run. * * @return the standard deviation of average fitnesses. */ public double getAverageFitnessStdDev() { double average = getAverageFitness(); double temp, stdDev = 0; for (RunEntry runEntry : runEntries) { temp = runEntry.getFitness() - average; temp = temp * temp; stdDev += temp; } return stdDev; } /** * Averages the number of active nodes in the * best chromosomes obtained across all runs. * * @return the average number of active nodes. */ public double getAverageActiveNodes() { double average = 0; for (RunEntry runEntry : runEntries) { average += runEntry.getActiveNodes() / runEntries.size(); } return average; } /** * Calculates the standard deviation of * the number of active nodes in the best solution * in each run. * * @return the standard deviation of active node counts. */ public double getAverageActiveNodesStdDev() { double average = getAverageActiveNodes(); double temp, stdDev = 0; for (RunEntry runEntry : runEntries) { temp = runEntry.getActiveNodes() - average; temp = temp * temp; stdDev += temp; } return stdDev; } /** * Calculates the average generation out of all runs. * The generation value in each run corresponds to the * last generation in which improvement happened. *

* Note that this method includes runs where no perfect * solution was found. For the average number of generations * for perfect solutions only, use {@code getAverageSuccessfulGenerations}. * * @return the average number of generations. */ public double getAverageGenerations() { double average = 0; for (RunEntry runEntry : runEntries) { average += runEntry.getGeneration() / runEntries.size(); } return average; } /** * Calculates the standard deviation of * the average number of generations in * each run. * * @return the standard deviation of the number of generations. */ public double getAverageGenerationsStdDev() { double average = getAverageGenerations(); double temp, stdDev = 0; for (RunEntry runEntry : runEntries) { temp = runEntry.getGeneration() - average; temp = temp * temp; stdDev += temp; } return stdDev; } /** * @return the highest fitness across all runs. */ public double getHighestFitness() { double highest = 0; for (RunEntry runEntry : runEntries) { if (runEntry.getFitness() > highest) { highest = runEntry.getFitness(); } } return highest; } /** * @return the lowest fitness across all runs. */ public double getLowestFitness() { double lowest = Double.MAX_VALUE; for (RunEntry runEntry : runEntries) { if (runEntry.getFitness() < lowest) { lowest = runEntry.getFitness(); } } return lowest; } /** * * @return the number of runs in which a perfect solution was found. */ public int getSuccessfulRuns() { int count = 0; for (RunEntry runEntry : runEntries) { // only increment if solution was perfect if (runEntry.isSuccessful()) { count++; } } return count; } /** * Calculates the ratio of successful runs (runs where * a perfect solution was found) to total number of runs. * A double-precision value between 0 and 1 is returned, * where 0 means 0% success rate and 1 means 100% success rate. * * @return the success rate across all runs. */ public double getSuccessRate() { return getSuccessfulRuns() / (double) runEntries.size(); } /** * Calculates the average generation out of successful runs only. * The generation value in each successful run corresponds to the * generation in which the perfect solution was found. * * @return the average number of generations for perfect solutions. */ public double getAverageSuccessfulGenerations() { double average = 0; int successfulRuns = getSuccessfulRuns(); for (RunEntry runEntry : runEntries) { // only if solution was perfect if (runEntry.isSuccessful()) { average += runEntry.getGeneration() / successfulRuns; } } return average; } /** * Calculates the standard deviation of * the average number of generations in * each run where a perfect solution was found. * * @return the standard deviation of the number of generations in successful runs. */ public double getAverageSuccessfulGenerationsStdDev() { double average = getAverageSuccessfulGenerations(); double temp, stdDev = 0; for (RunEntry runEntry : runEntries) { // only if solution was perfect if (runEntry.isSuccessful()) { temp = runEntry.getGeneration() - average; temp = temp * temp; stdDev += temp; } } return stdDev; } }