From 0dbf126fc524bc029d9f5803d849b7c8f43fe389 Mon Sep 17 00:00:00 2001 From: Eduardo Pedroni Date: Thu, 3 Apr 2014 15:29:24 +0100 Subject: Visual feedback for parameters implemented. --- src/jcgp/JCGP.java | 237 ++++++++++++++------- src/jcgp/backend/modules/Module.java | 6 +- .../backend/modules/ea/EvolutionaryAlgorithm.java | 3 +- src/jcgp/backend/modules/ea/MuPlusLambda.java | 57 +++-- .../backend/modules/ea/TournamentSelection.java | 18 +- .../backend/modules/fitness/TestCaseEvaluator.java | 6 +- src/jcgp/backend/modules/mutator/PointMutator.java | 19 +- src/jcgp/backend/parameters/BooleanParameter.java | 10 +- src/jcgp/backend/parameters/DoubleParameter.java | 10 +- src/jcgp/backend/parameters/IntegerParameter.java | 13 +- src/jcgp/backend/parameters/Parameter.java | 16 +- src/jcgp/backend/parameters/ParameterStatus.java | 16 ++ src/jcgp/backend/population/Chromosome.java | 10 +- src/jcgp/backend/population/Population.java | 53 ++++- src/jcgp/gui/GUI.java | 34 +-- src/jcgp/gui/settings/SettingsPane.java | 58 ++--- .../settings/parameters/GUIBooleanParameter.java | 63 ++++-- .../settings/parameters/GUIDoubleParameter.java | 89 ++++++-- .../settings/parameters/GUIIntegerParameter.java | 84 ++++++-- src/jcgp/gui/settings/parameters/GUIParameter.java | 21 +- 20 files changed, 556 insertions(+), 267 deletions(-) create mode 100644 src/jcgp/backend/parameters/ParameterStatus.java diff --git a/src/jcgp/JCGP.java b/src/jcgp/JCGP.java index 7451d4f..0c75f2b 100644 --- a/src/jcgp/JCGP.java +++ b/src/jcgp/JCGP.java @@ -25,6 +25,7 @@ import jcgp.backend.parameters.BooleanParameter; import jcgp.backend.parameters.DoubleParameter; import jcgp.backend.parameters.IntegerParameter; import jcgp.backend.parameters.Parameter; +import jcgp.backend.parameters.ParameterStatus; import jcgp.backend.population.Population; import jcgp.gui.console.Console; @@ -114,26 +115,143 @@ public class JCGP { } private void createBaseParameters() { - parameters.put("rows", new IntegerParameter(8, "Rows")); - parameters.put("columns", new IntegerParameter(9, "Columns")); - parameters.put("inputs", new IntegerParameter(3, "Inputs")); - parameters.put("outputs", new IntegerParameter(3, "Outputs")); - parameters.put("popSize", new IntegerParameter(5, "Population")); - parameters.put("levelsBack", new IntegerParameter(2, "Levels back")); + parameters.put("rows", new IntegerParameter(8, "Rows", false, true) { + @Override + protected void validate(int newValue) { + if (newValue <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Chromosome must have at least 1 row."); + } else { + status = ParameterStatus.VALID; + } + } + }); + parameters.put("columns", new IntegerParameter(9, "Columns", false, true) { + @Override + protected void validate(int newValue) { + if (newValue <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Chromosome must have at least 1 column."); + } else { + status = ParameterStatus.VALID; + } + } + }); + parameters.put("inputs", new IntegerParameter(3, "Inputs", false, true) { + @Override + protected void validate(int newValue) { + if (newValue <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Chromosome must have at least 1 input."); + } else { + status = ParameterStatus.VALID; + } + } + }); + parameters.put("outputs", new IntegerParameter(3, "Outputs", false, true) { + @Override + protected void validate(int newValue) { + if (newValue <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Chromosome must have at least 1 output."); + } else { + status = ParameterStatus.VALID; + } + } + }); + parameters.put("popSize", new IntegerParameter(5, "Population", false, true) { + @Override + protected void validate(int newValue) { + if (newValue <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Population size must be at least 1."); + } else { + status = ParameterStatus.VALID; + } + } + }); + parameters.put("levelsBack", new IntegerParameter(2, "Levels back", false, true) { + @Override + protected void validate(int newValue) { + if (newValue <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Levels back must be at least 1."); + } else if (newValue > getInt("columns")) { + status = ParameterStatus.INVALID; + status.setDetails("Levels back must be less than or equal to the number of columns."); + } else { + status = ParameterStatus.VALID; + } + } + }); - IntegerParameter nodes = new IntegerParameter(1, "Nodes", true, true, false); + IntegerParameter nodes = new IntegerParameter(1, "Nodes", true, false) { + @Override + protected void validate(int newValue) { + // blank + } + }; nodes.valueProperty().bind(((SimpleIntegerProperty) ((IntegerParameter) parameters.get("rows")).valueProperty()).multiply((SimpleIntegerProperty) ((IntegerParameter) parameters.get("columns")).valueProperty())); parameters.put("nodes", nodes); - parameters.put("generations", new IntegerParameter(1000000, "Generations")); - parameters.put("currentGen", new IntegerParameter(1, "Generation", true, false, false)); - - parameters.put("runs", new IntegerParameter(5, "Runs")); - parameters.put("currentRun", new IntegerParameter(1, "Run", true, false, false)); + parameters.put("generations", new IntegerParameter(1000000, "Generations") { + @Override + protected void validate(int newValue) { + if (newValue <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Number of generations must be greater than 0."); + } else if (newValue <= getInt("currentGen")) { + status = ParameterStatus.WARNING; + status.setDetails("Setting generations to less than the current generation will cause the experiment to restart."); + } else { + status = ParameterStatus.VALID; + } + } + }); + parameters.put("currentGen", new IntegerParameter(1, "Generation", true, false) { + @Override + protected void validate(int newValue) { + // blank + } + }); - parameters.put("arity", new IntegerParameter(0, "Max arity", false, true, false)); + parameters.put("runs", new IntegerParameter(5, "Runs") { + @Override + protected void validate(int newValue) { + if (newValue <= getInt("currentRun")) { + status = ParameterStatus.WARNING; + status.setDetails("Setting runs to less than the current run will cause the experiment to restart."); + } else { + status = ParameterStatus.VALID; + } + } + }); + parameters.put("currentRun", new IntegerParameter(1, "Run", true, false) { + @Override + protected void validate(int newValue) { + // blank + } + }); + + parameters.put("arity", new IntegerParameter(0, "Max arity", true, false) { + @Override + protected void validate(int newValue) { + // blank + } + }); + parameters.put("maxFitness", new IntegerParameter(6, "Max fitness", true, true) { + @Override + protected void validate(int newValue) { + // blank + } + }); - IntegerParameter seed = new IntegerParameter(123, "Seed"); + IntegerParameter seed = new IntegerParameter(123, "Seed", false, true) { + @Override + protected void validate(int newValue) { + status = ParameterStatus.VALID; + } + }; seed.valueProperty().addListener(new ChangeListener() { @Override public void changed( @@ -144,50 +262,17 @@ public class JCGP { }); parameters.put("seed", seed); - parameters.put("report", new IntegerParameter(1, "Report", false, false, false)); - } - - private boolean validateBaseParameters() { - boolean valid = true; - - if (getInt("rows") <= 0) { - valid = false; - println("Error: number of rows must be greater than 0."); - } - if (getInt("columns") <= 0) { - valid = false; - println("Error: number of columns must be greater than 0."); - } - if (getInt("inputs") <= 0) { - valid = false; - println("Error: number of inputs must be greater than 0."); - } - if (getInt("outputs") <= 0) { - println("Error: number of outputs must be greater than 0."); - valid = false; - } - if (getInt("popSize") <= 0) { - println("Error: population size must be greater than 0."); - valid = false; - } - if (getInt("levelsBack") <= 0 || getInt("levelsBack") > getInt("columns")) { - println("Error: levels back must be greater than 0 and no greater than the number of columns."); - valid = false; - } - if (getInt("generations") <= 0) { - println("Error: number of generations must be greater than 0."); - valid = false; - } - if (getInt("runs") <= 0) { - println("Error: number of runs must be greater than 0."); - valid = false; - } - if (getInt("report") < 0) { - println("Error: report frequency must not be smaller than 0."); - valid = false; - } - - return valid; + parameters.put("report", new IntegerParameter(1, "Report", false, false) { + @Override + protected void validate(int newValue) { + if (newValue > getInt("generations")) { + status = ParameterStatus.WARNING; + status.setDetails("No reports will be printed."); + } else { + status = ParameterStatus.VALID; + } + } + }); } /** @@ -310,6 +395,8 @@ public class JCGP { * the population of chromosomes */ private Population population; + + private boolean solutionFound = false; public JCGP() { @@ -323,21 +410,14 @@ public class JCGP { fitnessFunction = fitnessFunctions[0]; resources.setTestCases(new TestCase(new Integer[]{1, 2, 3}, new Integer[]{4, 5, 6}), - new TestCase(new Integer[]{1, 12, 4}, new Integer[]{6, 21, 2})); + new TestCase(new Integer[]{4, 7, 4}, new Integer[]{6, 21, 2})); } - - public void remakePopulation() { - population = new Population(resources); - } - - public Resources getResources() { return resources; } - public Population getPopulation() { return population; } @@ -415,22 +495,33 @@ public class JCGP { } public void nextGeneration() { - fitnessFunction.evaluate(population, resources); - evolutionaryAlgorithm.evolve(population, mutator, resources); - if (evolutionaryAlgorithm.getFittestChromosome().getFitness() >= 6) { - resources.println("solution found"); - evolutionaryAlgorithm.getFittestChromosome().printNodes(); + if (population.getChromosome(evolutionaryAlgorithm.getFittestChromosome()).getFitness() >= resources.getInt("maxFitness")) { + solutionFound = true; + resources.println("Solution found!"); + population.getChromosome(evolutionaryAlgorithm.getFittestChromosome()).printNodes(); } else { - resources.println("Generation: " + resources.getInt("currentGen") + ", fitness: " + evolutionaryAlgorithm.getFittestChromosome().getFitness()); + resources.println("Generation: " + resources.getInt("currentGen") + ", fitness: " + population.getChromosome(evolutionaryAlgorithm.getFittestChromosome()).getFitness()); + resources.set("currentGen", resources.getInt("currentGen") + 1); } - resources.set("currentGen", resources.getInt("currentGen") + 1); + + evolutionaryAlgorithm.evolve(population, mutator, resources); } public void start() { while (resources.getInt("currentGen") <= resources.getInt("generations")) { nextGeneration(); + + if (solutionFound) { + break; + } } } + + public void reset() { + solutionFound = false; + population = new Population(resources); + } + } diff --git a/src/jcgp/backend/modules/Module.java b/src/jcgp/backend/modules/Module.java index f9114de..82c3d80 100644 --- a/src/jcgp/backend/modules/Module.java +++ b/src/jcgp/backend/modules/Module.java @@ -1,11 +1,11 @@ package jcgp.backend.modules; -import java.util.HashMap; - import jcgp.JCGP.Resources; import jcgp.backend.parameters.Parameter; public interface Module { - public HashMap getLocalParameters(); + + public Parameter[] getLocalParameters(); + public ModuleStatus getStatus(Resources resources); } diff --git a/src/jcgp/backend/modules/ea/EvolutionaryAlgorithm.java b/src/jcgp/backend/modules/ea/EvolutionaryAlgorithm.java index ce457ef..7719111 100644 --- a/src/jcgp/backend/modules/ea/EvolutionaryAlgorithm.java +++ b/src/jcgp/backend/modules/ea/EvolutionaryAlgorithm.java @@ -3,13 +3,12 @@ package jcgp.backend.modules.ea; import jcgp.JCGP.Resources; import jcgp.backend.modules.Module; import jcgp.backend.modules.mutator.Mutator; -import jcgp.backend.population.Chromosome; import jcgp.backend.population.Population; public interface EvolutionaryAlgorithm extends Module { public abstract void evolve(Population population, Mutator mutator, Resources parameters); - public abstract Chromosome getFittestChromosome(); + public abstract int getFittestChromosome(); } diff --git a/src/jcgp/backend/modules/ea/MuPlusLambda.java b/src/jcgp/backend/modules/ea/MuPlusLambda.java index fcfba05..22a0876 100644 --- a/src/jcgp/backend/modules/ea/MuPlusLambda.java +++ b/src/jcgp/backend/modules/ea/MuPlusLambda.java @@ -1,13 +1,10 @@ package jcgp.backend.modules.ea; -import java.util.HashMap; - import jcgp.JCGP.Resources; import jcgp.backend.modules.ModuleStatus; import jcgp.backend.modules.mutator.Mutator; import jcgp.backend.parameters.IntegerParameter; import jcgp.backend.parameters.Parameter; -import jcgp.backend.population.Chromosome; import jcgp.backend.population.Population; /** @@ -19,52 +16,54 @@ import jcgp.backend.population.Population; */ public class MuPlusLambda implements EvolutionaryAlgorithm { - private Chromosome fittestChromosome; + private int fittestChromosome; private IntegerParameter parents, offspring; - - private HashMap localParameters; - - public MuPlusLambda() { - parents = new IntegerParameter(1, "Parents"); - offspring = new IntegerParameter(4, "Offspring"); - - localParameters = new HashMap(); - localParameters.put("mu", parents); - localParameters.put("lambda", offspring); + public MuPlusLambda() { + parents = new IntegerParameter(1, "Parents") { + @Override + protected void validate(int newValue) { + + } + }; + offspring = new IntegerParameter(4, "Offspring") { + @Override + protected void validate(int newValue) { + + } + }; } @Override - public void evolve(Population population, Mutator mutator, Resources parameters) { + public void evolve(Population population, Mutator mutator, Resources resources) { + // TODO actually use parents and offspring // select fittest chromosome - int fittest = 0; + fittestChromosome = 0; - for (int i = 1; i < parameters.getInt("popSize"); i++) { - if (population.getChromosome(i).getFitness() >= population.getChromosome(fittest).getFitness()) { - fittest = i; + for (int i = 1; i < resources.getInt("popSize"); i++) { + if (population.getChromosome(i).getFitness() >= population.getChromosome(fittestChromosome).getFitness()) { + fittestChromosome = i; } } - fittestChromosome = population.getChromosome(fittest); - population.setBestIndividual(fittest); + // create copies of fittest chromosome, mutate them - Chromosome fc = population.getChromosome(fittest); - for (int i = 0; i < parameters.getInt("popSize"); i++) { - if (i != fittest) { - population.getChromosome(i).copyConnections(fc); - mutator.mutate(population.getChromosome(i), parameters); + for (int i = 0; i < resources.getInt("popSize"); i++) { + if (i != fittestChromosome) { + population.copyChromosome(fittestChromosome, i); + mutator.mutate(population.getChromosome(i), resources); } } } @Override - public Chromosome getFittestChromosome() { + public int getFittestChromosome() { return fittestChromosome; } @Override - public HashMap getLocalParameters() { - return localParameters; + public Parameter[] getLocalParameters() { + return new Parameter[] {parents, offspring}; } @Override diff --git a/src/jcgp/backend/modules/ea/TournamentSelection.java b/src/jcgp/backend/modules/ea/TournamentSelection.java index 6cbaa45..3c4a539 100644 --- a/src/jcgp/backend/modules/ea/TournamentSelection.java +++ b/src/jcgp/backend/modules/ea/TournamentSelection.java @@ -7,39 +7,43 @@ import jcgp.backend.modules.ModuleStatus; import jcgp.backend.modules.mutator.Mutator; import jcgp.backend.parameters.IntegerParameter; import jcgp.backend.parameters.Parameter; -import jcgp.backend.population.Chromosome; import jcgp.backend.population.Population; public class TournamentSelection implements EvolutionaryAlgorithm { - private Chromosome fittestChromosome; + private int fittestChromosome; private IntegerParameter tournament; private HashMap localParameters; public TournamentSelection() { - tournament = new IntegerParameter(1, "Tournament size"); + tournament = new IntegerParameter(1, "Tournament size") { + @Override + protected void validate(int newValue) { + + } + }; localParameters = new HashMap(); localParameters.put("tournament", tournament); } @Override - public HashMap getLocalParameters() { - return localParameters; + public Parameter[] getLocalParameters() { + return new Parameter[] {tournament}; } @Override public void evolve(Population population, Mutator mutator, Resources parameters) { tournament.set(tournament.get() + 1); - fittestChromosome = population.getChromosome(0); + fittestChromosome = 0; // TODO implement this } @Override - public Chromosome getFittestChromosome() { + public int getFittestChromosome() { return fittestChromosome; } diff --git a/src/jcgp/backend/modules/fitness/TestCaseEvaluator.java b/src/jcgp/backend/modules/fitness/TestCaseEvaluator.java index 3b67f28..5ff6973 100644 --- a/src/jcgp/backend/modules/fitness/TestCaseEvaluator.java +++ b/src/jcgp/backend/modules/fitness/TestCaseEvaluator.java @@ -1,7 +1,5 @@ package jcgp.backend.modules.fitness; -import java.util.HashMap; - import jcgp.JCGP.Resources; import jcgp.backend.modules.ModuleStatus; import jcgp.backend.parameters.Parameter; @@ -10,7 +8,7 @@ import jcgp.backend.population.Population; public class TestCaseEvaluator implements FitnessFunction { @Override - public void evaluate(Population population, Resources resources) { + public void evaluate(Population population, Resources resources) { // for every chromosome in the population for (int i = 0; i < resources.getInt("popSize"); i++) { int fitness = 0; @@ -29,7 +27,7 @@ public class TestCaseEvaluator implements FitnessFunction { } @Override - public HashMap getLocalParameters() { + public Parameter[] getLocalParameters() { return null; } diff --git a/src/jcgp/backend/modules/mutator/PointMutator.java b/src/jcgp/backend/modules/mutator/PointMutator.java index 62d827d..cdac8bb 100644 --- a/src/jcgp/backend/modules/mutator/PointMutator.java +++ b/src/jcgp/backend/modules/mutator/PointMutator.java @@ -1,7 +1,5 @@ package jcgp.backend.modules.mutator; -import java.util.HashMap; - import jcgp.backend.function.Function; import jcgp.backend.modules.ModuleStatus; import jcgp.backend.parameters.DoubleParameter; @@ -15,15 +13,16 @@ import jcgp.JCGP.Resources; public class PointMutator implements Mutator { private DoubleParameter mutationRate; - private HashMap localParameters; - + private ModuleStatus status = ModuleStatus.READY; public PointMutator() { - mutationRate = new DoubleParameter(0.5, "Percent mutation", false, false, false); - - localParameters = new HashMap(); - localParameters.put("mutRate", mutationRate); + mutationRate = new DoubleParameter(0.5, "Percent mutation", false, false) { + @Override + public void validate(double newValue) { + // TODO this + } + }; } @Override @@ -47,8 +46,8 @@ public class PointMutator implements Mutator { } @Override - public HashMap getLocalParameters() { - return localParameters; + public Parameter[] getLocalParameters() { + return new Parameter[] {mutationRate}; } @Override diff --git a/src/jcgp/backend/parameters/BooleanParameter.java b/src/jcgp/backend/parameters/BooleanParameter.java index d5904cd..db78419 100644 --- a/src/jcgp/backend/parameters/BooleanParameter.java +++ b/src/jcgp/backend/parameters/BooleanParameter.java @@ -2,17 +2,17 @@ package jcgp.backend.parameters; import javafx.beans.property.SimpleBooleanProperty; -public class BooleanParameter extends Parameter { +public abstract class BooleanParameter extends Parameter { private SimpleBooleanProperty value; - public BooleanParameter(boolean value, String name, boolean monitor, boolean hidden, boolean critical) { - super(name, monitor, hidden, critical); + public BooleanParameter(boolean value, String name, boolean monitor, boolean critical) { + super(name, monitor, critical); this.value = new SimpleBooleanProperty(value); } public BooleanParameter(boolean value, String name) { - super(name, false, false, true); + super(name, false, false); this.value = new SimpleBooleanProperty(value); } @@ -26,6 +26,8 @@ public class BooleanParameter extends Parameter { } } + protected abstract void validate(boolean newValue); + public SimpleBooleanProperty valueProperty() { return value; } diff --git a/src/jcgp/backend/parameters/DoubleParameter.java b/src/jcgp/backend/parameters/DoubleParameter.java index 26031be..2b98991 100644 --- a/src/jcgp/backend/parameters/DoubleParameter.java +++ b/src/jcgp/backend/parameters/DoubleParameter.java @@ -2,17 +2,17 @@ package jcgp.backend.parameters; import javafx.beans.property.SimpleDoubleProperty; -public class DoubleParameter extends Parameter { +public abstract class DoubleParameter extends Parameter { protected SimpleDoubleProperty value; - public DoubleParameter(double value, String name, boolean monitor, boolean hidden, boolean critical) { - super(name, monitor, hidden, critical); + public DoubleParameter(double value, String name, boolean monitor, boolean critical) { + super(name, monitor, critical); this.value = new SimpleDoubleProperty(value); } public DoubleParameter(double value, String name) { - super(name, false, false, true); + super(name, false, false); this.value = new SimpleDoubleProperty(value); } @@ -30,4 +30,6 @@ public class DoubleParameter extends Parameter { return value; } + protected abstract void validate(double newValue); + } diff --git a/src/jcgp/backend/parameters/IntegerParameter.java b/src/jcgp/backend/parameters/IntegerParameter.java index dbfa5c5..1127817 100644 --- a/src/jcgp/backend/parameters/IntegerParameter.java +++ b/src/jcgp/backend/parameters/IntegerParameter.java @@ -2,17 +2,17 @@ package jcgp.backend.parameters; import javafx.beans.property.SimpleIntegerProperty; -public class IntegerParameter extends Parameter { +public abstract class IntegerParameter extends Parameter { - protected SimpleIntegerProperty value; + private SimpleIntegerProperty value; - public IntegerParameter(int value, String name, boolean monitor, boolean hidden, boolean critical) { - super(name, monitor, hidden, critical); + public IntegerParameter(int value, String name, boolean monitor, boolean critical) { + super(name, monitor, critical); this.value = new SimpleIntegerProperty(value); } public IntegerParameter(int value, String name) { - super(name, false, false, true); + super(name, false, false); this.value = new SimpleIntegerProperty(value); } @@ -22,6 +22,7 @@ public class IntegerParameter extends Parameter { public void set(int newValue) { if (!value.isBound()) { + validate(newValue); value.set(newValue); } } @@ -30,4 +31,6 @@ public class IntegerParameter extends Parameter { return value; } + protected abstract void validate(int newValue); + } diff --git a/src/jcgp/backend/parameters/Parameter.java b/src/jcgp/backend/parameters/Parameter.java index 2f584a4..26bc8f1 100644 --- a/src/jcgp/backend/parameters/Parameter.java +++ b/src/jcgp/backend/parameters/Parameter.java @@ -4,21 +4,18 @@ import javafx.beans.property.Property; public abstract class Parameter { - protected boolean hidden, monitor, critical; + protected boolean monitor, critical; + + protected ParameterStatus status = ParameterStatus.VALID; protected String name; - public Parameter(String name, boolean monitor, boolean hidden, boolean critical) { + public Parameter(String name, boolean monitor, boolean critical) { this.name = name; - this.hidden = hidden; this.monitor = monitor; this.critical = critical; } - public boolean isHidden() { - return hidden; - } - public boolean isMonitor() { return monitor; } @@ -31,5 +28,10 @@ public abstract class Parameter { return name; } + public ParameterStatus getStatus() { + return status; + } + public abstract Property valueProperty(); + } diff --git a/src/jcgp/backend/parameters/ParameterStatus.java b/src/jcgp/backend/parameters/ParameterStatus.java new file mode 100644 index 0000000..ed235d4 --- /dev/null +++ b/src/jcgp/backend/parameters/ParameterStatus.java @@ -0,0 +1,16 @@ +package jcgp.backend.parameters; + +public enum ParameterStatus { + INVALID, WARNING, VALID; + + private String details; + + public void setDetails(String details) { + this.details = details; + } + + public String getDetails() { + return details; + } + +} diff --git a/src/jcgp/backend/population/Chromosome.java b/src/jcgp/backend/population/Chromosome.java index 18ae9bb..41ba06e 100644 --- a/src/jcgp/backend/population/Chromosome.java +++ b/src/jcgp/backend/population/Chromosome.java @@ -43,12 +43,12 @@ public class Chromosome { */ public Chromosome(Chromosome clone) { // store a reference to the parameters - this.resources = clone.getParameters(); + this.resources = clone.getResources(); // allocate memory for all elements of the chromosome instantiateElements(); // initialise all connections based on argument - copyConnections(clone); + copyGenes(clone); } /** @@ -102,7 +102,7 @@ public class Chromosome { /** * @param clone */ - public void copyConnections(Chromosome clone) { + public void copyGenes(Chromosome clone) { int arity = resources.getInt("arity"); // copy nodes - [rows][columns] @@ -119,7 +119,7 @@ public class Chromosome { } else if (copyConnection instanceof Node) { connections[i] = nodes[((Node) copyConnection).getRow()][((Node) copyConnection).getColumn()]; } else { - System.out.println("Warning: Connection of subtype " + copyConnection.getClass().toString() + " is not explicitly handled by copy constructor."); + System.out.println("Error: Connection of subtype " + copyConnection.getClass().toString() + " is not explicitly handled by copy method."); } } // initialise with copied arguments @@ -329,7 +329,7 @@ public class Chromosome { } } - public Resources getParameters() { + public Resources getResources() { return resources; } } diff --git a/src/jcgp/backend/population/Population.java b/src/jcgp/backend/population/Population.java index 7049d79..7b62d27 100644 --- a/src/jcgp/backend/population/Population.java +++ b/src/jcgp/backend/population/Population.java @@ -6,27 +6,41 @@ import jcgp.JCGP.Resources; public class Population { private Chromosome[] chromosomes; - private int fittest; + private Resources resources; + /** + * Initialise a random population according to the parameters specified + * in the resources. + * + * @param resources the CGP resources + */ public Population(Resources resources) { + this.resources = resources; + chromosomes = new Chromosome[(resources.getInt("popSize"))]; for (int c = 0; c < chromosomes.length; c++) { chromosomes[c] = new Chromosome(resources); } } + /** + * Initialise a population of copies of the given chromosome. + * + * @param parent + * @param resources + */ public Population(Chromosome parent, Resources resources) { + this.resources = resources; + chromosomes = new Chromosome[(resources.getInt("popSize"))]; - // make a clone for safety - this.chromosomes[0] = new Chromosome(parent); // generate the rest of the individuals - for (int c = 1; c < chromosomes.length; c++) { - chromosomes[c] = new Chromosome(chromosomes[0]); + for (int c = 0; c < chromosomes.length; c++) { + chromosomes[c] = new Chromosome(parent); } } /** - * Returns all chromosomes, parents first, then offspring. + * Returns the indexed chromosome. * * @param index * @return @@ -35,11 +49,30 @@ public class Population { return chromosomes[index]; } - public void setBestIndividual(int index) { - fittest = index; + /** + * @return a random chromosome from this population. + */ + public Chromosome getRandomChromosome() { + return chromosomes[resources.getRandomInt(chromosomes.length)]; } + - public Chromosome getBestIndividual() { - return chromosomes[fittest]; + /** + * Copy a chromosome into a different position. + * After this returns, the target chromosome has + * identical connections and functions to the source + * one, though they are separate instances. + * + * This method does nothing if source == target. + * + * @param source + * @param target + */ + public void copyChromosome(int source, int target) { + if (source != target) { + chromosomes[target].copyGenes(chromosomes[source]); + } } + + } diff --git a/src/jcgp/gui/GUI.java b/src/jcgp/gui/GUI.java index 7e7f385..0902b21 100644 --- a/src/jcgp/gui/GUI.java +++ b/src/jcgp/gui/GUI.java @@ -22,16 +22,16 @@ public class GUI extends Application { * (no hovering, clicking, dragging or any other form of interaction). */ public static final String NEUTRAL_COLOUR = "#FFFFFF"; public static final String HARD_HIGHLIGHT_COLOUR = "#5496FF"; - // 89AAD6 public static final String MEDIUM_HIGHLIGHT_COLOUR = "#75BAFF"; public static final String SOFT_HIGHLIGHT_COLOUR = "#C7DFFF"; - // BDFFC2 - public static final String GOOD_SELECTION_COLOUR = "#38C25B"; - // FBFFB8 - public static final String NEUTRAL_SELECTION_COLOUR = "#EDEB72"; - // FF9C9C - public static final String BAD_SELECTION_COLOUR = "#F53D3D"; + public static final String GOOD_SELECTION_COLOUR = "#38C25B"; + public static final String NEUTRAL_SELECTION_COLOUR = "#FFEF73"; + public static final String BAD_SELECTION_COLOUR = "#FF5C5C"; + + public static final String INVALID_PARAMETER_STYLE = "-fx-border-color: C9C9C9; -fx-border-radius: 2; -fx-padding: 0; -fx-background-color: " + BAD_SELECTION_COLOUR; + public static final String WARNING_PARAMETER_STYLE = "-fx-border-color: C9C9C9; -fx-border-radius: 2; -fx-padding: 0; -fx-background-color: " + NEUTRAL_SELECTION_COLOUR; + public static final String VALID_PARAMETER_STYLE = "-fx-border-color: C9C9C9; -fx-border-radius: 2; -fx-padding: 0; -fx-background-color: " + NEUTRAL_COLOUR; /* Sizes and distances */ public static final double RESIZE_MARGIN = 5.0; @@ -71,7 +71,7 @@ public class GUI extends Application { public static void main(String[] args) { cgp = new JCGP(); - resources = cgp.getResources(); + resources = cgp.getResources(); launch(); } @@ -166,17 +166,20 @@ public class GUI extends Application { public void disableChromosomePanes(boolean value) { chromosomeTabs.setDisable(value); } + + public void disable(boolean value) { + chromosomeTabs.setDisable(value); + settings.disableSettings(value); + } public void playPause() { if (!evolving) { - settings.disableSettings(true); - disableChromosomePanes(true); + disable(true); unlockOutputs(); evolving = true; cgpService.restart(); } else { - settings.disableSettings(false); - disableChromosomePanes(false); + disable(false); updateNodeGrids(); evolving = false; cgpService.cancel(); @@ -201,11 +204,8 @@ public class GUI extends Application { } } - public void resetCGP() { - cgp.remakePopulation(); + public void reset() { + cgp.reset(); makeChromosomeTabPane(); - - resources.set("currentGen", 1); - resources.set("currentRun", 1); } } diff --git a/src/jcgp/gui/settings/SettingsPane.java b/src/jcgp/gui/settings/SettingsPane.java index 61011aa..41b1bba 100644 --- a/src/jcgp/gui/settings/SettingsPane.java +++ b/src/jcgp/gui/settings/SettingsPane.java @@ -1,9 +1,6 @@ package jcgp.gui.settings; import java.util.ArrayList; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; import javafx.event.ActionEvent; import javafx.event.EventHandler; @@ -37,11 +34,15 @@ public class SettingsPane extends AnchorPane { private VBox bpPane, eaPane, mutatorPane, ffPane, nfPane; private ArrayList parameters = new ArrayList(); + private GUI gui; + private boolean dragging = false; public SettingsPane(JCGP cgp, GUI gui) { super(); + this.gui = gui; + setResizeListeners(); mainContainer = new VBox(8); @@ -84,8 +85,7 @@ public class SettingsPane extends AnchorPane { bpPane.getChildren().add(header); - GUIParameter rows = GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("rows")); - parameters.add(rows); + parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("rows"))); parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("columns"))); parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("inputs"))); parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("outputs"))); @@ -98,14 +98,13 @@ public class SettingsPane extends AnchorPane { parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("currentGen"))); parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("currentRun"))); + parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("maxFitness"))); parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("seed"))); parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("report"))); bpPane.getChildren().addAll(parameters); mainContainer.getChildren().add(bpPane); - - } private void initialiseEAParameters(final JCGP cgp) { @@ -122,8 +121,9 @@ public class SettingsPane extends AnchorPane { final VBox eaParameters = new VBox(); eaParameters.setSpacing(2); + if (cgp.getEvolutionaryAlgorithm().getLocalParameters() != null) { - refreshParameters(cgp.getEvolutionaryAlgorithm().getLocalParameters().entrySet().iterator(), eaParameters); + refreshParameters(cgp.getEvolutionaryAlgorithm().getLocalParameters(), eaParameters); } eaCBox.setOnAction(new EventHandler() { @@ -131,7 +131,7 @@ public class SettingsPane extends AnchorPane { public void handle(ActionEvent event) { cgp.setEvolutionaryAlgorithm(eaCBox.getSelectionModel().getSelectedIndex()); if (eaCBox.getSelectionModel().getSelectedItem().getLocalParameters() != null) { - refreshParameters(eaCBox.getSelectionModel().getSelectedItem().getLocalParameters().entrySet().iterator(), eaParameters); + refreshParameters(eaCBox.getSelectionModel().getSelectedItem().getLocalParameters(), eaParameters); } } }); @@ -156,7 +156,7 @@ public class SettingsPane extends AnchorPane { final VBox mutatorParameters = new VBox(); mutatorParameters.setSpacing(2); if (cgp.getEvolutionaryAlgorithm().getLocalParameters() != null) { - refreshParameters(cgp.getMutator().getLocalParameters().entrySet().iterator(), mutatorParameters); + refreshParameters(cgp.getMutator().getLocalParameters(), mutatorParameters); } mutatorCBox.setOnAction(new EventHandler() { @@ -164,7 +164,7 @@ public class SettingsPane extends AnchorPane { public void handle(ActionEvent event) { cgp.setEvolutionaryAlgorithm(mutatorCBox.getSelectionModel().getSelectedIndex()); if (mutatorCBox.getSelectionModel().getSelectedItem().getLocalParameters() != null) { - refreshParameters(mutatorCBox.getSelectionModel().getSelectedItem().getLocalParameters().entrySet().iterator(), mutatorParameters); + refreshParameters(mutatorCBox.getSelectionModel().getSelectedItem().getLocalParameters(), mutatorParameters); } } }); @@ -188,7 +188,7 @@ public class SettingsPane extends AnchorPane { final VBox ffParameters = new VBox(); ffParameters.setSpacing(2); if (cgp.getFitnessFunction().getLocalParameters() != null) { - refreshParameters(cgp.getFitnessFunction().getLocalParameters().entrySet().iterator(), ffParameters); + refreshParameters(cgp.getFitnessFunction().getLocalParameters(), ffParameters); } ffCBox.setOnAction(new EventHandler() { @@ -196,7 +196,7 @@ public class SettingsPane extends AnchorPane { public void handle(ActionEvent event) { cgp.setEvolutionaryAlgorithm(ffCBox.getSelectionModel().getSelectedIndex()); if (ffCBox.getSelectionModel().getSelectedItem().getLocalParameters() != null) { - refreshParameters(ffCBox.getSelectionModel().getSelectedItem().getLocalParameters().entrySet().iterator(), ffParameters); + refreshParameters(ffCBox.getSelectionModel().getSelectedItem().getLocalParameters(), ffParameters); } } }); @@ -244,8 +244,8 @@ public class SettingsPane extends AnchorPane { @Override public void handle(ActionEvent event) { if (play.getText() == "Run") { - if (applyChanges()) { - gui.resetCGP(); + if (isResetRequired()) { + resetExperiment(); } play.setText("Pause"); } else { @@ -260,8 +260,8 @@ public class SettingsPane extends AnchorPane { step.setOnAction(new EventHandler() { @Override public void handle(ActionEvent event) { - if (applyChanges()) { - gui.resetCGP(); + if (isResetRequired()) { + resetExperiment(); } gui.step(); } @@ -284,16 +284,15 @@ public class SettingsPane extends AnchorPane { * @param cgp * @param vb */ - private void refreshParameters(Iterator> it, VBox vb) { + private void refreshParameters(Parameter[] newParameters, VBox vb) { parameters.removeAll(vb.getChildren()); vb.getChildren().clear(); - while (it.hasNext()) { - Parameter parameter = ((Map.Entry) it.next()).getValue(); - GUIParameter gp = GUIParameter.create(parameter); + for (int i = 0; i < newParameters.length; i++) { + GUIParameter gp = GUIParameter.create(newParameters[i]); parameters.add(gp); vb.getChildren().add(gp); - } + } } private void refreshFunctions(final FunctionSet fs, VBox vb) { @@ -378,15 +377,22 @@ public class SettingsPane extends AnchorPane { * * @return true if the experiment needs to be reset, false otherwise. */ - private boolean applyChanges() { + private boolean isResetRequired() { boolean reset = false; - for (GUIParameter parameter : parameters) { - reset |= parameter.applyChange(); + reset |= parameter.requiresReset(); } - + System.out.println("reset: " + reset); return reset; } + private void resetExperiment() { + for (GUIParameter parameter : parameters) { + parameter.applyValue(); + } + gui.reset(); + + } + } diff --git a/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java b/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java index 8acd6dd..da564f7 100644 --- a/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java @@ -1,46 +1,71 @@ package jcgp.gui.settings.parameters; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; import javafx.scene.control.CheckBox; import jcgp.backend.parameters.BooleanParameter; +import jcgp.backend.parameters.ParameterStatus; +import jcgp.gui.GUI; public class GUIBooleanParameter extends GUIParameter { - GUIBooleanParameter(BooleanParameter parameter) { + private boolean originalValue; + + GUIBooleanParameter(final BooleanParameter parameter) { super(); this.parameter = parameter; - value = new CheckBox(parameter.getName()); - ((CheckBox) value).setSelected(parameter.get()); + originalValue = parameter.get(); + + valueControl = new CheckBox(parameter.getName()); + ((CheckBox) valueControl).setSelected(parameter.get()); - value.setDisable(parameter.isMonitor()); + valueControl.setDisable(parameter.isMonitor()); if (parameter.isMonitor()) { makeLightBinding(); - System.out.println("light binding made for " + name); + } else { + ((CheckBox) valueControl).selectedProperty().addListener(new ChangeListener() { + @Override + public void changed( + ObservableValue observable, + Boolean oldValue, Boolean newValue) { + parameter.set(newValue); + if (parameter.getStatus() == ParameterStatus.INVALID) { + valueControl.setStyle(GUI.INVALID_PARAMETER_STYLE); + valueControl.setTooltip(tooltip); + tooltip.setText(parameter.getStatus().getDetails()); + } else if (parameter.getStatus() == ParameterStatus.WARNING) { + valueControl.setStyle(GUI.WARNING_PARAMETER_STYLE); + valueControl.setTooltip(tooltip); + tooltip.setText(parameter.getStatus().getDetails()); + } else { + valueControl.setStyle(GUI.VALID_PARAMETER_STYLE); + valueControl.setTooltip(null); + } + } + }); } - getChildren().add(value); + getChildren().add(valueControl); } @Override public void refreshValue() { - if (!((CheckBox) value).selectedProperty().isBound()) { - ((CheckBox) value).setSelected(((BooleanParameter) parameter).get()); + if (!((CheckBox) valueControl).selectedProperty().isBound()) { + ((CheckBox) valueControl).setSelected(((BooleanParameter) parameter).get()); } - } - + @Override - public boolean applyChange() { - if (((CheckBox) value).isSelected() != ((BooleanParameter) parameter).get() && (!parameter.isMonitor())) { - // apply value - ((BooleanParameter) parameter).set(((CheckBox) value).isSelected()); - if (parameter.isCritical()) { - return true; - } - } - return false; + public boolean requiresReset() { + return parameter.isCritical() && ((BooleanParameter) parameter).get() != originalValue; + } + + @Override + public void applyValue() { + originalValue = ((BooleanParameter) parameter).get(); } } diff --git a/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java b/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java index 4b74c6e..a1b4526 100644 --- a/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java @@ -1,51 +1,104 @@ package jcgp.gui.settings.parameters; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.event.EventHandler; import javafx.geometry.Pos; import javafx.scene.control.TextField; +import javafx.scene.input.KeyEvent; import javafx.scene.layout.Priority; import javafx.scene.text.Text; import jcgp.backend.parameters.DoubleParameter; +import jcgp.backend.parameters.ParameterStatus; import jcgp.gui.GUI; public class GUIDoubleParameter extends GUIParameter { - GUIDoubleParameter(DoubleParameter parameter) { + private double originalValue; + + GUIDoubleParameter(final DoubleParameter parameter) { super(); this.parameter = parameter; + originalValue = parameter.get(); + name = new Text(parameter.getName()); - value = new TextField(String.valueOf(parameter.get())); + valueControl = new TextField(String.valueOf(parameter.get())); + valueControl.setStyle(GUI.VALID_PARAMETER_STYLE); - ((TextField) value).setAlignment(Pos.CENTER_RIGHT); + ((TextField) valueControl).setAlignment(Pos.CENTER_RIGHT); - setHgrow(value, Priority.ALWAYS); + setHgrow(valueControl, Priority.ALWAYS); name.setWrappingWidth(GUI.WRAP_WIDTH); - ((TextField) value).setEditable(!parameter.isMonitor()); + ((TextField) valueControl).setEditable(!parameter.isMonitor()); + // bind if monitor, else set changelistener if (parameter.isMonitor()) { makeLightBinding(); + } else { + valueControl.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler() { + public void handle( KeyEvent t ) { + char ch = t.getCharacter().toCharArray()[t.getCharacter().toCharArray().length - 1]; + if (!((ch >= '0' && ch <= '9') || (ch == '.' && !((TextField) valueControl).getText().contains(".")))) { + t.consume(); + } + } + }); + ((TextField) valueControl).textProperty().addListener(new ChangeListener() { + @Override + public void changed( + ObservableValue observable, + String oldValue, String newValue) { + if (!newValue.isEmpty()) { + parameter.set(Double.parseDouble(newValue)); + if (parameter.getStatus() == ParameterStatus.INVALID) { + valueControl.setStyle(GUI.INVALID_PARAMETER_STYLE); + valueControl.setTooltip(tooltip); + tooltip.setText(parameter.getStatus().getDetails()); + } else if (parameter.getStatus() == ParameterStatus.WARNING) { + valueControl.setStyle(GUI.WARNING_PARAMETER_STYLE); + valueControl.setTooltip(tooltip); + tooltip.setText(parameter.getStatus().getDetails()); + } else { + valueControl.setStyle(GUI.VALID_PARAMETER_STYLE); + valueControl.setTooltip(null); + } + } + + } + }); + valueControl.focusedProperty().addListener(new ChangeListener() { + @Override + public void changed( + ObservableValue observable, + Boolean oldValue, Boolean newValue) { + if (!newValue && ((TextField) valueControl).getText().isEmpty()) { + ((TextField) valueControl).setText(String.valueOf(parameter.get())); + } + } + }); } - getChildren().addAll(name, value); + + getChildren().addAll(name, valueControl); } @Override public void refreshValue() { - ((TextField) value).setText(String.valueOf(((DoubleParameter) parameter).get())); + ((TextField) valueControl).setText(String.valueOf(((DoubleParameter) parameter).get())); } - + @Override - public boolean applyChange() { - if (Double.valueOf(((TextField) value).getText()) != ((DoubleParameter) parameter).get() && (!parameter.isMonitor())) { - // apply value - ((DoubleParameter) parameter).set(Double.valueOf(((TextField) value).getText())); - if (parameter.isCritical()) { - return true; - } - } - return false; - } + public boolean requiresReset() { + return parameter.isCritical() && ((DoubleParameter) parameter).get() != originalValue; + } + + @Override + public void applyValue() { + originalValue = ((DoubleParameter) parameter).get(); + } + } diff --git a/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java b/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java index 70b78a6..bf1a61e 100644 --- a/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java @@ -1,49 +1,103 @@ package jcgp.gui.settings.parameters; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.event.EventHandler; import javafx.geometry.Pos; import javafx.scene.control.TextField; +import javafx.scene.input.KeyEvent; import javafx.scene.layout.Priority; import javafx.scene.text.Text; import jcgp.backend.parameters.IntegerParameter; +import jcgp.backend.parameters.ParameterStatus; import jcgp.gui.GUI; public class GUIIntegerParameter extends GUIParameter { - GUIIntegerParameter(IntegerParameter parameter) { + private int originalValue; + + GUIIntegerParameter(final IntegerParameter parameter) { super(); this.parameter = parameter; + originalValue = parameter.get(); + name = new Text(parameter.getName()); - value = new TextField(String.valueOf(parameter.get())); + valueControl = new TextField(String.valueOf(parameter.get())); + valueControl.setStyle(GUI.VALID_PARAMETER_STYLE); - ((TextField) value).setAlignment(Pos.CENTER_RIGHT); + ((TextField) valueControl).setAlignment(Pos.CENTER_RIGHT); - setHgrow(value, Priority.ALWAYS); + setHgrow(valueControl, Priority.ALWAYS); name.setWrappingWidth(GUI.WRAP_WIDTH); - ((TextField) value).setEditable(!parameter.isMonitor()); - + ((TextField) valueControl).setEditable(!parameter.isMonitor()); + + // bind if monitor, else set listeners if (parameter.isMonitor()) { makeLightBinding(); + } else { + valueControl.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler() { + public void handle( KeyEvent t ) { + char ch = t.getCharacter().toCharArray()[t.getCharacter().toCharArray().length - 1]; + if (!(ch >= '0' && ch <= '9')) { + t.consume(); + } + } + }); + ((TextField) valueControl).textProperty().addListener(new ChangeListener() { + @Override + public void changed( + ObservableValue observable, + String oldValue, String newValue) { + if (!newValue.isEmpty()) { + parameter.set(Integer.parseInt(newValue)); + if (parameter.getStatus() == ParameterStatus.INVALID) { + valueControl.setStyle(GUI.INVALID_PARAMETER_STYLE); + valueControl.setTooltip(tooltip); + tooltip.setText(parameter.getStatus().getDetails()); + } else if (parameter.getStatus() == ParameterStatus.WARNING) { + valueControl.setStyle(GUI.WARNING_PARAMETER_STYLE); + valueControl.setTooltip(tooltip); + tooltip.setText(parameter.getStatus().getDetails()); + } else { + valueControl.setStyle(GUI.VALID_PARAMETER_STYLE); + valueControl.setTooltip(null); + } + } + + } + }); + valueControl.focusedProperty().addListener(new ChangeListener() { + @Override + public void changed( + ObservableValue observable, + Boolean oldValue, Boolean newValue) { + if (!newValue && ((TextField) valueControl).getText().isEmpty()) { + ((TextField) valueControl).setText(String.valueOf(parameter.get())); + } + } + }); } - getChildren().addAll(name, value); + getChildren().addAll(name, valueControl); } @Override public void refreshValue() { - ((TextField) value).setText(String.valueOf(((IntegerParameter) parameter).get())); + ((TextField) valueControl).setText(String.valueOf(((IntegerParameter) parameter).get())); } @Override - public boolean applyChange() { - if (Integer.valueOf(((TextField) value).getText()) != ((IntegerParameter) parameter).get() && (!parameter.isMonitor())) { - // apply value - ((IntegerParameter) parameter).set(Integer.valueOf(((TextField) value).getText())); - return parameter.isCritical(); - } - return false; + public boolean requiresReset() { + return parameter.isCritical() && ((IntegerParameter) parameter).get() != originalValue; + } + + @Override + public void applyValue() { + originalValue = ((IntegerParameter) parameter).get(); } + } diff --git a/src/jcgp/gui/settings/parameters/GUIParameter.java b/src/jcgp/gui/settings/parameters/GUIParameter.java index a783d71..9d6f07a 100644 --- a/src/jcgp/gui/settings/parameters/GUIParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIParameter.java @@ -7,6 +7,7 @@ import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.geometry.Pos; import javafx.scene.control.Control; +import javafx.scene.control.Tooltip; import javafx.scene.layout.HBox; import javafx.scene.text.Text; import jcgp.backend.parameters.BooleanParameter; @@ -18,13 +19,19 @@ public abstract class GUIParameter extends HBox { protected Parameter parameter; protected Text name; - protected Control value; + protected Control valueControl; + + protected Tooltip tooltip; protected AtomicBoolean updateCheck = new AtomicBoolean(true); protected GUIParameter() { setAlignment(Pos.CENTER_LEFT); setSpacing(5); + + tooltip = new Tooltip(); + tooltip.setStyle("-fx-background-color: white; -fx-border-color: black; .page-corner {-fx-background-color: transparent;}"); + tooltip.setSkin(null); } protected final void makeLightBinding() { @@ -48,6 +55,10 @@ public abstract class GUIParameter extends HBox { public abstract void refreshValue(); + public abstract boolean requiresReset(); + + public abstract void applyValue(); + public static GUIParameter create(Parameter parameter) { if (parameter instanceof IntegerParameter) { return new GUIIntegerParameter((IntegerParameter) parameter); @@ -59,12 +70,4 @@ public abstract class GUIParameter extends HBox { throw new ClassCastException("No GUIParameter subclass exists for argument of type " + parameter.getClass()); } } - - /** - * Writes the GUI parameter value back to the resources parameter, - * if any changes were made. - * - * @return true if the experiment needs to be reset, false otherwise. - */ - public abstract boolean applyChange(); } -- cgit v1.2.3