package jcgp.backend.modules.problem; import java.io.File; import jcgp.backend.function.FunctionSet; import jcgp.backend.modules.Module; import jcgp.backend.parameters.DoubleParameter; import jcgp.backend.parameters.monitors.DoubleMonitor; import jcgp.backend.population.Population; import jcgp.backend.resources.ModifiableResources; import jcgp.backend.resources.Resources; /** * Defines the general behaviour of a CGP problem. The primary function of {@code Problem} * is to evaluate a population and assign a fitness value to each chromosome. *
* Problems are free to define whether better fitness means a higher or lower fitness value. * In some problem types, it is more convenient to treat fitness 0 as the best possible value. * This can be done by changing the fitness orientation to {@code BestFitness.HIGH} or {@code BestFitness.LOW} as appropriate. * Fitness orientation is set to high by default. *

* When extending this class, the constructor should call a few methods in order to * properly construct the problem type: {@code setFunctionSet()}, {@code setFileExtension()} and {@code setFitnessOrientation()}, * with the respective arguments. As with all subclasses of {@code Module}, {@code setName()} and * {@code registerParameters()} should be used where appropriate as well. *

* It is advisable to use {@code Resources.reportln()} and {@code Resources.report()} * to print any relevant information. Note that reportln() and report() are affected * by the report interval base parameter. Use {@code Resources.println()} and * {@code Resources.print()} to print information regardless of the current generation. * See {@link Resources} for more information. * * @see Module * @author Eduardo Pedroni * */ public abstract class Problem extends Module { private FunctionSet functionSet; private String fileExtension = ".*"; private BestFitness fitnessOrientation = BestFitness.HIGH; protected DoubleParameter maxFitness, bestFitness; /** * Initialises the two problem-wide parameters, maxFitness and bestFitness. * * @param resources a reference to the experiment's resources. */ protected Problem(Resources resources) { super(resources); maxFitness = new DoubleMonitor(0, "Max fitness"); bestFitness = new DoubleMonitor(0, "Best fitness"); registerParameters(maxFitness, bestFitness); } /** * The most important method of the problem type. This is called once * per generation, when the new population has been generated. *

* The basic functionality of this method is to loop through all chromosomes * in the population and decode them according to the problem type. The * fitness of each chromosome is then calculated using the problem data * or otherwise (subjective problem types such as art generation might * leave fitness evaluations up to the user) and assigned to the appropriate * chromosome. *

* In addition, realisations of this method should update the value of * bestFitness as appropriate, since the value of this parameter is displayed * if a GUI is in use. * * @param population the population to be evaluated. */ public abstract void evaluate(Population population); /** * Used to assert whether a given population contains a perfect solution * to the problem. It is up to the problem to define what qualifies * a perfect solution, as some problems (subject ones such as music and * art evolution, for example) might not have perfect solutions at all. * * @param population the population to search through for a perfect chromosome. * @return the perfect solution index, if one exits, -1 if no perfect solution was found. */ public abstract int hasPerfectSolution(Population population); /** * Used to assert whether a given population has a chromosome that is an improvement over * the current best chromosome. A typical implementation of this method * will simply compare chromosome fitness values, though the problem type * is free to implement this in any way. * * @param population the population potentially containing a fitter chromosome. * @return the index of the first chromosome in the population that is an improvement, -1 if none is found. */ public abstract int hasImprovement(Population population); /** * Parses the specified file and uses the parsed data to * set up the problem type instance appropriately. Any necessary * resource changes can be performed using the provided {@code ModifiableResources} * instance. *

* In addition, realisations of this method should update the value of * maxFitness where appropriate, as this may be displayed to the user * if a GUI is in use. * * @param file the data file to parse. * @param resources a modifiable reference to the experiment's resources. */ public abstract void parseProblemData(File file, ModifiableResources resources); /** * For internal use in subclass constructor, sets the functions to be * used for this problem type. See {@link FunctionSet} for more details. * * @param newFunctionSet the function set to use. */ protected void setFunctionSet(FunctionSet newFunctionSet) { this.functionSet = newFunctionSet; } /** * @return the FunctionSet object used by this problem type. */ public FunctionSet getFunctionSet() { return functionSet; } /** * For internal use in subclass constructors, sets the file extension accepted * by this problem type's parser. This is used by the GUI to filter loaded files * by extension in a file chooser. File extensions should be set in the form ".*", * so for plain text files, ".txt" would be used. * * @param fileExtension the accepted file extension. */ protected void setFileExtension(String fileExtension) { this.fileExtension = fileExtension; } /** * @return the file extension accepted by this problem type for problem data files. */ public String getFileExtension() { return fileExtension; } /** * @param newOrientation the new fitness orientation to set. */ protected void setFitnessOrientation(BestFitness newOrientation) { this.fitnessOrientation = newOrientation; } /** * @return the fitness orientation of this particular problem. */ public BestFitness getFitnessOrientation() { return fitnessOrientation; } /** * @return the current best fitness, in other words, the fitness * value of the fittest chromosome in the current generation. */ public double getBestFitness() { return bestFitness.get(); } /** * Resets the bestFitness parameter. */ public void reset() { bestFitness.set(0); } }