/*
* This file is part of JGAP.
*
* JGAP offers a dual license model containing the LGPL as well as the MPL.
*
* For licensing information please see the file license.txt included with JGAP
* or have a look at the top of class org.jgap.Chromosome which representatively
* includes the JGAP license policy applicable for any file delivered with JGAP.
*/
package org.jgap.audit;
import java.io.*;
import java.util.*;
import org.jgap.*;
/**
* Gathers statistical data and returns them on request.
*
* @author Klaus Meffert
* @since 2.2
*/
public class Evaluator
implements Serializable {
/**@todo implement: overall score calculation (out of best/avg. fitness value
* etc.)
*/
/** String containing the CVS revision. Read out via reflection!*/
private final static String CVS_REVISION = "$Revision: 1.12 $";
/**
* Each data has its own data container
*/
private Map<Object, KeyedValues2D> m_permutationData;
/**
* Stores the run-numbers (indexes) for all permutations submitted
*/
private Map<Integer, Map> m_permutationRuns;
/**
* For processinf without permutation
*/
private KeyedValues2D m_data;
private PermutingConfiguration m_permConf;
/**
* Genotype data per permutation per run
*/
private Map<String, GenotypeData> m_genotypeData;
/**
* Genotype data per permutation (averaged over all runs)
*/
private List<GenotypeDataAvg> m_genotypeDataAvg;
public Evaluator(final PermutingConfiguration a_conf) {
if (a_conf == null) {
throw new IllegalArgumentException(
"Permuting Configuration must not be null!");
}
m_permConf = a_conf;
m_data = new KeyedValues2D();
m_permutationData = new Hashtable();
m_permutationRuns = new Hashtable();
m_genotypeData = new Hashtable();
m_genotypeDataAvg = new Vector();
}
public boolean hasNext() {
return m_permConf.hasNext();
}
public Configuration next()
throws InvalidConfigurationException {
return m_permConf.next();
}
public void setValue(double a_value, Comparable a_rowKey,
Comparable a_columnKey) {
m_data.setValue(new Double(a_value), a_rowKey, a_columnKey);
// fireDatasetChanged();
}
public Number getValue(Comparable rowKey, Comparable columnKey) {
return m_data.getValue(rowKey, columnKey);
}
/**
* Sets a specific value.
*
* @param a_permutation int
* @param a_run int
* @param a_value double
* @param a_rowKey Comparable
* @param a_columnKey Comparable
*
* @author Klaus Meffert
* @since 2.2
*/
public void setValue(int a_permutation, int a_run, double a_value,
Comparable a_rowKey, Comparable a_columnKey) {
Object key = createKey(a_permutation, a_run);
KeyedValues2D a_data = m_permutationData.get(key);
if (a_data == null) {
a_data = new KeyedValues2D();
m_permutationData.put(key, a_data);
}
// Add run-number (index).
// -----------------------
addRunNumber(a_permutation, a_run);
a_data.setValue(new Double(a_value), a_rowKey, a_columnKey);
}
protected void addRunNumber(int a_permutation, int a_run) {
Map v = m_permutationRuns.get(new Integer(a_permutation));
if (v == null) {
v = new Hashtable();
}
v.put(new Integer(a_run), new Integer(a_run));
m_permutationRuns.put(new Integer(a_permutation), v);
}
public Number getValue(int a_permutation, int a_run, Comparable rowKey,
Comparable columnKey) {
KeyedValues2D a_data = m_permutationData.get(createKey(a_permutation, a_run));
if (a_data == null) {
return null;
}
return a_data.getValue(rowKey, columnKey);
}
public KeyedValues2D getData() {
return m_data;
}
protected Object createKey(int a_permutation, int a_run) {
return a_permutation + "_" + a_run;
}
/**
* Calculates the average fitness value curve for a given permutation.
* If permutation -1 is given, a composition of all permutations available
* is created.
* @param a_permutation -1 to use all permutations
* @return DefaultKeyedValues2D list of fitness values, one for each
* individual in the generation
*
* @author Klaus Meffert
* @since 2.2
*/
public KeyedValues2D calcAvgFitness(int a_permutation) {
if (a_permutation == -1) {
Iterator it = m_permutationRuns.keySet().iterator();
Integer permNumberI;
int permNumber;
KeyedValues2D result = new KeyedValues2D();
while (it.hasNext()) {
permNumberI = (Integer) it.next();
permNumber = permNumberI.intValue();
calcAvgFitnessHelper(permNumber, result);
}
return result;
}
else {
KeyedValues2D a_data = new KeyedValues2D();
calcAvgFitnessHelper(a_permutation, a_data);
return a_data;
}
}
protected void calcAvgFitnessHelper(int a_permutation,
final KeyedValues2D result) {
// Determine run-numbers of given permutation.
// -------------------------------------------
Map runNumbers = m_permutationRuns.get(new Integer(a_permutation));
if (runNumbers == null) {
return;
}
// Loop over all run-numbers.
// --------------------------
Iterator it = runNumbers.keySet().iterator();
int numRuns = runNumbers.keySet().size();
Integer runI;
while (it.hasNext()) {
runI = (Integer) it.next();
// Determine dataset of given permutation.
// ---------------------------------------
KeyedValues2D a_data = m_permutationData.get(createKey(a_permutation,
runI.intValue()));
// Determine values for current run-number and "add" them to gathered
// data.
// ------------------------------------------------------------------
for (int col = 0; col < a_data.getColumnCount(); col++) {
for (int row = 0; row < a_data.getRowCount(); row++) {
// Previous value (summation).
// --------------------------.
Double d = (Double) result.getValue(a_data.getRowKey(row),
a_data.getColumnKey(col));
double newValue;
if (d == null) {
newValue = 0.0d;
}
else {
newValue = d.doubleValue();
}
// Add current value (divided by total number of runs to get an
// averaged value).
// ------------------------------------------------------------
newValue +=
a_data.getValue(a_data.getRowKey(row), a_data.getColumnKey(col)).
doubleValue() / numRuns;
// Set averaged value back to result dataset.
// ------------------------------------------
result.setValue(new Double(newValue), a_data.getRowKey(row),
a_data.getColumnKey(col));
}
}
}
}
/**
* Returns a list of lists (i.e. a matrix) to use for output as a table
* @param a_data DefaultKeyedValues2D
* @return List
*/
/**@todo implement*/
// public List getTable(KeyedValues2D a_data) {
// return null;
// }
/**
* Calculates average fitness value improvement per generation.
*
* @param a_permutation int
* @return DefaultKeyedValues2D
*
* @author Klaus Meffert
* @since 2.2
*/
public KeyedValues2D calcAvgFitnessImpr(int a_permutation) {
/**@todo implement*/
/**@todo is this method used resp. contained in calcPerformance?*/
Map runNumbers = m_permutationRuns.get(new Integer(a_permutation));
if (runNumbers == null) {
return null;
}
// Map fitnessImpr = new Hashtable();
// Loop over all run-numbers.
// --------------------------
Iterator it = runNumbers.keySet().iterator();
// int numRuns = runNumbers.keySet().size();
Integer runI;
while (it.hasNext()) {
runI = (Integer) it.next();
// Determine dataset of given permutation.
// ---------------------------------------
KeyedValues2D a_data = m_permutationData.get(createKey(a_permutation,
runI.intValue()));
for (int col = 0; col < a_data.getColumnCount(); col++) {
for (int row = 0; row < a_data.getRowCount(); row++) {
}
}
}
return null;
}
/**
*
* @param a_permutation the permutation to determine the number of runs for
* @return the number of runs for the given permutation
*/
public int getNumberOfRuns(int a_permutation) {
Map runNumbers = m_permutationRuns.get(new Integer(a_permutation));
if (runNumbers == null) {
return 0;
}
else {
return runNumbers.keySet().size();
}
}
/**
* Stores information contained in the given genotype.
* @param a_permutation int
* @param a_run index of the run proceeded for the given genotype
* @param a_genotype the genotype holding the population of chromosomes
*
* @author Klaus Meffert
* @since 2.2
*/
public void storeGenotype(int a_permutation, int a_run, Genotype a_genotype) {
storePopulation(a_permutation, a_run, a_genotype.getPopulation());
}
/**
* Stores information contained in the given genotype.
*
* @param a_permutation int
* @param a_run index of the run proceeded for the given genotype
* @param a_pop the population holding the relevant chromosomes
*
* @author Klaus Meffert
* @since 3.5 (originally named storeGenotype)
*/
public void storePopulation(int a_permutation, int a_run, Population a_pop) {
/**@todo implement*/
// average and maximum fitness value
//
GenotypeData data = new GenotypeData();
int generation = a_pop.getConfiguration().getGenerationNr();
data.generation = generation;
Population pop = a_pop;
data.hashCode = a_pop.hashCode();
int popSize = pop.size();
data.chromosomeData = new ChromosomeData[popSize];
data.size = popSize;
// Gather data of Chromosomes.
// ---------------------------
IChromosome chrom;
ChromosomeData chromData;
for (int i = 0; i < popSize; i++) {
chrom = pop.getChromosome(i);
chromData = new ChromosomeData();
chromData.fitnessValue = chrom.getFitnessValue();
chromData.size = chrom.size();
chromData.index = i;
data.chromosomeData[i] = chromData;
}
String key = a_permutation + "_" + a_run;
m_genotypeData.put(key, data);
addRunNumber(a_permutation, a_run);
}
public GenotypeData retrieveGenotype(int a_permutation, int a_run) {
return m_genotypeData.get(a_permutation + "_" + a_run);
}
/**
* Calculates performance metrics for a given permutation and run stored
* before with storeGenotype, like:
* average fitness, maximum fitness...
* @param a_permutation the permutation to compute the performance metrics
* for
* @return computed statistical data
*
* @author Klaus Meffert
* @since 2.2
*/
public GenotypeDataAvg calcPerformance(int a_permutation) {
int numRuns = getNumberOfRuns(a_permutation);
GenotypeData data;
GenotypeDataAvg dataAvg = new GenotypeDataAvg();
dataAvg.permutation = a_permutation;
double sizeAvg = 0.0d;
double fitnessAvg = 0.0d;
double fitnessBest = -1.0d;
double fitnessBestOld = -1.0d;
double fitness;
int fitnessBestGen = -1;
double fitnessAvgChroms;
double fitnessDiversityChromsOld = -1.0d;
double fitnessBestDeltaAvg = 0.0d;
double fitnessDiversity;
double fitnessDiversityAvg = 0.0d;
int size;
ChromosomeData chrom;
for (int i = 0; i < numRuns; i++) {
data = retrieveGenotype(a_permutation, i);
if (i == 0) {
dataAvg.generation = data.generation;
}
// Average number of chromosomes.
// ------------------------------
sizeAvg += (double) data.size / numRuns;
size = data.size;
fitnessAvgChroms = 0.0d;
fitnessDiversity = 0.0d;
double fitnessBestLocal = -1.0d;
for (int j = 0; j < size; j++) {
chrom = data.chromosomeData[j];
fitness = chrom.fitnessValue;
// diversity of fitness values over all chromosomes
if (j > 0) {
fitnessDiversity += Math.abs(fitness - fitnessDiversityChromsOld) /
(size - 1);
}
fitnessDiversityChromsOld = fitness;
// average fitness value for generation over all Chromosomes
fitnessAvgChroms += fitness / size;
// fittest chromosome in generation over all runs
if (fitnessBest < fitness) {
fitnessBest = fitness;
// memorize generation number in which fittest chromosome appeared
fitnessBestGen = data.generation;
}
// fittest chromosome in generation over current runs
if (fitnessBestLocal < fitness) {
fitnessBestLocal = fitness;
}
}
// average fitness value for generation over all runs
fitnessAvg += fitnessAvgChroms / numRuns;
// average fitness delta value for generation over all runs
fitnessDiversityAvg += fitnessDiversity / numRuns;
// absolute delta between two adjacent best fitness values
if (i > 0) {
fitnessBestDeltaAvg += Math.abs(fitnessBestLocal - fitnessBestOld) /
(numRuns - 1);
}
fitnessBestOld = fitnessBestLocal;
}
dataAvg.sizeAvg = sizeAvg;
dataAvg.avgFitnessValue = fitnessAvg;
dataAvg.bestFitnessValue = fitnessBest;
dataAvg.bestFitnessValueGeneration = fitnessBestGen;
dataAvg.avgDiversityFitnessValue = fitnessDiversityAvg;
dataAvg.avgBestDeltaFitnessValue = fitnessBestDeltaAvg;
// Store computed (averaged) data.
// -------------------------------
m_genotypeDataAvg.add(dataAvg);
return dataAvg;
}
/**
* Averaged genotype data (computed over all runs of a permutation)
*
* @author Klaus Meffert
* @since 2.2
*/
public class GenotypeDataAvg {
public int permutation;
public int generation;
public double sizeAvg;
public double bestFitnessValue;
public double avgFitnessValue;
public int bestFitnessValueGeneration;
public double avgDiversityFitnessValue;
public double avgBestDeltaFitnessValue;
}
/**
* Genotype data for one single run
*
* @author Klaus Meffert
* @since 2.2
*/
public class GenotypeData {
public int generation;
public int hashCode;
public int size;
public ChromosomeData[] chromosomeData;
}
public class ChromosomeData {
public int index;
public int size;
public double fitnessValue;
}
/**
* @return Averaged genotype data
*
* @author Klaus Meffert
* @since 3.5
*/
public List<GenotypeDataAvg> getGenotypeAverageData() {
return m_genotypeDataAvg;
}
}