aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorEduardo Pedroni <ep625@york.ac.uk>2014-05-06 14:29:37 +0100
committerEduardo Pedroni <ep625@york.ac.uk>2014-05-06 14:29:37 +0100
commit8189116ea4b5db4675e31dfd04a5687d55e29262 (patch)
treec1815021452a888f8838f1628d8fb4689777e73e /src
parentaa9e74e7f67789f6353fc26e02ee8e68e40609a2 (diff)
Added javadocs, made minor changes to the comments
Diffstat (limited to 'src')
-rw-r--r--src/jcgp/JCGP.java53
-rw-r--r--src/jcgp/backend/function/DigitalCircuitFunctions.java6
-rw-r--r--src/jcgp/backend/function/SymbolicRegressionFunctions.java8
-rw-r--r--src/jcgp/backend/function/TravellingSalesmanFunctions.java11
-rw-r--r--src/jcgp/backend/modules/Module.java22
-rw-r--r--src/jcgp/backend/modules/es/EvolutionaryStrategy.java13
-rw-r--r--src/jcgp/backend/modules/es/MuPlusLambda.java43
-rw-r--r--src/jcgp/backend/modules/es/TournamentSelection.java33
-rw-r--r--src/jcgp/backend/modules/mutator/FixedPointMutator.java2
-rw-r--r--src/jcgp/backend/modules/mutator/Mutator.java12
-rw-r--r--src/jcgp/backend/modules/mutator/PercentPointMutator.java4
-rw-r--r--src/jcgp/backend/modules/mutator/PointMutator.java31
-rw-r--r--src/jcgp/backend/modules/mutator/ProbabilisticMutator.java37
-rw-r--r--src/jcgp/backend/modules/problem/DigitalCircuitProblem.java52
-rw-r--r--src/jcgp/backend/modules/problem/Problem.java57
-rw-r--r--src/jcgp/backend/modules/problem/SymbolicRegressionProblem.java46
-rw-r--r--src/jcgp/backend/modules/problem/TestCaseProblem.java14
-rw-r--r--src/jcgp/backend/modules/problem/TravellingSalesmanProblem.java56
-rw-r--r--src/jcgp/backend/parameters/BooleanParameter.java1
-rw-r--r--src/jcgp/backend/parameters/DoubleParameter.java1
-rw-r--r--src/jcgp/backend/parameters/IntegerParameter.java1
-rw-r--r--src/jcgp/backend/parameters/Parameter.java2
-rw-r--r--src/jcgp/backend/parameters/ParameterStatus.java1
-rw-r--r--src/jcgp/backend/parsers/ChromosomeParser.java3
-rw-r--r--src/jcgp/backend/population/Chromosome.java7
-rw-r--r--src/jcgp/backend/population/MutableElement.java2
-rw-r--r--src/jcgp/backend/population/Population.java64
-rw-r--r--src/jcgp/backend/resources/ModifiableResources.java8
-rw-r--r--src/jcgp/backend/resources/Resources.java14
-rw-r--r--src/jcgp/backend/tests/PopulationTests.java4
-rw-r--r--src/jcgp/gui/GUI.java4
-rw-r--r--src/jcgp/gui/console/GUIConsole.java21
-rw-r--r--src/jcgp/gui/dragresize/HorizontalDragResize.java51
-rw-r--r--src/jcgp/gui/dragresize/VerticalDragResize.java46
-rw-r--r--src/jcgp/gui/population/ChromosomePane.java41
-rw-r--r--src/jcgp/gui/population/GUIGene.java6
-rw-r--r--src/jcgp/gui/population/GUIInput.java3
-rw-r--r--src/jcgp/gui/population/GUINode.java18
-rw-r--r--src/jcgp/gui/population/GUIOutput.java15
-rw-r--r--src/jcgp/gui/population/PopulationPane.java6
-rw-r--r--src/jcgp/gui/settings/SettingsPane.java9
-rw-r--r--src/jcgp/gui/settings/parameters/GUIParameter.java2
-rw-r--r--src/jcgp/gui/settings/testcase/TestCaseTable.java33
43 files changed, 500 insertions, 363 deletions
diff --git a/src/jcgp/JCGP.java b/src/jcgp/JCGP.java
index 213dd06..898f229 100644
--- a/src/jcgp/JCGP.java
+++ b/src/jcgp/JCGP.java
@@ -38,7 +38,6 @@ import org.reflections.Reflections;
* and must implement jcgp.resources.Console.
*
* @author Eduardo Pedroni
- * @see Resources, Module
*/
public class JCGP {
@@ -121,7 +120,7 @@ public class JCGP {
* Iterates through all classes in the classpath and builds a list
* of instances of all valid evolutionary strategies found. In order
* to be included in this list, a class must implement {@code EvolutionaryStrategy}
- * and must contain a constructor with a Resources object as its only argument.
+ * and must contain a constructor with a {@code Resources} object as its only argument.
*/
private void createEvolutionaryStrategyList() {
Set<Class<? extends EvolutionaryStrategy>> esList = reflections.getSubTypesOf(EvolutionaryStrategy.class);
@@ -159,7 +158,7 @@ public class JCGP {
* Iterates through all classes in the classpath and builds a list
* of instances of all valid mutators found. In order to be included
* in this list, a class must implement {@code Mutator}
- * and must contain a constructor with a Resources object as its only argument.
+ * and must contain a constructor with a {@code Resources} object as its only argument.
*/
private void createMutatorList() {
Set<Class<? extends Mutator>> mutatorList = reflections.getSubTypesOf(Mutator.class);
@@ -198,7 +197,7 @@ public class JCGP {
* Iterates through all classes in the classpath and builds a list
* of instances of all valid problem types found. In order to be included
* in this list, a class must implement {@code Problem}
- * and must contain a constructor with a Resources object as its only argument.
+ * and must contain a constructor with a {@code Resources} object as its only argument.
*/
private void createProblemList() {
Set<Class<? extends Problem>> problemTypes = reflections.getSubTypesOf(Problem.class);
@@ -236,7 +235,7 @@ public class JCGP {
}
/**
- * Returns a reference to the ModifiableResources used by the
+ * Returns a reference to the {@code ModifiableResources} used by the
* experiment. <br>
* Use this with care, since changing experiment parameters may
* have unintended effects if not done properly.
@@ -303,7 +302,7 @@ public class JCGP {
/**
- * @param mutator the index of the desired mutator.
+ * @param index the index of the desired mutator.
*/
public void setMutator(int index) {
this.mutator = mutators.get(index);
@@ -312,7 +311,7 @@ public class JCGP {
/**
- * @param evolutionaryStrategy the index of the desired evolutionary strategy.
+ * @param index the index of the desired evolutionary strategy.
*/
public void setEvolutionaryStrategy(int index) {
this.evolutionaryStrategy = evolutionaryStrategies.get(index);
@@ -321,11 +320,12 @@ public class JCGP {
/**
- * @param problem the index of the desired problem type.
+ * @param index the index of the desired problem type.
*/
public void setProblem(int index) {
this.problem = problems.get(index);
resources.setFunctionSet(problem.getFunctionSet());
+ resources.setFitnessOrientation(problem.getFitnessOrientation());
}
/**
@@ -348,13 +348,14 @@ public class JCGP {
if (resources.currentGeneration() < resources.generations()) {
// we still have generations left to go
- if (problem.isPerfectSolution(population.getFittest())) {
+ int perfect = problem.perfectSolutionFound(population);
+ if (perfect >= 0) {
// log results
- statistics.logRun(resources.currentGeneration(), population.getFittest().getFitness(), population.getFittest().getActiveNodes().size(), true);
+ statistics.logRun(resources.currentGeneration(), population.get(perfect).getFitness(), population.get(perfect).getActiveNodes().size(), true);
resetStatisticsValues();
// solution has been found, start next run
- resources.println("[CGP] Solution found, generation " + resources.currentGeneration() + ", chromosome " + population.getFittestIndex() + "\n");
+ resources.println("[CGP] Solution found: generation " + resources.currentGeneration() + ", chromosome " + perfect + "\n");
if (resources.currentRun() < resources.runs()) {
// there are still runs left
@@ -369,17 +370,23 @@ public class JCGP {
finished = true;
}
} else {
- if (problem.isImprovement(population.getFittest())) {
- printImprovement();
+ // solution not found, look for improvement
+ int improvement = problem.hasImprovement(population);
+
+ if (improvement >= 0) {
+ // there has been improvement, print it
+ printImprovement(improvement);
lastImprovementGeneration = resources.currentGeneration();
- bestFitnessFound = population.getFittest().getFitness();
- activeNodes = population.getFittest().getActiveNodes().size();
+ bestFitnessFound = population.get(improvement).getFitness();
+ activeNodes = population.get(improvement).getActiveNodes().size();
} else {
+ // there has been no improvement, report generation
reportGeneration();
}
resources.incrementGeneration();
- // solution isn't perfect and we still have generations left, evolve more!
- evolutionaryStrategy.evolve(population, mutator, (Resources) resources);
+
+ // we still have generations left, evolve more!
+ evolutionaryStrategy.evolve(population, mutator);
}
} else {
// the run has ended, tell the user and log it
@@ -436,9 +443,9 @@ public class JCGP {
* Used internally for reporting improvement, which happens independently of
* the report interval parameter.
*/
- private void printImprovement() {
+ private void printImprovement(int chromosome) {
resources.println("[CGP] Generation: " + resources.currentGeneration() + ", fittest chromosome ("
- + population.getFittestIndex() + ") has fitness: " + population.getFittest().getFitness());
+ + chromosome + ") has fitness: " + population.get(chromosome).getFitness());
}
/**
@@ -446,8 +453,8 @@ public class JCGP {
* by the report interval parameter.
*/
private void reportGeneration() {
- resources.reportln("[CGP] Generation: " + resources.currentGeneration() + ", fittest chromosome ("
- + population.getFittestIndex() + ") has fitness: " + population.getFittest().getFitness());
+ resources.reportln("[CGP] Generation: " + resources.currentGeneration() + ", best fitness: "
+ + problem.getBestFitness());
}
/**
@@ -545,7 +552,7 @@ public class JCGP {
* @param chromosomeIndex the population index into which to parse.
*/
public void loadChromosome(File file, int chromosomeIndex) {
- ChromosomeParser.parse(file, population.getChromosome(chromosomeIndex), resources);
+ ChromosomeParser.parse(file, population.get(chromosomeIndex), resources);
}
/**
@@ -556,7 +563,7 @@ public class JCGP {
* @param chromosomeIndex the index of the chromosome to save.
*/
public void saveChromosome(File file, int chromosomeIndex) {
- ChromosomeParser.save(file, population.getChromosome(chromosomeIndex), resources);
+ ChromosomeParser.save(file, population.get(chromosomeIndex), resources);
}
/**
diff --git a/src/jcgp/backend/function/DigitalCircuitFunctions.java b/src/jcgp/backend/function/DigitalCircuitFunctions.java
index 0d4ae8e..a217eb7 100644
--- a/src/jcgp/backend/function/DigitalCircuitFunctions.java
+++ b/src/jcgp/backend/function/DigitalCircuitFunctions.java
@@ -5,16 +5,16 @@ package jcgp.backend.function;
* (defined as unsigned integer functions in the classic
* CGP implementation) defined in static nested classes.
* <br>
- * This is the function set used by DigitalCircuitProblem.
+ * This is the function set used by {@code DigitalCircuitProblem}.
*
- * @see DigitalCircuiProblem
+ * @see jcgp.backend.modules.problem.DigitalCircuitProblem
* @author Eduardo Pedroni
*
*/
public class DigitalCircuitFunctions extends FunctionSet {
/**
- * Creates a new instance of DigitalCircuitFunctions.
+ * Creates a new instance of {@code DigitalCircuitFunctions}.
*/
public DigitalCircuitFunctions() {
registerFunctions(
diff --git a/src/jcgp/backend/function/SymbolicRegressionFunctions.java b/src/jcgp/backend/function/SymbolicRegressionFunctions.java
index 9c6a3b1..a4750fc 100644
--- a/src/jcgp/backend/function/SymbolicRegressionFunctions.java
+++ b/src/jcgp/backend/function/SymbolicRegressionFunctions.java
@@ -1,5 +1,7 @@
package jcgp.backend.function;
+import jcgp.backend.modules.problem.SymbolicRegressionProblem;
+
/**
* This class contains all symbolic regression functions
* (defined as double functions in the classic CGP implementation)
@@ -180,7 +182,7 @@ public class SymbolicRegressionFunctions extends FunctionSet {
/**
* Protected tangent function, in radians. Returns the tangent of input 0.
- * If input 0 is less than {@link DoubleArithmetic.}DIVISION_LIMIT,
+ * If input 0 is less than {@code DoubleArithmetic.DIVISION_LIMIT},
* this returns it unchanged.
*
* @see Math
@@ -317,7 +319,7 @@ public class SymbolicRegressionFunctions extends FunctionSet {
/**
* Protected natural log function. Returns the natural log of the absolute
- * value of input 0. If input 0 is less than {@link DoubleArithmetic.}DIVISION_LIMIT,
+ * value of input 0. If input 0 is less than {@code DoubleArithmetic.DIVISION_LIMIT},
* this returns it unchanged.
*
* @see Math
@@ -346,7 +348,7 @@ public class SymbolicRegressionFunctions extends FunctionSet {
/**
* Protected log base 10 function. Returns the log to base 10 the absolute
- * value of input 0. If input 0 is less than {@link DoubleArithmetic.}DIVISION_LIMIT,
+ * value of input 0. If input 0 is less than {@code DoubleArithmetic.DIVISION_LIMIT},
* this returns it unchanged.
*
* @see Math
diff --git a/src/jcgp/backend/function/TravellingSalesmanFunctions.java b/src/jcgp/backend/function/TravellingSalesmanFunctions.java
index 06b44bb..8a326b5 100644
--- a/src/jcgp/backend/function/TravellingSalesmanFunctions.java
+++ b/src/jcgp/backend/function/TravellingSalesmanFunctions.java
@@ -3,9 +3,8 @@ package jcgp.backend.function;
/**
* This class contains all travelling salesman functions in static nested classes.
* <br>
- * This is the function set used by TravellingSalesmanProblem.
+ * This is the function set to be used by TravellingSalesmanProblem.
*
- * @see TravellingSalesmanProblem
* @author Eduardo Pedroni
*
*/
@@ -113,7 +112,7 @@ public class TravellingSalesmanFunctions extends FunctionSet {
/**
* Scaled exponential function. Returns the exponential of input 0
- * scaled to the range 0 < x < 1.
+ * scaled to the range 0 &lt; x &gt; 1.
*
* @see Math
*/
@@ -228,7 +227,7 @@ public class TravellingSalesmanFunctions extends FunctionSet {
/**
* Scaled hypotenuse function. Returns the square root of input 0 squared
- * plus input 1 squared, scaled to the range 0 < x < 1.
+ * plus input 1 squared, scaled to the range 0 &lt; x &gt; 1.
*
* @see Math
*/
@@ -257,7 +256,7 @@ public class TravellingSalesmanFunctions extends FunctionSet {
/**
* Scaled addition returns the sum of inputs 0 and 1 scaled
- * to the range 0 < x < 1.
+ * to the range 0 &lt; x &gt; 1.
*
*/
public static class ScaledAddition extends Function {
@@ -285,7 +284,7 @@ public class TravellingSalesmanFunctions extends FunctionSet {
/**
* Symmetric subtraction returns the absolute difference between inputs 0 and 1,
- * scaled to the range 0 < x < 1.
+ * scaled to the range 0 &lt;; x &gt; 1.
*
*/
public static class SymmetricSubtraction extends Function {
diff --git a/src/jcgp/backend/modules/Module.java b/src/jcgp/backend/modules/Module.java
index b53184e..a4d36c1 100644
--- a/src/jcgp/backend/modules/Module.java
+++ b/src/jcgp/backend/modules/Module.java
@@ -3,6 +3,7 @@ package jcgp.backend.modules;
import java.util.ArrayList;
import jcgp.backend.parameters.Parameter;
+import jcgp.backend.resources.Resources;
/**
* This class defines the expected behaviour of a module. Generally, modules
@@ -15,6 +16,9 @@ import jcgp.backend.parameters.Parameter;
* In addition, implementations of {@code Module} should specify a module name
* in their constructor using {@code setName()}. If a name is not provided,
* the simple name of the class will be used.
+ * <br>
+ * All modules, by definition, contain a reference to the experiment's resources, which
+ * must be passed at construction. The resources should be accessed with {@code getResources()}.
*
* @see Parameter
* @author Eduardo Pedroni
@@ -24,6 +28,17 @@ public abstract class Module {
private ArrayList<Parameter<?>> localParameters = new ArrayList<Parameter<?>>();
private String name = getClass().getSimpleName();
+ private Resources resources;
+
+ /**
+ * Makes a new instance of this class. This should never
+ * be called directly, and should instead be used by subclasses.
+ *
+ * @param resources a reference to the experiment's resources.
+ */
+ protected Module(Resources resources) {
+ this.resources = resources;
+ }
/**
* This method is used by the GUI in order to build visual
@@ -66,6 +81,13 @@ public abstract class Module {
this.name = name;
}
+ /**
+ * @return the experiment's resources.
+ */
+ protected Resources getResources() {
+ return resources;
+ }
+
@Override
public String toString() {
return name;
diff --git a/src/jcgp/backend/modules/es/EvolutionaryStrategy.java b/src/jcgp/backend/modules/es/EvolutionaryStrategy.java
index 1a14552..30a3c4a 100644
--- a/src/jcgp/backend/modules/es/EvolutionaryStrategy.java
+++ b/src/jcgp/backend/modules/es/EvolutionaryStrategy.java
@@ -33,12 +33,21 @@ import jcgp.backend.resources.Resources;
public abstract class EvolutionaryStrategy extends Module {
/**
+ * For internal use only, initialises the resources field.
+ *
+ * @param resources the experiment's resources.
+ */
+ protected EvolutionaryStrategy(Resources resources) {
+ super(resources);
+ }
+
+ /**
* Performs the selection algorithm and uses the mutator to create
* the next generation of solutions.
*
* @param population the population to evolve.
* @param mutator the mutator with which to mutate the promoted individuals.
- * @param resources parameters and utilities for optional reference.
*/
- public abstract void evolve(Population population, Mutator mutator, Resources resources);
+ public abstract void evolve(Population population, Mutator mutator);
+
}
diff --git a/src/jcgp/backend/modules/es/MuPlusLambda.java b/src/jcgp/backend/modules/es/MuPlusLambda.java
index 8186b11..3743ee6 100644
--- a/src/jcgp/backend/modules/es/MuPlusLambda.java
+++ b/src/jcgp/backend/modules/es/MuPlusLambda.java
@@ -28,21 +28,21 @@ import jcgp.backend.resources.Resources;
*
*/
public class MuPlusLambda extends EvolutionaryStrategy {
-
+
private IntegerParameter mu, lambda;
private BooleanParameter report;
-
+
/**
* Creates a new instance of MuPlusLambda.
*
* @param resources a reference to the experiment's resources.
*/
public MuPlusLambda(final Resources resources) {
- super();
+ super(resources);
mu = new IntegerParameter(1, "Parents (μ)") {
@Override
public void validate(Number newValue) {
- if (newValue.intValue() + lambda.get() != resources.populationSize()) {
+ if (newValue.intValue() + lambda.get() != getResources().populationSize()) {
status = ParameterStatus.INVALID;
status.setDetails("Parents + offspring must equal population size.");
} else if (newValue.intValue() <= 0) {
@@ -53,11 +53,11 @@ public class MuPlusLambda extends EvolutionaryStrategy {
}
}
};
-
+
lambda = new IntegerParameter(4, "Offspring (λ)") {
@Override
public void validate(Number newValue) {
- if (newValue.intValue() + mu.get() != resources.populationSize()) {
+ if (newValue.intValue() + mu.get() != getResources().populationSize()) {
status = ParameterStatus.INVALID;
status.setDetails("Parents + offspring must equal population size.");
} else if (newValue.intValue() <= 0) {
@@ -68,29 +68,30 @@ public class MuPlusLambda extends EvolutionaryStrategy {
}
}
};
-
+
report = new BooleanParameter(false, "Report");
-
+
setName("(μ + λ)");
registerParameters(mu, lambda, report);
}
-
+
@Override
- public void evolve(Population population, Mutator mutator, Resources resources) {
- /* Population is sorted in ascending order of fitness, so leave the last
- * mu chromosomes intact
- */
- for (int i = 0; i < resources.populationSize() - mu.get(); i++) {
+ public void evolve(Population population, Mutator mutator) {
+ // sort the population in order of best fitness
+ population.sort();
+
+ // population is now sorted in ascending order of fitness, the last chromosomes are the fittest
+ for (int i = 0; i < getResources().populationSize() - mu.get(); i++) {
// select a random parent out of the lambda offspring individuals
- int randomParent = resources.populationSize() - 1 - resources.getRandomInt(mu.get());
- if (report.get()) resources.reportln("[ES] Copying Chr " + randomParent + " to population position " + i);
+ int randomParent = getResources().populationSize() - 1 - getResources().getRandomInt(mu.get());
+ if (report.get()) getResources().reportln("[ES] Copying Chr " + randomParent + " to population position " + i);
population.copyChromosome(randomParent, i);
-
+
// mutate selected chromosome
- if (report.get()) resources.reportln("[ES] Mutating copied chromosome");
- mutator.mutate(population.getChromosome(i), resources);
+ if (report.get()) getResources().reportln("[ES] Mutating copied chromosome");
+ mutator.mutate(population.get(i));
}
-
- if (report.get()) resources.reportln("[ES] Generation is complete");
+
+ if (report.get()) getResources().reportln("[ES] Generation is complete");
}
}
diff --git a/src/jcgp/backend/modules/es/TournamentSelection.java b/src/jcgp/backend/modules/es/TournamentSelection.java
index 209caca..6d467f5 100644
--- a/src/jcgp/backend/modules/es/TournamentSelection.java
+++ b/src/jcgp/backend/modules/es/TournamentSelection.java
@@ -44,7 +44,7 @@ public class TournamentSelection extends EvolutionaryStrategy {
* @param resources a reference to the experiment's resources.
*/
public TournamentSelection(final Resources resources) {
- super();
+ super(resources);
tournamentSize = new IntegerParameter(1, "Tournament size") {
@Override
public void validate(Number newValue) {
@@ -73,17 +73,22 @@ public class TournamentSelection extends EvolutionaryStrategy {
}
@Override
- public void evolve(Population population, Mutator mutator, Resources resources) {
+ public void evolve(Population population, Mutator mutator) {
/* Create an entirely new population by isolating random subsets of
* the original population and choosing the fittest individual within
* that subset. Each chosen individual is mutated and copied back into the
* population.
*/
- Chromosome[] newPopulation = new Chromosome[resources.populationSize()];
+
+ // sort the population by fitness to make things easier
+ population.sort();
+
+ // this array holds the new population temporarily, until it is copied over
+ Chromosome[] newPopulation = new Chromosome[getResources().populationSize()];
// start by selecting all of the chromosomes that will be promoted
- for (int i = 0; i < resources.populationSize(); i++) {
- if (report.get()) resources.reportln("[ES] Starting tournament " + i);
+ for (int i = 0; i < getResources().populationSize(); i++) {
+ if (report.get()) getResources().reportln("[ES] Starting tournament " + i);
/* the population is sorted in ascending order of fitness,
* meaning the higher the index of the contender, the fitter
@@ -91,21 +96,21 @@ public class TournamentSelection extends EvolutionaryStrategy {
*/
int[] contenders = new int[tournamentSize.get()];
for (int t = 0; t < tournamentSize.get() - 1; t++) {
- contenders[t] = resources.getRandomInt(resources.populationSize());
+ contenders[t] = getResources().getRandomInt(getResources().populationSize());
}
- if (report.get()) resources.reportln("[ES] Selected contenders: " + Arrays.toString(contenders));
+ if (report.get()) getResources().reportln("[ES] Selected contenders: " + Arrays.toString(contenders));
Arrays.sort(contenders);
- if (report.get()) resources.reportln("[ES] Chr " + contenders[contenders.length - 1] + " wins the tournament, copying and mutating...");
+ if (report.get()) getResources().reportln("[ES] Chr " + contenders[contenders.length - 1] + " wins the tournament, copying and mutating...");
// create a copy of the selected chromosome and mutate it
- newPopulation[i] = new Chromosome(population.getChromosome(contenders[contenders.length - 1]));
- mutator.mutate(newPopulation[i], resources);
+ newPopulation[i] = new Chromosome(population.get(contenders[contenders.length - 1]));
+ mutator.mutate(newPopulation[i]);
}
- if (report.get()) resources.reportln("[ES] Tournaments are finished, copying new chromosomes into population");
+ if (report.get()) getResources().reportln("[ES] Tournaments are finished, copying new chromosomes into population");
// newPopulation has been generated, copy into the population
- for (int c = 0; c < resources.populationSize(); c++) {
- population.getChromosome(c).copyGenes(newPopulation[c]);
+ for (int c = 0; c < getResources().populationSize(); c++) {
+ population.get(c).copyGenes(newPopulation[c]);
}
- if (report.get()) resources.reportln("[ES] Generation is complete");
+ if (report.get()) getResources().reportln("[ES] Generation is complete");
}
}
diff --git a/src/jcgp/backend/modules/mutator/FixedPointMutator.java b/src/jcgp/backend/modules/mutator/FixedPointMutator.java
index 5d40c57..1efdd2f 100644
--- a/src/jcgp/backend/modules/mutator/FixedPointMutator.java
+++ b/src/jcgp/backend/modules/mutator/FixedPointMutator.java
@@ -25,7 +25,7 @@ public class FixedPointMutator extends PointMutator {
* @param resources a reference to the experiment's resources.
*/
public FixedPointMutator(final Resources resources) {
- super();
+ super(resources);
genesMutated = new IntegerParameter(5, "Genes mutated", false, false) {
@Override
public void validate(Number newValue) {
diff --git a/src/jcgp/backend/modules/mutator/Mutator.java b/src/jcgp/backend/modules/mutator/Mutator.java
index 02fd70a..56692ef 100644
--- a/src/jcgp/backend/modules/mutator/Mutator.java
+++ b/src/jcgp/backend/modules/mutator/Mutator.java
@@ -25,14 +25,22 @@ import jcgp.backend.resources.Resources;
*
*/
public abstract class Mutator extends Module {
+
+ /**
+ * For internal use only, initialises the resources field.
+ *
+ * @param resources the experiment's resources.
+ */
+ protected Mutator(Resources resources) {
+ super(resources);
+ }
/**
* Applies mutations to the specified chromosome according
* to the parameter values.
*
* @param chromosome the chromosome to mutate.
- * @param resources parameters and utilities for optional reference.
*/
- public abstract void mutate(Chromosome chromosome, Resources resources);
+ public abstract void mutate(Chromosome chromosome);
}
diff --git a/src/jcgp/backend/modules/mutator/PercentPointMutator.java b/src/jcgp/backend/modules/mutator/PercentPointMutator.java
index 31a7739..015edf4 100644
--- a/src/jcgp/backend/modules/mutator/PercentPointMutator.java
+++ b/src/jcgp/backend/modules/mutator/PercentPointMutator.java
@@ -24,12 +24,12 @@ public class PercentPointMutator extends PointMutator {
private DoubleParameter mutationRate;
/**
- * Creates a new instance of PointMutator.
+ * Creates a new instance of PercentPointMutator.
*
* @param resources a reference to the experiment's resources.
*/
public PercentPointMutator(final Resources resources) {
- super();
+ super(resources);
mutationRate = new DoubleParameter(10, "Percent mutation", false, false) {
@Override
public void validate(Number newValue) {
diff --git a/src/jcgp/backend/modules/mutator/PointMutator.java b/src/jcgp/backend/modules/mutator/PointMutator.java
index 0223a37..9e421c2 100644
--- a/src/jcgp/backend/modules/mutator/PointMutator.java
+++ b/src/jcgp/backend/modules/mutator/PointMutator.java
@@ -24,48 +24,57 @@ public abstract class PointMutator extends Mutator {
protected IntegerParameter genesMutated;
protected BooleanParameter report;
+
+ /**
+ * For internal use only, initialises the resources field.
+ *
+ * @param resources the experiment's resources.
+ */
+ protected PointMutator(Resources resources) {
+ super(resources);
+ }
@Override
- public void mutate(Chromosome chromosome, Resources resources) {
- if (report.get()) resources.reportln("[Mutator] Number of mutations to be performed: " + genesMutated.get());
+ public void mutate(Chromosome chromosome) {
+ if (report.get()) getResources().reportln("[Mutator] Number of mutations to be performed: " + genesMutated.get());
// for however many genes must be mutated
for (int i = 0; i < genesMutated.get(); i++) {
MutableElement m = chromosome.getRandomMutableElement();
- if (report.get()) resources.report("[Mutator] Mutation " + i + " selected " + m + ", ");
+ if (report.get()) getResources().report("[Mutator] Mutation " + i + " selected " + m + ", ");
// outputs and nodes are mutated differently
if (m instanceof Output) {
- if (report.get()) resources.report("changed source from " + ((Output) m).getSource() + " ");
+ if (report.get()) getResources().report("changed source from " + ((Output) m).getSource() + " ");
// outputs are easy, simply set to a different random connection, any will do
m.setConnection(0, chromosome.getRandomConnection());
- if (report.get()) resources.reportln("to " + ((Output) m).getSource());
+ if (report.get()) getResources().reportln("to " + ((Output) m).getSource());
} else if (m instanceof Node) {
/* nodes are more complicated, first we must decide whether to mutate the function
* or a connection
* we do this by generating a random int between 0 and 1 + arity
*/
- int geneType = resources.getRandomInt(1 + resources.arity());
+ int geneType = getResources().getRandomInt(1 + getResources().arity());
// if the int is less than 1, mutate function, else mutate connections
if (geneType < 1) {
- if (report.get()) resources.report("changed function from " + ((Node) m).getFunction() + " ");
+ if (report.get()) getResources().report("changed function from " + ((Node) m).getFunction() + " ");
- ((Node) m).setFunction(resources.getRandomFunction());
+ ((Node) m).setFunction(getResources().getRandomFunction());
- if (report.get()) resources.reportln("to " + ((Node) m).getFunction());
+ if (report.get()) getResources().reportln("to " + ((Node) m).getFunction());
} else {
// if we decided to mutate connection, subtract 1 from geneType so it fits into the arity range
geneType -= 1;
- if (report.get()) resources.report("changed connection " + geneType + " from " + ((Node) m).getConnection(geneType) + " ");
+ if (report.get()) getResources().report("changed connection " + geneType + " from " + ((Node) m).getConnection(geneType) + " ");
m.setConnection(geneType, chromosome.getRandomConnection(((Node) m).getColumn()));
- if (report.get()) resources.reportln("to " + ((Node) m).getConnection(geneType));
+ if (report.get()) getResources().reportln("to " + ((Node) m).getConnection(geneType));
}
}
}
diff --git a/src/jcgp/backend/modules/mutator/ProbabilisticMutator.java b/src/jcgp/backend/modules/mutator/ProbabilisticMutator.java
index c65fc22..9273558 100644
--- a/src/jcgp/backend/modules/mutator/ProbabilisticMutator.java
+++ b/src/jcgp/backend/modules/mutator/ProbabilisticMutator.java
@@ -25,17 +25,14 @@ public class ProbabilisticMutator extends Mutator {
private DoubleParameter mutationProbability;
private BooleanParameter report;
-
- private Resources resources;
-
+
/**
* Creates a new instance of ProbabilisticMutator.
*
* @param resources a reference to the experiment's resources.
*/
public ProbabilisticMutator(Resources resources) {
- super();
- this.resources = resources;
+ super(resources);
mutationProbability = new DoubleParameter(10, "Mutation probability", false, false) {
@Override
@@ -56,53 +53,53 @@ public class ProbabilisticMutator extends Mutator {
}
@Override
- public void mutate(Chromosome chromosome, Resources resources) {
- if (report.get()) resources.reportln("[Mutator] Starting mutations");
+ public void mutate(Chromosome chromosome) {
+ if (report.get()) getResources().reportln("[Mutator] Starting mutations");
// go through nodes - [rows][columns]
- for (int r = 0; r < resources.rows(); r++) {
- for (int c = 0; c < resources.columns(); c++) {
+ for (int r = 0; r < getResources().rows(); r++) {
+ for (int c = 0; c < getResources().columns(); c++) {
// go through all connections
- for (int a = 0; a < resources.arity(); a++) {
+ for (int a = 0; a < getResources().arity(); a++) {
if (mutateGene()) {
Node n = chromosome.getNode(r, c);
- if (report.get()) resources.report("[Mutator] Mutating " + n +
+ if (report.get()) getResources().report("[Mutator] Mutating " + n +
", changed connection " + a + " from " + n.getConnection(a) + " ");
n.setConnection(a, chromosome.getRandomConnection(c));
- if (report.get()) resources.reportln("to " + n.getConnection(a));
+ if (report.get()) getResources().reportln("to " + n.getConnection(a));
}
}
// deal with node function next
if (mutateGene()) {
Node n = chromosome.getNode(r, c);
- if (report.get()) resources.report("[Mutator] Mutating " + n +
+ if (report.get()) getResources().report("[Mutator] Mutating " + n +
", changed function from " + n.getFunction());
- n.setFunction(resources.getRandomFunction());
+ n.setFunction(getResources().getRandomFunction());
- if (report.get()) resources.reportln(" to " + n.getFunction());
+ if (report.get()) getResources().reportln(" to " + n.getFunction());
}
}
}
// finally, mutate outputs
- for (int o = 0; o < resources.outputs(); o++) {
+ for (int o = 0; o < getResources().outputs(); o++) {
if (mutateGene()) {
Output out = chromosome.getOutput(o);
- if (report.get()) resources.report("[Mutator] Mutating " + out +
+ if (report.get()) getResources().report("[Mutator] Mutating " + out +
", changed source from " + out.getSource());
out.setConnection(0, chromosome.getRandomConnection());
- if (report.get()) resources.reportln("to " + out.getSource());
+ if (report.get()) getResources().reportln("to " + out.getSource());
}
}
- if (report.get()) resources.reportln("[Mutator] Mutation finished ");
+ if (report.get()) getResources().reportln("[Mutator] Mutation finished ");
}
@@ -115,6 +112,6 @@ public class ProbabilisticMutator extends Mutator {
* @return true if a mutation should be performed, false if otherwise.
*/
private boolean mutateGene() {
- return resources.getRandomDouble(100) < mutationProbability.get();
+ return getResources().getRandomDouble(100) < mutationProbability.get();
}
}
diff --git a/src/jcgp/backend/modules/problem/DigitalCircuitProblem.java b/src/jcgp/backend/modules/problem/DigitalCircuitProblem.java
index 6654928..0071ed5 100644
--- a/src/jcgp/backend/modules/problem/DigitalCircuitProblem.java
+++ b/src/jcgp/backend/modules/problem/DigitalCircuitProblem.java
@@ -2,7 +2,6 @@ package jcgp.backend.modules.problem;
import jcgp.backend.function.DigitalCircuitFunctions;
import jcgp.backend.function.UnsignedInteger;
-import jcgp.backend.population.Chromosome;
import jcgp.backend.population.Population;
import jcgp.backend.resources.Resources;
@@ -18,7 +17,7 @@ import jcgp.backend.resources.Resources;
*
*/
public class DigitalCircuitProblem extends TestCaseProblem<UnsignedInteger> {
-
+
/**
* Construct a new instance of DigitalCircuitProblem.
*
@@ -30,43 +29,38 @@ public class DigitalCircuitProblem extends TestCaseProblem<UnsignedInteger> {
setName("Digital circuit");
setFileExtension(".plu");
}
-
+
@Override
public void evaluate(Population population, Resources resources) {
// for every chromosome in the population
for (int i = 0; i < resources.populationSize(); i++) {
// assume an initial fitness of 0
int fitness = 0;
-
+
// iterate over every test case
for (int t = 0; t < testCases.size(); t++) {
- population.getChromosome(i).setInputs((Object[]) testCases.get(t).getInputs());
+ population.get(i).setInputs((Object[]) testCases.get(t).getInputs());
// check each output
for (int o = 0; o < resources.outputs(); o++) {
- Integer output = ((UnsignedInteger) population.getChromosome(i).getOutput(o).calculate()).get();
+ Integer output = ((UnsignedInteger) population.get(i).getOutput(o).calculate()).get();
Integer matches = ~(output ^ testCases.get(t).getOutput(o).get());
// check only the relevant bits
int bits = (int) Math.pow(2.0, (double) resources.inputs());
for (int b = 0; b < bits; b++) {
- if (((matches >>> b) & 1) == 1) {
- fitness++;
- }
+ fitness += (matches >>> b) & 1;
}
}
}
-
+
// assign the resulting fitness to the respective individual
- population.getChromosome(i).setFitness(fitness);
+ population.get(i).setFitness(fitness);
}
-
- // sort population
- population.sortAscending();
}
-
+
@Override
protected double getMaxFitness() {
// calculate the fitness by looking at inputs, not number of test cases
- double maxFitness = Math.pow(2.0, (double) resources.inputs()) * resources.outputs();
+ double maxFitness = Math.pow(2.0, (double) getResources().inputs()) * getResources().outputs();
return maxFitness;
}
@@ -81,24 +75,30 @@ public class DigitalCircuitProblem extends TestCaseProblem<UnsignedInteger> {
for (int o = 0; o < outputCases.length; o++) {
outputCases[o] = new UnsignedInteger(outputs[o]);
}
-
+
return new TestCase<UnsignedInteger>(inputCases, outputCases);
}
-
+
@Override
- public boolean isPerfectSolution(Chromosome fittest) {
+ public int perfectSolutionFound(Population population) {
// higher fitness is better
- return fittest.getFitness() >= maxFitness.get();
+ for (int i = 0; i < getResources().populationSize(); i++) {
+ if (population.get(i).getFitness() >= maxFitness.get()) {
+ return i;
+ }
+ }
+ return -1;
}
@Override
- public boolean isImprovement(Chromosome fittest) {
+ public int hasImprovement(Population population) {
// higher fitness is better
- if (fittest.getFitness() > bestFitness.get()) {
- bestFitness.set(fittest.getFitness());
- return true;
- } else {
- return false;
+ for (int i = 0; i < getResources().populationSize(); i++) {
+ if (population.get(i).getFitness() > bestFitness.get()) {
+ bestFitness.set(population.get(i).getFitness());
+ return i;
+ }
}
+ return -1;
}
}
diff --git a/src/jcgp/backend/modules/problem/Problem.java b/src/jcgp/backend/modules/problem/Problem.java
index 721b9b3..5f194b5 100644
--- a/src/jcgp/backend/modules/problem/Problem.java
+++ b/src/jcgp/backend/modules/problem/Problem.java
@@ -6,23 +6,21 @@ 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.Chromosome;
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 Problem
+ * 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.
* <br>
- * By convention, the population should be sorted into ascending order of fitness. The
- * reason for this is because high fitness is not necessarily better - some problem types
- * might treat 0 as the best fitness. In order for the evolutionary strategy to be able to
- * pick chromosomes by fitness, the safest way is to sort them such that the last chromosome
- * is the fittest.
+ * 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.
* <br><br>
- * When extending this class, the constructor should call a couple methods in order to
- * properly construct the problem type: {@code setFunctionSet()} and {@code setFileExtension()},
+ * 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.
* <br><br>
@@ -41,12 +39,18 @@ 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.
*/
- public Problem() {
+ public Problem(Resources resources) {
+ super(resources);
+
maxFitness = new DoubleMonitor(0, "Max fitness");
bestFitness = new DoubleMonitor(0, "Best fitness");
registerParameters(maxFitness, bestFitness);
@@ -73,29 +77,26 @@ public abstract class Problem extends Module {
public abstract void evaluate(Population population, Resources resources);
/**
- * Used to assert whether a given chromosome is a perfect solution
+ * 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.
- * <br><br>
- * Note that if this method returns true, the experiment will move on
- * to the next run, or finish if no more runs are left.
*
- * @param candidate the potentially perfect chromosome.
- * @return true if the argument is a perfect solution.
+ * @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 boolean isPerfectSolution(Chromosome candidate);
+ public abstract int perfectSolutionFound(Population population);
/**
- * Used to assert whether a given chromosome is an improvement over
+ * 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 candidate the potentially fitter chromosome.
- * @return true if the argument is fitter than the currently fittest chromosome.
+ * @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 boolean isImprovement(Chromosome candidate);
+ public abstract int hasImprovement(Population population);
/**
* Parses the specified file and uses the parsed data to
@@ -149,6 +150,20 @@ public abstract class Problem extends Module {
}
/**
+ * @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.
*/
diff --git a/src/jcgp/backend/modules/problem/SymbolicRegressionProblem.java b/src/jcgp/backend/modules/problem/SymbolicRegressionProblem.java
index 04e9fe8..24c61d6 100644
--- a/src/jcgp/backend/modules/problem/SymbolicRegressionProblem.java
+++ b/src/jcgp/backend/modules/problem/SymbolicRegressionProblem.java
@@ -4,7 +4,6 @@ import jcgp.backend.function.SymbolicRegressionFunctions;
import jcgp.backend.parameters.BooleanParameter;
import jcgp.backend.parameters.DoubleParameter;
import jcgp.backend.parameters.ParameterStatus;
-import jcgp.backend.population.Chromosome;
import jcgp.backend.population.Population;
import jcgp.backend.resources.Resources;
@@ -20,12 +19,12 @@ import jcgp.backend.resources.Resources;
* <li>Error threshold: the maximum difference allowed between an
* evolved output and the equivalent output from the problem data.
* Outputs within the error threshold will be considered correct.
- * This is only used if HITS is enabled.</li>
+ * This is only used if hits is enabled.</li>
* <li>Perfection threshold: if the fitness is calculated without
- * using the HITS method, it is a decimal value. A solution is
+ * using the hits method, it is a decimal value. A solution is
* considered perfect when the difference between its fitness and
* the maximum possible fitness is within the perfection threshold.</li>
- * <li>HITS-based fitness: increment the fitness by 1 whenever the
+ * <li>Hits-based fitness: increment the fitness by 1 whenever the
* chromosome output is within the error threshold.</li></ul>
*
*
@@ -79,7 +78,7 @@ public class SymbolicRegressionProblem extends TestCaseProblem<Double> {
}
};
- hitsBasedFitness = new BooleanParameter(true, "HITS-based fitness");
+ hitsBasedFitness = new BooleanParameter(false, "Hits-based fitness");
registerParameters(errorThreshold, perfectionThreshold, hitsBasedFitness);
}
@@ -87,15 +86,15 @@ public class SymbolicRegressionProblem extends TestCaseProblem<Double> {
@Override
public void evaluate(Population population, Resources resources) {
// for every chromosome in the population
- for (int i = 0; i < resources.populationSize(); i++) {
+ for (int i = 0; i < getResources().populationSize(); i++) {
// assume an initial fitness of 0
double fitness = 0;
// for each test case
for (int t = 0; t < testCases.size(); t++) {
- population.getChromosome(i).setInputs((Object[]) testCases.get(t).getInputs());
+ population.get(i).setInputs((Object[]) testCases.get(t).getInputs());
// check each output
- for (int o = 0; o < resources.outputs(); o++) {
- Double cgpValue = (Double) population.getChromosome(i).getOutput(o).calculate();
+ for (int o = 0; o < getResources().outputs(); o++) {
+ Double cgpValue = (Double) population.get(i).getOutput(o).calculate();
Double dataValue = testCases.get(t).getOutput(o);
if (hitsBasedFitness.get()) {
if (Math.abs(cgpValue - dataValue) <= errorThreshold.get()) {
@@ -107,11 +106,8 @@ public class SymbolicRegressionProblem extends TestCaseProblem<Double> {
}
}
// assign the resulting fitness to the respective individual
- population.getChromosome(i).setFitness(fitness);
+ population.get(i).setFitness(fitness);
}
-
- // sort population
- population.sortAscending();
}
@Override
@@ -130,19 +126,27 @@ public class SymbolicRegressionProblem extends TestCaseProblem<Double> {
}
@Override
- public boolean isPerfectSolution(Chromosome fittest) {
+ public int perfectSolutionFound(Population population) {
// higher fitness is better
- return fittest.getFitness() >= maxFitness.get() - perfectionThreshold.get();
+ for (int i = 0; i < getResources().populationSize(); i++) {
+ if (population.get(i).getFitness() >= maxFitness.get() - perfectionThreshold.get()) {
+ return i;
+ }
+ }
+ return -1;
}
@Override
- public boolean isImprovement(Chromosome fittest) {
+ public int hasImprovement(Population population) {
// higher fitness is better
- if (fittest.getFitness() > bestFitness.get()) {
- bestFitness.set(fittest.getFitness());
- return true;
- } else {
- return false;
+ for (int i = 0; i < getResources().populationSize(); i++) {
+ System.out.println("checking for improvement");
+ if (population.get(i).getFitness() > bestFitness.get()) {
+ System.out.println("found a better chr, " + i);
+ bestFitness.set(population.get(i).getFitness());
+ return i;
+ }
}
+ return -1;
}
}
diff --git a/src/jcgp/backend/modules/problem/TestCaseProblem.java b/src/jcgp/backend/modules/problem/TestCaseProblem.java
index c11fab4..69c078d 100644
--- a/src/jcgp/backend/modules/problem/TestCaseProblem.java
+++ b/src/jcgp/backend/modules/problem/TestCaseProblem.java
@@ -30,7 +30,7 @@ public abstract class TestCaseProblem<T> extends Problem {
* contains arrays of inputs and outputs and associated getters.
*
* @author Eduardo Pedroni
- * @param <U>
+ * @param <U> the data type of the test case.
*/
public static class TestCase<U> {
private U[] inputs;
@@ -80,7 +80,6 @@ public abstract class TestCaseProblem<T> extends Problem {
}
protected ObservableList<TestCase<T>> testCases;
- protected Resources resources;
/**
* Creates a new TestCaseProblem object.
@@ -88,8 +87,7 @@ public abstract class TestCaseProblem<T> extends Problem {
* @param resources a reference to the experiment's resources.
*/
public TestCaseProblem(Resources resources) {
- super();
- this.resources = resources;
+ super(resources);
testCases = FXCollections.observableArrayList();
}
@@ -139,12 +137,12 @@ public abstract class TestCaseProblem<T> extends Problem {
public final void addTestCase(String[] inputs, String[] outputs) {
TestCase<T> testCase = parseTestCase(inputs, outputs);
- if (testCase.getInputs().length != resources.inputs()) {
+ if (testCase.getInputs().length != getResources().inputs()) {
throw new IllegalArgumentException("Received test case with " + testCase.getInputs().length +
- " inputs but need exactly " + resources.inputs());
- } else if (testCase.getOutputs().length != resources.outputs()) {
+ " inputs but need exactly " + getResources().inputs());
+ } else if (testCase.getOutputs().length != getResources().outputs()) {
throw new IllegalArgumentException("Received test case with " + testCase.getOutputs().length +
- " outputs but need exactly " + resources.outputs());
+ " outputs but need exactly " + getResources().outputs());
} else {
this.testCases.add(testCase);
maxFitness.set(getMaxFitness());
diff --git a/src/jcgp/backend/modules/problem/TravellingSalesmanProblem.java b/src/jcgp/backend/modules/problem/TravellingSalesmanProblem.java
deleted file mode 100644
index 94417ae..0000000
--- a/src/jcgp/backend/modules/problem/TravellingSalesmanProblem.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package jcgp.backend.modules.problem;
-
-import java.io.File;
-
-import jcgp.backend.function.TravellingSalesmanFunctions;
-import jcgp.backend.population.Chromosome;
-import jcgp.backend.population.Population;
-import jcgp.backend.resources.ModifiableResources;
-import jcgp.backend.resources.Resources;
-
-/**
- * Travelling salesman problem
- * <br><br>
- * Using this problem type, travelling salesman tours can be evolved.
- * {@code parseData()} must be used to load the desired city
- * coordinates in the standard .tsp format.
- *
- * @see TravellingSalesmanFunctions
- * @author Eduardo Pedroni
- *
- */
-public class TravellingSalesmanProblem extends Problem {
-
- /**
- * Construct a new instance of TravellingSalesmanProblem.
- *
- * @param resources a reference to the experiment's resources.
- */
- public TravellingSalesmanProblem(Resources resources) {
- setFunctionSet(new TravellingSalesmanFunctions());
- setName("Travelling salesman");
- setFileExtension(".tsp");
- }
-
- @Override
- public void evaluate(Population population, Resources resources) {
- // TODO Auto-generated method stub
- }
-
- @Override
- public boolean isPerfectSolution(Chromosome fittest) {
- // TODO Auto-generated method stub
- return false;
- }
-
- @Override
- public void parseProblemData(File file, ModifiableResources resources) {
- // TODO Auto-generated method stub
- }
-
- @Override
- public boolean isImprovement(Chromosome fittest) {
- // TODO Auto-generated method stub
- return false;
- }
-}
diff --git a/src/jcgp/backend/parameters/BooleanParameter.java b/src/jcgp/backend/parameters/BooleanParameter.java
index f0abe50..8c869ac 100644
--- a/src/jcgp/backend/parameters/BooleanParameter.java
+++ b/src/jcgp/backend/parameters/BooleanParameter.java
@@ -1,6 +1,7 @@
package jcgp.backend.parameters;
import javafx.beans.property.SimpleBooleanProperty;
+import jcgp.backend.parameters.monitors.BooleanMonitor;
/**
* Parameter subclass for the boolean type. Most of the
diff --git a/src/jcgp/backend/parameters/DoubleParameter.java b/src/jcgp/backend/parameters/DoubleParameter.java
index 14b3151..235848f 100644
--- a/src/jcgp/backend/parameters/DoubleParameter.java
+++ b/src/jcgp/backend/parameters/DoubleParameter.java
@@ -1,6 +1,7 @@
package jcgp.backend.parameters;
import javafx.beans.property.SimpleDoubleProperty;
+import jcgp.backend.parameters.monitors.DoubleMonitor;
/**
* Parameter subclass for the double type. Most of the
diff --git a/src/jcgp/backend/parameters/IntegerParameter.java b/src/jcgp/backend/parameters/IntegerParameter.java
index d0d7328..2c3723a 100644
--- a/src/jcgp/backend/parameters/IntegerParameter.java
+++ b/src/jcgp/backend/parameters/IntegerParameter.java
@@ -1,6 +1,7 @@
package jcgp.backend.parameters;
import javafx.beans.property.SimpleIntegerProperty;
+import jcgp.backend.parameters.monitors.IntegerMonitor;
/**
* Parameter subclass for the double type. Most of the
diff --git a/src/jcgp/backend/parameters/Parameter.java b/src/jcgp/backend/parameters/Parameter.java
index c04dee9..c605180 100644
--- a/src/jcgp/backend/parameters/Parameter.java
+++ b/src/jcgp/backend/parameters/Parameter.java
@@ -34,7 +34,7 @@ import javafx.beans.property.ReadOnlyProperty;
* will likely have different validity criteria. The type of status is
* {@link ParameterStatus}, an enum type defining all valid states.
*
- * @see Module, ParameterStatus
+ * @see jcgp.backend.modules.Module
* @author Eduardo Pedroni
* @param <T> the data type stored in the parameter.
*/
diff --git a/src/jcgp/backend/parameters/ParameterStatus.java b/src/jcgp/backend/parameters/ParameterStatus.java
index 4041cad..558f57f 100644
--- a/src/jcgp/backend/parameters/ParameterStatus.java
+++ b/src/jcgp/backend/parameters/ParameterStatus.java
@@ -11,6 +11,7 @@ package jcgp.backend.parameters;
* <li>WARNING_RESET: the new parameter value is technically valid
* but will require a reset.</li>
* <li>VALID: the new value is valid.</li>
+ * </ul>
* <br><br>
* The above definitions are final in the sense that they outline
* how parameters are treated by the program depending on their
diff --git a/src/jcgp/backend/parsers/ChromosomeParser.java b/src/jcgp/backend/parsers/ChromosomeParser.java
index c35b5b9..f5f25ae 100644
--- a/src/jcgp/backend/parsers/ChromosomeParser.java
+++ b/src/jcgp/backend/parsers/ChromosomeParser.java
@@ -137,8 +137,9 @@ public abstract class ChromosomeParser {
*
* @param file the file to write to
* @param chromosome the chromosome to save
+ * @param resources a reference to the experiment's resources.
*/
- public static void save(File file, Chromosome chromosome, ModifiableResources resources) {
+ public static void save(File file, Chromosome chromosome, Resources resources) {
PrintWriter writer;
try {
writer = new PrintWriter(file);
diff --git a/src/jcgp/backend/population/Chromosome.java b/src/jcgp/backend/population/Chromosome.java
index b99b817..0dfa801 100644
--- a/src/jcgp/backend/population/Chromosome.java
+++ b/src/jcgp/backend/population/Chromosome.java
@@ -159,7 +159,8 @@ public class Chromosome implements Comparable<Chromosome> {
* this instance. In practice, this iterates through the
* entire chromosome making equivalent connections and
* setting functions to the same values as those in the
- * specified chromosome.
+ * specified chromosome. It also sets the fitness of the
+ * copy to the same value as the original.
* <br>
* It is assumed that both chromosomes have the same
* topology; while this method will still run if that is not
@@ -205,6 +206,9 @@ public class Chromosome implements Comparable<Chromosome> {
System.out.println("Warning: Connection of subtype " + copyOutput.getClass().toString() + " is not explicitly handled by copy constructor.");
}
}
+
+ // copy fitness as well
+ this.fitness = clone.getFitness();
}
/**
@@ -264,7 +268,6 @@ public class Chromosome implements Comparable<Chromosome> {
* number of inputs exactly, an exception is thrown.
*
* @param values the values the input should take.
- * @throws ParameterMismatchException if the wrong number of values is received.
*/
public void setInputs(Object ... values) {
// if the values provided don't match the specified number of inputs, the user should be warned
diff --git a/src/jcgp/backend/population/MutableElement.java b/src/jcgp/backend/population/MutableElement.java
index 33f3890..5782a99 100644
--- a/src/jcgp/backend/population/MutableElement.java
+++ b/src/jcgp/backend/population/MutableElement.java
@@ -44,7 +44,7 @@ public interface MutableElement {
* <li>not reflexive: a.copyOf(a) returns false;</li>
* <li>not transitive: if a.copyOf(b) is true and b.copyOf(c) is true, a.copyOf(c) is
* not necessarily true since it is possible that a == c.</li>
- *
+ * </ul>
* @param element the mutable element to compare to.
* @return true if {@code element} is a copy of this element.
*/
diff --git a/src/jcgp/backend/population/Population.java b/src/jcgp/backend/population/Population.java
index 5d549e9..70cfda8 100644
--- a/src/jcgp/backend/population/Population.java
+++ b/src/jcgp/backend/population/Population.java
@@ -3,6 +3,7 @@ package jcgp.backend.population;
import java.util.Arrays;
import java.util.Collections;
+import jcgp.backend.modules.problem.BestFitness;
import jcgp.backend.resources.Resources;
/**
@@ -19,16 +20,6 @@ import jcgp.backend.resources.Resources;
* experiment's specified seed. If an entirely random population
* is needed, {@code reinitialise()} should be used to randomise
* all chromosomes without creating a new instance of {@code Population}.
- * <br><br>
- * By convention the population's chromosomes should always be sorted in
- * order of fitness, from worst to best. This cannot be done automatically
- * since a higher fitness value does not necessarily mean better fitness;
- * some problem types might instead interpret fitness 0 as a perfect solution.
- * Sorting the population is done easily using {@code sortAscending()} and
- * {@code sortDescending()}, and should be done by the problem type as appropriate.
- * It is <b>critical</b> to sort the population after it is evaluated as
- * evolutionary strategies will obey the convention and assume the population
- * is sorted in order of fitness.
*
*
* @author Eduardo Pedroni
@@ -75,7 +66,7 @@ public class Population {
* @param index the chromosome to return.
* @return the indexed chromosome.
*/
- public Chromosome getChromosome(int index) {
+ public Chromosome get(int index) {
return chromosomes[index];
}
@@ -112,46 +103,19 @@ public class Population {
chromosomes[c].reinitialiseConnections();
}
}
-
- /**
- * The fittest chromosome, by convention, is the last one
- * in the array. Problem evaluation methods are expected to
- * sort the population into ascending order of fitness, such
- * that the best chromosome is in the last position (size - 1).
- * This method assumes that the population is sorted as such
- * and returns the last chromosome in the array.
- *
- * @return the fittest chromosome in the population.
- */
- public Chromosome getFittest() {
- return chromosomes[chromosomes.length - 1];
- }
-
- /**
- * The fittest chromosome, by convention, is the last one
- * in the array. Problem evaluation methods are expected to
- * sort the population into ascending order of fitness, such
- * that the best chromosome is in the last position (size - 1).
- * This method assumes that the population is sorted as such
- * and returns the last index in the array.
- *
- * @return the index of the fittest chromosome.
- */
- public int getFittestIndex() {
- return chromosomes.length - 1;
- }
-
- /**
- * Sort the population into ascending order of fitness.
- */
- public void sortAscending() {
- Arrays.sort(chromosomes);
- }
-
+
/**
- * Sort the population into descending order of fitness.
+ * Sorts the population in ascending order of fitness quality.
+ * What this means is that the best fitness chromosome will be
+ * in the last position, even though it might have the lowest
+ * fitness value. Fitness orientation as specified in the resources
+ * is respected.
*/
- public void sortDescending() {
- Arrays.sort(chromosomes, Collections.reverseOrder());
+ public void sort() {
+ if (resources.fitnessOrientation() == BestFitness.HIGH) {
+ Arrays.sort(chromosomes);
+ } else {
+ Arrays.sort(chromosomes, Collections.reverseOrder());
+ }
}
}
diff --git a/src/jcgp/backend/resources/ModifiableResources.java b/src/jcgp/backend/resources/ModifiableResources.java
index 3dab2aa..5df26e2 100644
--- a/src/jcgp/backend/resources/ModifiableResources.java
+++ b/src/jcgp/backend/resources/ModifiableResources.java
@@ -3,6 +3,7 @@ package jcgp.backend.resources;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import jcgp.backend.function.FunctionSet;
+import jcgp.backend.modules.problem.BestFitness;
import jcgp.backend.parameters.IntegerParameter;
import jcgp.backend.parameters.ParameterStatus;
import jcgp.backend.parameters.monitors.IntegerMonitor;
@@ -137,6 +138,13 @@ public class ModifiableResources extends Resources {
}
/**
+ * @param newOrientation the new orientation to set.
+ */
+ public void setFitnessOrientation(BestFitness newOrientation) {
+ this.fitnessOrientation = newOrientation;
+ }
+
+ /**
* @return the rows parameter.
*/
public IntegerParameter getRowsParameter() {
diff --git a/src/jcgp/backend/resources/Resources.java b/src/jcgp/backend/resources/Resources.java
index ebd6fb2..59a29ea 100644
--- a/src/jcgp/backend/resources/Resources.java
+++ b/src/jcgp/backend/resources/Resources.java
@@ -4,6 +4,7 @@ import java.util.Random;
import jcgp.backend.function.Function;
import jcgp.backend.function.FunctionSet;
+import jcgp.backend.modules.problem.BestFitness;
import jcgp.backend.parameters.IntegerParameter;
/**
@@ -15,6 +16,8 @@ import jcgp.backend.parameters.IntegerParameter;
* the actual parameter values can be obtained using getter methods. Note that, for code brevity,
* this class's getters do not start with the word "get". For instance, to get the number of rows,
* one would use {@code rows()} instead of {@code getRows()} which doesn't exist.
+ * The fitness orientation of the problem being solved can also be retrieved using {@code fitnessOrientation()}.
+ * Evolutionary strategies will typically use this to perform selection.
* <br><br>
* In addition to parameters, this class also offers utility methods. Any necessary random numbers
* should be obtained using {@code getRandomInt()} and {@code getRandomDouble()} as these methods
@@ -23,7 +26,7 @@ import jcgp.backend.parameters.IntegerParameter;
* Finally, printing to the console should be done via the resources using the report and print
* methods, so that these prints also get sent to the GUI console (if one is present).
*
- * @see Parameter, Console, FunctionSet
+ * @see jcgp.backend.parameters.Parameter
* @author Eduardo Pedroni
*
*/
@@ -37,6 +40,8 @@ public class Resources {
protected Console console;
+ protected BestFitness fitnessOrientation;
+
/**
* @return the number of rows.
*/
@@ -135,6 +140,13 @@ public class Resources {
return reportInterval.get();
}
+ /**
+ * @return the fitness orientation.
+ */
+ public BestFitness fitnessOrientation() {
+ return fitnessOrientation;
+ }
+
/*
* Utility functions
*/
diff --git a/src/jcgp/backend/tests/PopulationTests.java b/src/jcgp/backend/tests/PopulationTests.java
index 2f36ce1..21789d7 100644
--- a/src/jcgp/backend/tests/PopulationTests.java
+++ b/src/jcgp/backend/tests/PopulationTests.java
@@ -46,7 +46,7 @@ public class PopulationTests {
int chromosomes = 0;
while (true) {
try {
- population.getChromosome(chromosomes);
+ population.get(chromosomes);
} catch (IndexOutOfBoundsException e) {
break;
}
@@ -64,6 +64,6 @@ public class PopulationTests {
// initialise a population with a copy of it
population = new Population(oc, resources);
// check that the first parent chromosome is identical to, but not the same instance as, the one given
- assertTrue("Incorrect chromosome in population.", population.getChromosome(0).compareGenesTo(oc) && population.getChromosome(0) != oc);
+ assertTrue("Incorrect chromosome in population.", population.get(0).compareGenesTo(oc) && population.get(0) != oc);
}
}
diff --git a/src/jcgp/gui/GUI.java b/src/jcgp/gui/GUI.java
index cd4778f..437a739 100644
--- a/src/jcgp/gui/GUI.java
+++ b/src/jcgp/gui/GUI.java
@@ -257,4 +257,8 @@ public class GUI extends Application {
public void flushConsole() {
console.flush();
}
+
+ public int getChromosomeIndex() {
+ return populationPane.getSelectionModel().getSelectedIndex();
+ }
}
diff --git a/src/jcgp/gui/console/GUIConsole.java b/src/jcgp/gui/console/GUIConsole.java
index de4b378..b3d037c 100644
--- a/src/jcgp/gui/console/GUIConsole.java
+++ b/src/jcgp/gui/console/GUIConsole.java
@@ -15,12 +15,25 @@ import javafx.scene.layout.AnchorPane;
import jcgp.backend.resources.Console;
import jcgp.gui.GUI;
+/**
+ * Console pane used by the GUI to display CGP output messages.
+ * This class realises {@code Console}. It consists of a JavaFX
+ * {@code TextArea} and a {@code StringBuffer}. The buffer is filled
+ * as print messages are queued. Calling {@code flush()} writes the
+ * contents of the buffer to the {@code TextArea} and empties the buffer.
+ *
+ * @see Console
+ * @author Eduardo Pedroni
+ *
+ */
public class GUIConsole extends AnchorPane implements Console {
private TextArea textArea = new TextArea("Welcome to JCGP!\n");
-
private StringBuffer printBuffer = new StringBuffer();
+ /**
+ * Creates a new instance of this class.
+ */
public GUIConsole() {
super();
textArea.setEditable(false);
@@ -31,7 +44,10 @@ public class GUIConsole extends AnchorPane implements Console {
* This has not been fixed as of 8/4/2014.
*
* The following code modifies the EventDispatcher to consume the right mouse
- * button click, preventing the default menu from appearing.
+ * button click, preventing the default menu from appearing. It propagates the mouse
+ * click further so other elements will respond appropriately.
+ *
+ * TODO this should be refactored once the API is updated.
*/
final EventDispatcher initial = textArea.getEventDispatcher();
textArea.setEventDispatcher(new EventDispatcher() {
@@ -76,6 +92,7 @@ public class GUIConsole extends AnchorPane implements Console {
new SeparatorMenuItem(),
clearConsole));
+ // anchor the text area so it resizes automatically
AnchorPane.setTopAnchor(textArea, GUI.RESIZE_MARGIN);
AnchorPane.setBottomAnchor(textArea, 0.0);
AnchorPane.setRightAnchor(textArea, 0.0);
diff --git a/src/jcgp/gui/dragresize/HorizontalDragResize.java b/src/jcgp/gui/dragresize/HorizontalDragResize.java
index 84c322f..b618b74 100644
--- a/src/jcgp/gui/dragresize/HorizontalDragResize.java
+++ b/src/jcgp/gui/dragresize/HorizontalDragResize.java
@@ -7,9 +7,11 @@ import javafx.scene.layout.Region;
import jcgp.gui.GUI;
/**
- *
- * Decorator pattern.
- *
+ * This class adds horizontal drag resize functionality to any
+ * arbitrary region provided. This is done by using the static
+ * method {@code makeDragResizable()}.
+ * <br><br>
+ * This is based on a class by Andrew Till found on:
* http://andrewtill.blogspot.co.uk/2012/12/dragging-to-resize-javafx-region.html
*
*/
@@ -18,13 +20,29 @@ public class HorizontalDragResize {
private boolean dragging = false;
private final Region region;
+ /**
+ * For internal use only, creates an instance of the actual
+ * resizer used.
+ *
+ * @param region the region to make resizable.
+ */
private HorizontalDragResize(Region region) {
this.region = region;
}
+ /**
+ * Makes the specified region drag resizable.
+ * This particular implementation only creates a resize
+ * click-and-drag area on the left side of the region.
+ * The resize area is defined by {@code GUI.RESIZE_MARGIN}.
+ *
+ * @param region the region to make resizable.
+ */
public static void makeDragResizable(final Region region) {
+ // make the instance, this actually performs the resizing
final HorizontalDragResize dr = new HorizontalDragResize(region);
+ // set mouse listeners
region.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
@@ -52,12 +70,22 @@ public class HorizontalDragResize {
}
+ /**
+ * If the press happened in the resize area, raise the drag flag.
+ *
+ * @param event the associated mouse event.
+ */
private void mousePressed(MouseEvent event) {
if(isInDraggableZone(event)) {
dragging = true;
}
}
+ /**
+ * If drag flag is high, resize the region to match the mouse position.
+ *
+ * @param event the associated mouse event.
+ */
private void mouseDragged(MouseEvent event) {
if(dragging) {
double newWidth = region.getWidth() - event.getX();
@@ -69,20 +97,33 @@ public class HorizontalDragResize {
}
}
+ /**
+ * Change the cursor if the mouse position overlaps with the resize area.
+ *
+ * @param event the associated mouse event.
+ */
private void mouseMoved(MouseEvent event) {
if(isInDraggableZone(event) || dragging) {
region.setCursor(Cursor.H_RESIZE);
- }
- else {
+ } else {
region.setCursor(Cursor.DEFAULT);
}
}
+ /**
+ * Finish resizing.
+ */
private void mouseReleased() {
dragging = false;
region.setCursor(Cursor.DEFAULT);
}
+ /**
+ * Assert whether the mouse cursor is in the draggable area defined by {@code GUI.RESIZE_MARGIN}.
+ *
+ * @param event the associated mouse event.
+ * @return true if the mouse position is in the draggable area.
+ */
private boolean isInDraggableZone(MouseEvent event) {
return event.getX() < (GUI.RESIZE_MARGIN);
}
diff --git a/src/jcgp/gui/dragresize/VerticalDragResize.java b/src/jcgp/gui/dragresize/VerticalDragResize.java
index 9397e5d..06186c6 100644
--- a/src/jcgp/gui/dragresize/VerticalDragResize.java
+++ b/src/jcgp/gui/dragresize/VerticalDragResize.java
@@ -7,7 +7,11 @@ import javafx.scene.layout.Region;
import jcgp.gui.GUI;
/**
- *
+ * This class adds vertical drag resize functionality to any
+ * arbitrary region provided. This is done by using the static
+ * method {@code makeDragResizable()}.
+ * <br><br>
+ * This is based on a class by Andrew Till found on:
* http://andrewtill.blogspot.co.uk/2012/12/dragging-to-resize-javafx-region.html
*
*/
@@ -16,13 +20,29 @@ public class VerticalDragResize {
private boolean dragging = false;
private final Region region;
+ /**
+ * For internal use only, creates an instance of the actual
+ * resizer used.
+ *
+ * @param region the region to make resizable.
+ */
private VerticalDragResize(Region region) {
this.region = region;
}
+ /**
+ * Makes the specified region drag resizable.
+ * This particular implementation only creates a resize
+ * click-and-drag area on the top side of the region.
+ * The resize area is defined by {@code GUI.RESIZE_MARGIN}.
+ *
+ * @param region the region to make resizable.
+ */
public static void makeDragResizable(final Region region) {
+ // make the instance, this actually performs the resizing
final VerticalDragResize dr = new VerticalDragResize(region);
+ // set mouse listeners
region.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
@@ -50,12 +70,22 @@ public class VerticalDragResize {
}
+ /**
+ * If the press happened in the resize area, raise the drag flag.
+ *
+ * @param event the associated mouse event.
+ */
private void mousePressed(MouseEvent event) {
if(isInDraggableZone(event)) {
dragging = true;
}
}
+ /**
+ * If drag flag is high, resize the region to match the mouse position.
+ *
+ * @param event the associated mouse event.
+ */
private void mouseDragged(MouseEvent event) {
if(dragging) {
double newHeight = region.getHeight() - event.getY();
@@ -67,6 +97,11 @@ public class VerticalDragResize {
}
}
+ /**
+ * Change the cursor if the mouse position overlaps with the resize area.
+ *
+ * @param event the associated mouse event.
+ */
private void mouseMoved(MouseEvent event) {
if(isInDraggableZone(event) || dragging) {
region.setCursor(Cursor.V_RESIZE);
@@ -76,11 +111,20 @@ public class VerticalDragResize {
}
}
+ /**
+ * Finish resizing.
+ */
private void mouseReleased() {
dragging = false;
region.setCursor(Cursor.DEFAULT);
}
+ /**
+ * Assert whether the mouse cursor is in the draggable area defined by {@code GUI.RESIZE_MARGIN}.
+ *
+ * @param event the associated mouse event.
+ * @return true if the mouse position is in the draggable area.
+ */
private boolean isInDraggableZone(MouseEvent event) {
return event.getY() < (GUI.RESIZE_MARGIN);
}
diff --git a/src/jcgp/gui/population/ChromosomePane.java b/src/jcgp/gui/population/ChromosomePane.java
index 4a47f34..d40de2e 100644
--- a/src/jcgp/gui/population/ChromosomePane.java
+++ b/src/jcgp/gui/population/ChromosomePane.java
@@ -25,6 +25,8 @@ public class ChromosomePane extends ScrollPane {
private int rows, columns;
+ private Object[] testInputs;
+
private boolean target = false;
private PopulationPane parent;
@@ -111,20 +113,21 @@ public class ChromosomePane extends ScrollPane {
target = newValue;
}
- public void updateGenes() {
+ public void updateGenes(Chromosome chr) {
for (int r = 0; r < rows; r++) {
for (int c = 0; c < columns; c++) {
+ guiNodes[r][c].setNode(chr.getNode(r, c));
guiNodes[r][c].updateLines();
guiNodes[r][c].updateText();
}
}
for (int i = 0; i < guiOutputs.length; i++) {
+ guiOutputs[i].setOutput(chr.getOutput(i));
guiOutputs[i].updateLines();
}
if (isEvaluating()) {
- evaluate(0);
+ setInputs(testInputs);
}
-
}
public void unlockOutputs() {
@@ -144,29 +147,27 @@ public class ChromosomePane extends ScrollPane {
}
public void setInputs(Object[] values) {
+ testInputs = values;
for (int i = 0; i < guiInputs.length; i++) {
guiInputs[i].setValue(values[i]);
- guiInputs[i].updateText();
}
- evaluate(0);
+ updateValues();
}
- public void evaluate(int start) {
- if (start >= 0 || start < columns) {
- for (int c = start; c < columns; c++) {
- for (int r = 0; r < rows; r++) {
- guiNodes[r][c].calculate();
- guiNodes[r][c].updateText();
- }
- }
- for (int o = 0; o < guiOutputs.length; o++) {
- guiOutputs[o].calculate();
- guiOutputs[o].updateText();
- }
- }
- }
+// public void evaluate(int start) {
+// if (start >= 0 || start < columns) {
+// for (int c = 0; c < columns; c++) {
+// for (int r = 0; r < rows; r++) {
+// guiNodes[r][c].updateText();
+// }
+// }
+// for (int o = 0; o < guiOutputs.length; o++) {
+// guiOutputs[o].updateText();
+// }
+// }
+// }
- public void hideValues() {
+ public void updateValues() {
for (int i = 0; i < guiInputs.length; i++) {
guiInputs[i].updateText();
}
diff --git a/src/jcgp/gui/population/GUIGene.java b/src/jcgp/gui/population/GUIGene.java
index 3ace150..0eea045 100644
--- a/src/jcgp/gui/population/GUIGene.java
+++ b/src/jcgp/gui/population/GUIGene.java
@@ -39,8 +39,6 @@ public abstract class GUIGene extends Group {
protected ChromosomePane parent;
protected int locked = 0;
-
- protected Object value;
public GUIGene() {
text.setFont(Font.font("Arial", 12));
@@ -92,9 +90,5 @@ public abstract class GUIGene extends Group {
public abstract void setConnectionLine(GUIGene gene);
- public Object getValue() {
- return value;
- }
-
public abstract void updateText();
}
diff --git a/src/jcgp/gui/population/GUIInput.java b/src/jcgp/gui/population/GUIInput.java
index 05372c4..fd66ab4 100644
--- a/src/jcgp/gui/population/GUIInput.java
+++ b/src/jcgp/gui/population/GUIInput.java
@@ -219,14 +219,13 @@ public class GUIInput extends GUIGene {
}
public void setValue(Object newValue) {
- value = newValue;
input.setValue(newValue);
}
@Override
public void updateText() {
if (parent.isEvaluating()) {
- text.setText("I: " + input.getIndex() + "\n" + value.toString());
+ text.setText("I: " + input.getIndex() + "\n" + input.getValue().toString());
} else {
text.setText("I: " + input.getIndex());
}
diff --git a/src/jcgp/gui/population/GUINode.java b/src/jcgp/gui/population/GUINode.java
index 6dfeaa4..d3ae27f 100644
--- a/src/jcgp/gui/population/GUINode.java
+++ b/src/jcgp/gui/population/GUINode.java
@@ -398,7 +398,8 @@ public class GUINode extends GUIGene {
public void setChangingConnection(Connection newConnection) {
node.setConnection(connectionIndex, newConnection);
if (parent.isEvaluating()) {
- parent.evaluate(node.getColumn());
+ parent.updateValues();
+// parent.evaluate(node.getColumn());
}
}
@@ -452,24 +453,23 @@ public class GUINode extends GUIGene {
public void updateText() {
if (parent.isEvaluating()) {
- text.setText(node.getFunction() + "\n" + value.toString());
+ text.setText(node.getFunction() + "\n" + node.getValue().toString());
} else {
text.setText(node.getFunction().toString());
}
}
-
- public void calculate() {
- value = node.getValue();
- }
public void setFunction(Function function) {
node.setFunction(function);
if (parent.isEvaluating()) {
- calculate();
- parent.evaluate(node.getColumn());
+// parent.evaluate(node.getColumn());
+ parent.updateValues();
} else {
updateText();
}
-
+ }
+
+ public void setNode(Node newNode) {
+ node = newNode;
}
}
diff --git a/src/jcgp/gui/population/GUIOutput.java b/src/jcgp/gui/population/GUIOutput.java
index 29752cd..5a76298 100644
--- a/src/jcgp/gui/population/GUIOutput.java
+++ b/src/jcgp/gui/population/GUIOutput.java
@@ -267,10 +267,7 @@ public class GUIOutput extends GUIGene {
@Override
public void setChangingConnection(Connection newConnection) {
output.setConnection(0, newConnection);
- if (parent.isEvaluating()) {
- calculate();
- updateText();
- }
+ updateText();
}
@Override
@@ -308,19 +305,19 @@ public class GUIOutput extends GUIGene {
setLocked(true);
}
}
-
- public void calculate() {
- value = output.getSource().getValue();
- }
@Override
public void updateText() {
if (parent.isEvaluating()) {
- text.setText("O: " + output.getIndex() + "\n" + value.toString());
+ text.setText("O: " + output.getIndex() + "\n" + output.getSource().getValue().toString());
} else {
text.setText("O: " + output.getIndex());
}
}
+ public void setOutput(Output newOutput) {
+ output = newOutput;
+ }
+
}
diff --git a/src/jcgp/gui/population/PopulationPane.java b/src/jcgp/gui/population/PopulationPane.java
index 5fa6067..4b1b7f8 100644
--- a/src/jcgp/gui/population/PopulationPane.java
+++ b/src/jcgp/gui/population/PopulationPane.java
@@ -27,7 +27,7 @@ public class PopulationPane extends TabPane {
Tab tab;
ChromosomePane cp;
for (int i = 0; i < jcgp.getResources().populationSize(); i++) {
- cp = new ChromosomePane(jcgp.getPopulation().getChromosome(i), gui, this);
+ cp = new ChromosomePane(jcgp.getPopulation().get(i), gui, this);
tab = new Tab("Chr " + i);
tab.setContent(cp);
getTabs().add(tab);
@@ -39,7 +39,7 @@ public class PopulationPane extends TabPane {
evaluateTestCase(currentTestCase);
}
for (int i = 0; i < getTabs().size(); i++) {
- ((ChromosomePane) getTabs().get(i).getContent()).updateGenes();
+ ((ChromosomePane) getTabs().get(i).getContent()).updateGenes(gui.getExperiment().getPopulation().get(i));
}
}
@@ -73,7 +73,7 @@ public class PopulationPane extends TabPane {
public void hideValues() {
evaluating = false;
for (int i = 0; i < getTabs().size(); i++) {
- ((ChromosomePane) getTabs().get(i).getContent()).hideValues();
+ ((ChromosomePane) getTabs().get(i).getContent()).updateValues();
}
}
diff --git a/src/jcgp/gui/settings/SettingsPane.java b/src/jcgp/gui/settings/SettingsPane.java
index 802c1f1..2898dc3 100644
--- a/src/jcgp/gui/settings/SettingsPane.java
+++ b/src/jcgp/gui/settings/SettingsPane.java
@@ -255,7 +255,7 @@ public class SettingsPane extends AnchorPane {
}
private Button makeTestCaseButton() {
- Button b = new Button("Show data1");
+ Button b = new Button("Show data");
b.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
@@ -331,7 +331,7 @@ public class SettingsPane extends AnchorPane {
fc.getExtensionFilters().add(new ExtensionFilter("All files", "*.*"));
File chrFile = fc.showOpenDialog(gui.getStage());
if (chrFile != null) {
- gui.getExperiment().loadChromosome(chrFile, 0);
+ gui.getExperiment().loadChromosome(chrFile, gui.getChromosomeIndex());
gui.reDraw();
}
gui.flushConsole();
@@ -346,7 +346,7 @@ public class SettingsPane extends AnchorPane {
fc.getExtensionFilters().add(new ExtensionFilter("All files", "*.*"));
File chrFile = fc.showSaveDialog(gui.getStage());
if (chrFile != null) {
- gui.getExperiment().saveChromosome(chrFile, 0);
+ gui.getExperiment().saveChromosome(chrFile, gui.getChromosomeIndex());
}
gui.flushConsole();
}
@@ -474,6 +474,9 @@ public class SettingsPane extends AnchorPane {
parameter.applyValue();
}
updateArity();
+ if (testCaseTable != null) {
+ testCaseTable.close();
+ }
}
/**
diff --git a/src/jcgp/gui/settings/parameters/GUIParameter.java b/src/jcgp/gui/settings/parameters/GUIParameter.java
index 3009340..f896fa3 100644
--- a/src/jcgp/gui/settings/parameters/GUIParameter.java
+++ b/src/jcgp/gui/settings/parameters/GUIParameter.java
@@ -23,7 +23,7 @@ import jcgp.gui.settings.SettingsPane;
* This is the base class for all @code{GUIParameter}s. Using the factory method @code{GUIParameter.create()}
* generates an appropriate instance of this class for the specified parameter.
* <br><br>
- * @code{GUIParameter} is an @code{HBox} containing a @code{Text} for the parameter name
+ * A @code{GUIParameter} is an @code{HBox} containing a @code{Text} for the parameter name
* and a @code{Control} for interaction.
* It stores an instance of its associated @code{Parameter} object and also contains a @code{Tooltip} for
* displaying status information.
diff --git a/src/jcgp/gui/settings/testcase/TestCaseTable.java b/src/jcgp/gui/settings/testcase/TestCaseTable.java
index d4c1ff9..d4f789c 100644
--- a/src/jcgp/gui/settings/testcase/TestCaseTable.java
+++ b/src/jcgp/gui/settings/testcase/TestCaseTable.java
@@ -20,7 +20,10 @@ import jcgp.backend.resources.Resources;
import jcgp.gui.GUI;
/**
- *
+ * This is a test case table. For problems that have test cases,
+ * this table shows the test case inputs and outputs. Clicking on
+ * a test case (one is shown per row) applies the values to all
+ * chromosome inputs shows the calculated values throughout the chromosome.
*
* @author Eduardo Pedroni
*
@@ -29,17 +32,27 @@ public class TestCaseTable extends Stage {
private TableView<TestCase<Object>> table;
+ /**
+ * Make a new instance of {@code TestCaseTable}.
+ *
+ * @param testCaseProblem the {@code TestCaseProblem} whose data must be displayed.
+ * @param gui a reference to the GUI.
+ */
public TestCaseTable(final TestCaseProblem<Object> testCaseProblem, final GUI gui) {
super();
Resources resources = gui.getExperiment().getResources();
+ // create the actual table view
table = new TableView<TestCase<Object>>();
+ // get test cases from problem
ObservableList<TestCase<Object>> testCaseList = testCaseProblem.getTestCases();
+ // prepare input and output columns
ArrayList<TableColumn<TestCase<Object>, String>> inputs = new ArrayList<TableColumn<TestCase<Object>, String>>(resources.inputs());
ArrayList<TableColumn<TestCase<Object>, String>> outputs = new ArrayList<TableColumn<TestCase<Object>, String>>(resources.outputs());
+ // create input columns
TableColumn<TestCase<Object>, String> tc;
for (int i = 0; i < resources.inputs(); i++) {
tc = new TableColumn<TestCase<Object>, String>("I: " + i);
@@ -48,13 +61,16 @@ public class TestCaseTable extends Stage {
tc.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<TestCase<Object>,String>, ObservableValue<String>>() {
@Override
public ObservableValue<String> call(CellDataFeatures<TestCase<Object>, String> param) {
+ // create a new string property and give it the test case value, no need for dynamic binding - this wont change often
return new SimpleStringProperty(param.getValue().getInput(index).toString());
}
});
tc.setSortable(false);
+ // set column width so all columns are distributed across the width of the stage
tc.prefWidthProperty().bind(table.widthProperty().divide(resources.inputs() + resources.outputs()));
}
+ // create output columns
for (int o = 0; o < resources.outputs(); o++) {
tc = new TableColumn<TestCase<Object>, String>("O: " + o);
outputs.add(tc);
@@ -62,37 +78,46 @@ public class TestCaseTable extends Stage {
tc.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<TestCase<Object>,String>, ObservableValue<String>>() {
@Override
public ObservableValue<String> call(CellDataFeatures<TestCase<Object>, String> param) {
+ // create a new string property and give it the test case value, no need for dynamic binding - this wont change often
return new SimpleStringProperty(param.getValue().getOutput(index).toString());
}
});
tc.setSortable(false);
+ // set column width so all columns are distributed across the width of the stage
tc.prefWidthProperty().bind(table.widthProperty().divide(resources.inputs() + resources.outputs()));
}
+ // add created columns
table.getColumns().addAll(inputs);
table.getColumns().addAll(outputs);
+ // populate table with actual data
table.setItems(testCaseList);
+ // apply test case values when a new test case is selected
table.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<TestCase<Object>>() {
@Override
- public void changed(
- ObservableValue<? extends TestCase<Object>> observable, TestCase<Object> oldValue, TestCase<Object> newValue) {
+ public void changed(ObservableValue<? extends TestCase<Object>> observable, TestCase<Object> oldValue, TestCase<Object> newValue) {
gui.evaluateTestCase(newValue);
}
});
+ // when the stage is closed, clear the selection
+ // this doesn't work if the stage is closed by the program for some reason...
setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
gui.hideGeneValues();
- table.getSelectionModel().select(null);
+ table.getSelectionModel().clearSelection();
}
});
setScene(new Scene(table));
}
+ /**
+ * @return a reference to the actual table of test cases.
+ */
public TableView<TestCase<Object>> getTable() {
return table;
}