From dbae5ce2e0765f229e11b692a2aba570286980f4 Mon Sep 17 00:00:00 2001 From: Eduardo Pedroni Date: Thu, 10 Apr 2014 16:57:30 +0100 Subject: Added manual test case evaluation to GUI --- src/jcgp/JCGP.java | 19 +- .../exceptions/ParameterMismatchException.java | 2 +- .../backend/modules/fitness/DigitalCircuit.java | 18 -- src/jcgp/backend/modules/fitness/Problem.java | 20 --- .../modules/fitness/SymbolicRegression.java | 18 -- .../backend/modules/fitness/TestCaseProblem.java | 114 ------------ .../backend/modules/problem/DigitalCircuit.java | 25 +++ src/jcgp/backend/modules/problem/Problem.java | 20 +++ .../modules/problem/SymbolicRegression.java | 26 +++ .../backend/modules/problem/TestCaseProblem.java | 156 +++++++++++++++++ src/jcgp/gui/GUI.java | 167 ++++++++++-------- src/jcgp/gui/console/GUIConsole.java | 4 +- src/jcgp/gui/population/ChromosomePane.java | 92 ++++++++-- src/jcgp/gui/population/FunctionSelector.java | 3 +- src/jcgp/gui/population/GUIGene.java | 32 ++-- src/jcgp/gui/population/GUIInput.java | 17 +- src/jcgp/gui/population/GUINode.java | 48 ++++-- src/jcgp/gui/population/GUIOutput.java | 26 ++- src/jcgp/gui/population/PopulationPane.java | 40 ++++- src/jcgp/gui/settings/SettingsPane.java | 191 ++++++++++++++------- .../settings/parameters/GUIBooleanParameter.java | 7 +- .../settings/parameters/GUIDoubleParameter.java | 7 +- .../settings/parameters/GUIIntegerParameter.java | 5 +- src/jcgp/gui/settings/parameters/GUIParameter.java | 19 +- src/jcgp/gui/settings/testcase/TestCaseTable.java | 90 ++++++++++ 25 files changed, 771 insertions(+), 395 deletions(-) delete mode 100644 src/jcgp/backend/modules/fitness/DigitalCircuit.java delete mode 100644 src/jcgp/backend/modules/fitness/Problem.java delete mode 100644 src/jcgp/backend/modules/fitness/SymbolicRegression.java delete mode 100644 src/jcgp/backend/modules/fitness/TestCaseProblem.java create mode 100644 src/jcgp/backend/modules/problem/DigitalCircuit.java create mode 100644 src/jcgp/backend/modules/problem/Problem.java create mode 100644 src/jcgp/backend/modules/problem/SymbolicRegression.java create mode 100644 src/jcgp/backend/modules/problem/TestCaseProblem.java create mode 100644 src/jcgp/gui/settings/testcase/TestCaseTable.java diff --git a/src/jcgp/JCGP.java b/src/jcgp/JCGP.java index 538f677..6d881d4 100644 --- a/src/jcgp/JCGP.java +++ b/src/jcgp/JCGP.java @@ -1,16 +1,13 @@ package jcgp; -import java.util.ArrayList; - import jcgp.backend.modules.es.EvolutionaryStrategy; import jcgp.backend.modules.es.MuPlusLambda; import jcgp.backend.modules.es.TournamentSelection; -import jcgp.backend.modules.fitness.DigitalCircuit; -import jcgp.backend.modules.fitness.Problem; -import jcgp.backend.modules.fitness.SymbolicRegression; -import jcgp.backend.modules.fitness.TestCaseProblem.TestCase; import jcgp.backend.modules.mutator.Mutator; import jcgp.backend.modules.mutator.PointMutator; +import jcgp.backend.modules.problem.DigitalCircuit; +import jcgp.backend.modules.problem.Problem; +import jcgp.backend.modules.problem.SymbolicRegression; import jcgp.backend.population.Population; import jcgp.backend.resources.Console; import jcgp.backend.resources.ModifiableResources; @@ -72,16 +69,6 @@ public class JCGP { setProblem(0); population = new Population(resources); - - ArrayList> tc = new ArrayList>(); - tc.add(new TestCase(new Integer[]{1, 2, 3}, new Integer[]{-4, 5, 6})); - - ((SymbolicRegression) problem).setTestCases(tc); - - ArrayList> tcdc = new ArrayList>(); - tcdc.add(new TestCase(new Integer[]{1, 2, 3}, new Integer[]{-4, 5, 6})); - - ((DigitalCircuit) problems[1]).setTestCases(tc); } public ModifiableResources getResources() { diff --git a/src/jcgp/backend/exceptions/ParameterMismatchException.java b/src/jcgp/backend/exceptions/ParameterMismatchException.java index 3ab9fa1..83e2ba2 100644 --- a/src/jcgp/backend/exceptions/ParameterMismatchException.java +++ b/src/jcgp/backend/exceptions/ParameterMismatchException.java @@ -6,5 +6,5 @@ public class ParameterMismatchException extends RuntimeException { * */ private static final long serialVersionUID = -3161318886125868134L; - + } diff --git a/src/jcgp/backend/modules/fitness/DigitalCircuit.java b/src/jcgp/backend/modules/fitness/DigitalCircuit.java deleted file mode 100644 index 8677d5f..0000000 --- a/src/jcgp/backend/modules/fitness/DigitalCircuit.java +++ /dev/null @@ -1,18 +0,0 @@ -package jcgp.backend.modules.fitness; - -import jcgp.backend.function.BitwiseLogic; -import jcgp.backend.resources.Resources; - -public class DigitalCircuit extends TestCaseProblem { - - public DigitalCircuit(Resources resources) { - super(resources); - functionSet = new BitwiseLogic(); - } - - @Override - public String toString() { - return "Digital circuit"; - } - -} diff --git a/src/jcgp/backend/modules/fitness/Problem.java b/src/jcgp/backend/modules/fitness/Problem.java deleted file mode 100644 index 1e70c13..0000000 --- a/src/jcgp/backend/modules/fitness/Problem.java +++ /dev/null @@ -1,20 +0,0 @@ -package jcgp.backend.modules.fitness; - -import jcgp.backend.function.FunctionSet; -import jcgp.backend.modules.Module; -import jcgp.backend.population.Chromosome; -import jcgp.backend.population.Population; -import jcgp.backend.resources.Resources; - -public abstract class Problem implements Module { - - protected FunctionSet functionSet; - - public abstract void evaluate(Population population, Resources resources); - - public FunctionSet getFunctionSet() { - return functionSet; - } - - public abstract boolean isPerfectSolution(Chromosome fittest); -} diff --git a/src/jcgp/backend/modules/fitness/SymbolicRegression.java b/src/jcgp/backend/modules/fitness/SymbolicRegression.java deleted file mode 100644 index cb9d1a7..0000000 --- a/src/jcgp/backend/modules/fitness/SymbolicRegression.java +++ /dev/null @@ -1,18 +0,0 @@ -package jcgp.backend.modules.fitness; - -import jcgp.backend.function.IntegerArithmetic; -import jcgp.backend.resources.Resources; - -public class SymbolicRegression extends TestCaseProblem { - - public SymbolicRegression(Resources resources) { - super(resources); - functionSet = new IntegerArithmetic(); - } - - @Override - public String toString() { - return "Symbolic regression"; - } - -} diff --git a/src/jcgp/backend/modules/fitness/TestCaseProblem.java b/src/jcgp/backend/modules/fitness/TestCaseProblem.java deleted file mode 100644 index 7dd24af..0000000 --- a/src/jcgp/backend/modules/fitness/TestCaseProblem.java +++ /dev/null @@ -1,114 +0,0 @@ -package jcgp.backend.modules.fitness; - -import java.util.ArrayList; -import java.util.List; - -import jcgp.backend.population.Chromosome; -import jcgp.backend.population.Population; -import jcgp.backend.resources.Resources; -import jcgp.backend.resources.parameters.IntegerParameter; -import jcgp.backend.resources.parameters.Parameter; - -/** - * - * This fitness function module implements a simple test case evaluator. - * - * A TestCase object is a - * - * - * @author Eduardo Pedroni - * - */ -public abstract class TestCaseProblem extends Problem { - - public static class TestCase { - - private T[] inputs; - private T[] outputs; - - public TestCase(T[] inputs, T[] outputs) { - this.inputs = inputs; - this.outputs = outputs; - } - - public T getInput(int index) { - return inputs[index]; - } - - public T getOutput(int index) { - return outputs[index]; - } - - public T[] getInputs() { - return inputs; - } - - public T[] getOutputs() { - return outputs; - } - } - - private ArrayList> testCases; - private IntegerParameter maxFitness; - - public TestCaseProblem(Resources resources) { - super(); - - maxFitness = new IntegerParameter(0, "Max fitness", true, false) { - @Override - public void validate(Number newValue) { - // blank - } - }; - testCases = new ArrayList>(); - } - - - @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; - // for each test case - for (int t = 0; t < testCases.size(); t++) { - population.getChromosome(i).setInputs(testCases.get(t).getInputs()); - // check each output - for (int o = 0; o < resources.outputs(); o++) { - if (population.getChromosome(i).getOutput(o).calculate() == testCases.get(t).getOutput(o)) { - fitness++; - } - } - } - // assign the resulting fitness to the respective individual - population.getChromosome(i).setFitness(fitness); - } - } - - @Override - public Parameter[] getLocalParameters() { - return new Parameter[]{maxFitness}; - } - - private int getMaxFitness() { - int fitness = 0; - - for (TestCase tc : testCases) { - fitness += tc.getOutputs().length; - } - - return fitness; - } - - public void setTestCases(List> testCases) { - this.testCases.clear(); - this.testCases.addAll(testCases); - maxFitness.set(getMaxFitness()); - } - - @Override - public boolean isPerfectSolution(Chromosome fittest) { - return fittest.getFitness() >= maxFitness.get(); - } -} - diff --git a/src/jcgp/backend/modules/problem/DigitalCircuit.java b/src/jcgp/backend/modules/problem/DigitalCircuit.java new file mode 100644 index 0000000..d94197d --- /dev/null +++ b/src/jcgp/backend/modules/problem/DigitalCircuit.java @@ -0,0 +1,25 @@ +package jcgp.backend.modules.problem; + +import java.util.ArrayList; + +import jcgp.backend.function.BitwiseLogic; +import jcgp.backend.resources.Resources; + +public class DigitalCircuit extends TestCaseProblem { + + public DigitalCircuit(Resources resources) { + super(resources); + functionSet = new BitwiseLogic(); + + ArrayList> tc = new ArrayList>(); + tc.add(new TestCase(new Integer[]{1, 2, 3}, new Integer[]{-4, 5, 6})); + + setTestCases(tc); + } + + @Override + public String toString() { + return "Digital circuit"; + } + +} diff --git a/src/jcgp/backend/modules/problem/Problem.java b/src/jcgp/backend/modules/problem/Problem.java new file mode 100644 index 0000000..d01f5b0 --- /dev/null +++ b/src/jcgp/backend/modules/problem/Problem.java @@ -0,0 +1,20 @@ +package jcgp.backend.modules.problem; + +import jcgp.backend.function.FunctionSet; +import jcgp.backend.modules.Module; +import jcgp.backend.population.Chromosome; +import jcgp.backend.population.Population; +import jcgp.backend.resources.Resources; + +public abstract class Problem implements Module { + + protected FunctionSet functionSet; + + public abstract void evaluate(Population population, Resources resources); + + public FunctionSet getFunctionSet() { + return functionSet; + } + + public abstract boolean isPerfectSolution(Chromosome fittest); +} diff --git a/src/jcgp/backend/modules/problem/SymbolicRegression.java b/src/jcgp/backend/modules/problem/SymbolicRegression.java new file mode 100644 index 0000000..c92d183 --- /dev/null +++ b/src/jcgp/backend/modules/problem/SymbolicRegression.java @@ -0,0 +1,26 @@ +package jcgp.backend.modules.problem; + +import java.util.ArrayList; + +import jcgp.backend.function.IntegerArithmetic; +import jcgp.backend.resources.Resources; + +public class SymbolicRegression extends TestCaseProblem { + + public SymbolicRegression(Resources resources) { + super(resources); + functionSet = new IntegerArithmetic(); + + ArrayList> tc = new ArrayList>(); + tc.add(new TestCase(new Integer[]{1, 2, 3}, new Integer[]{-4, 5, 6})); + tc.add(new TestCase(new Integer[]{3, 2, 5}, new Integer[]{2, 5, 9})); + + setTestCases(tc); + } + + @Override + public String toString() { + return "Symbolic regression"; + } + +} diff --git a/src/jcgp/backend/modules/problem/TestCaseProblem.java b/src/jcgp/backend/modules/problem/TestCaseProblem.java new file mode 100644 index 0000000..68318cf --- /dev/null +++ b/src/jcgp/backend/modules/problem/TestCaseProblem.java @@ -0,0 +1,156 @@ +package jcgp.backend.modules.problem; + +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import jcgp.backend.population.Chromosome; +import jcgp.backend.population.Population; +import jcgp.backend.resources.Resources; +import jcgp.backend.resources.parameters.IntegerParameter; +import jcgp.backend.resources.parameters.Parameter; + +/** + * + * This fitness function module implements a simple test case evaluator. + * + * A TestCase object is a + * + * + * @author Eduardo Pedroni + * + */ +public abstract class TestCaseProblem extends Problem { + + public static class TestCase { + private T[] inputs; + private T[] outputs; + + public TestCase(T[] inputs, T[] outputs) { + this.inputs = inputs; + this.outputs = outputs; + } + + public T getInput(int index) { + return inputs[index]; + } + + public T getOutput(int index) { + return outputs[index]; + } + + public T[] getInputs() { + return inputs; + } + + public T[] getOutputs() { + return outputs; + } + } + + private ObservableList> testCases; + private IntegerParameter maxFitness; + private final int inputCount, outputCount; + + private U type; + + public TestCaseProblem(Resources resources) { + super(); + + inputCount = resources.inputs(); + outputCount = resources.outputs(); + + maxFitness = new IntegerParameter(0, "Max fitness", true, false) { + @Override + public void validate(Number newValue) { + // blank + } + }; + testCases = FXCollections.observableArrayList(); + //testCases = new ObservableList>(); + } + + + @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; + // for each test case + for (int t = 0; t < testCases.size(); t++) { + population.getChromosome(i).setInputs(testCases.get(t).getInputs()); + // check each output + for (int o = 0; o < resources.outputs(); o++) { + if (population.getChromosome(i).getOutput(o).calculate() == testCases.get(t).getOutput(o)) { + fitness++; + } + } + } + // assign the resulting fitness to the respective individual + population.getChromosome(i).setFitness(fitness); + } + } + + @Override + public Parameter[] getLocalParameters() { + return new Parameter[]{maxFitness}; + } + + @Override + public boolean isPerfectSolution(Chromosome fittest) { + return fittest.getFitness() >= maxFitness.get(); + } + + private int getMaxFitness() { + int fitness = 0; + + for (TestCase tc : testCases) { + fitness += tc.getOutputs().length; + } + + return fitness; + } + + public void setTestCases(List> testCases) { + this.testCases.clear(); + this.testCases.addAll(testCases); + maxFitness.set(getMaxFitness()); + } + + public ObservableList> getTestCases() { + return testCases; + } + + public void addTestCase(TestCase testCase) { + if (testCase.getInputs().length != inputCount) { + throw new IllegalArgumentException("Received test case with " + testCase.getInputs().length + + "inputs but need exactly " + inputCount); + } else if (testCase.getOutputs().length != outputCount) { + throw new IllegalArgumentException("Received test case with " + testCase.getOutputs().length + + "outputs but need exactly " + outputCount); + } else { + this.testCases.add(testCase); + maxFitness.set(getMaxFitness()); + } + } + + public void removeTestCase(TestCase testCase) { + testCases.remove(testCase); + maxFitness.set(getMaxFitness()); + } + + public int getInputCount() { + return inputCount; + } + + public int getOutputCount() { + return outputCount; + } + + public Class getType() { + return type.getClass(); + } +} + + diff --git a/src/jcgp/gui/GUI.java b/src/jcgp/gui/GUI.java index 5a7a16d..6d87de1 100644 --- a/src/jcgp/gui/GUI.java +++ b/src/jcgp/gui/GUI.java @@ -4,24 +4,26 @@ import javafx.application.Application; import javafx.application.Platform; import javafx.concurrent.Service; import javafx.concurrent.Task; +import javafx.event.EventHandler; import javafx.scene.Scene; +import javafx.scene.input.MouseEvent; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; import javafx.stage.Stage; +import javafx.stage.WindowEvent; import jcgp.JCGP; -import jcgp.backend.resources.Resources; +import jcgp.backend.modules.problem.TestCaseProblem.TestCase; import jcgp.gui.console.GUIConsole; import jcgp.gui.dragresize.HorizontalDragResize; import jcgp.gui.dragresize.VerticalDragResize; import jcgp.gui.population.FunctionSelector; +import jcgp.gui.population.GUINode; import jcgp.gui.population.PopulationPane; import jcgp.gui.settings.SettingsPane; public class GUI extends Application { /* Colours */ - /** NEUTRAL_COLOUR is the colour elements should be when no interaction is occurring - * (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"; public static final String MEDIUM_HIGHLIGHT_COLOUR = "#75BAFF"; @@ -33,16 +35,12 @@ public class GUI extends Application { /* Sizes and distances */ public static final double RESIZE_MARGIN = 5.0; + public static final double SETTINGS_MIN_WIDTH = 200; + public static final double CONSOLE_MIN_HEIGHT = 100; - public static final double SETTINGS_WIDTH = 200; - public static final double CONSOLE_HEIGHT = 100; - - public static final double WRAP_WIDTH = 90; - - public static final JCGP jcgp = new JCGP(); - public static final Resources resources = jcgp.getResources(); + private final JCGP jcgp; - public static final FunctionSelector functionSelector = new FunctionSelector(resources.getFunctionSet()); + private final FunctionSelector functionSelector; private PopulationPane populationPane; @@ -50,53 +48,62 @@ public class GUI extends Application { private SettingsPane settingsPane; - private static boolean working = false; - - private Object printLock = new Object(); - - private Service cgpService = new Service () { - @Override - protected Task createTask() { - Task t = new Task() { - @Override - protected Void call() throws Exception { - while (!isCancelled() && !jcgp.isFinished()) { - synchronized (printLock) { - Platform.runLater(consoleFlush); - jcgp.nextGeneration(); - printLock.wait(); - } - } - if (jcgp.isFinished()) { - Platform.runLater(new Runnable() { - @Override - public void run() { - runningMode(false); - } - }); - } - return null; - } - }; - return t; - } - }; - - private Runnable consoleFlush = new Runnable() { - @Override - public void run() { - synchronized(printLock) { - console.flush(); - printLock.notifyAll(); - } - } - }; + private boolean running = false; + + private final Object printLock = new Object(); + + private Service cgpService; + + private Runnable consoleFlush; public static void main(String[] args) { - + new GUI(); launch(); } + public GUI() { + jcgp = new JCGP(); + functionSelector = new FunctionSelector(jcgp.getResources().getFunctionSet()); + + consoleFlush = new Runnable() { + @Override + public void run() { + synchronized(printLock) { + console.flush(); + printLock.notifyAll(); + } + } + }; + + cgpService = new Service () { + @Override + protected Task createTask() { + Task t = new Task() { + @Override + protected Void call() throws Exception { + while (!isCancelled() && !jcgp.isFinished()) { + synchronized (printLock) { + Platform.runLater(consoleFlush); + jcgp.nextGeneration(); + printLock.wait(); + } + } + if (jcgp.isFinished()) { + Platform.runLater(new Runnable() { + @Override + public void run() { + runningMode(false); + } + }); + } + return null; + } + }; + return t; + } + }; + } + @Override public void start(Stage primaryStage) throws Exception { console = new GUIConsole(); @@ -109,9 +116,9 @@ public class GUI extends Application { */ BorderPane leftFrame = new BorderPane(); - populationPane = new PopulationPane(jcgp); + populationPane = new PopulationPane(this); - settingsPane = new SettingsPane(jcgp, this); + settingsPane = new SettingsPane(this); HorizontalDragResize.makeDragResizable(settingsPane); VerticalDragResize.makeDragResizable(console); @@ -135,11 +142,18 @@ public class GUI extends Application { primaryStage.setMinWidth(800); primaryStage.setMinHeight(600); primaryStage.show(); + + primaryStage.setOnCloseRequest(new EventHandler() { + @Override + public void handle(WindowEvent event) { + settingsPane.getTestCaseTable().close(); + } + }); } public void runPause() { if (!jcgp.isFinished() && settingsPane.areParametersValid()) { - if (!working) { + if (!running) { runningMode(true); cgpService.restart(); } else { @@ -150,7 +164,7 @@ public class GUI extends Application { } public void step() { - if (!working && !jcgp.isFinished() && settingsPane.areParametersValid()) { + if (!running && !jcgp.isFinished() && settingsPane.areParametersValid()) { if (settingsPane.isResetRequired()) { reset(); } @@ -166,20 +180,18 @@ public class GUI extends Application { } public void reset() { - if (!working && settingsPane.areParametersValid()) { + if (!running && settingsPane.areParametersValid()) { settingsPane.applyParameters(); jcgp.reset(); - populationPane.remakeTabs(jcgp.getPopulation(), jcgp.getResources()); + populationPane.remakeTabs(); settingsPane.revalidateParameters(); + System.out.println("[reset] call: " + jcgp.isFinished()); settingsPane.updateControls(false, jcgp.isFinished()); console.flush(); } } - private void runningMode(boolean value) { - populationPane.setDisable(value); - settingsPane.updateControls(value, jcgp.isFinished()); - + private void runningMode(boolean value) { if (value) { populationPane.unlockOutputs(); if (settingsPane.isResetRequired()) { @@ -190,14 +202,33 @@ public class GUI extends Application { populationPane.relockOutputs(); settingsPane.revalidateParameters(); } - working = value; + populationPane.setDisable(value); + settingsPane.updateControls(value, jcgp.isFinished()); + + running = value; } - public static void updateFunctionSelector() { - functionSelector.remakeFunctions(resources.getFunctionSet()); + public void updateFunctionSelector() { + functionSelector.remakeFunctions(jcgp.getResources().getFunctionSet()); + } + + public boolean isWorking() { + return running; + } + + public void bringFunctionSelector(MouseEvent event, GUINode node) { + functionSelector.relocateAndShow(event, node); + } + + public JCGP getExperiment() { + return jcgp; } - public static boolean isWorking() { - return working; + public void evaluateTestCase(TestCase testCase) { + populationPane.evaluateTestCase(testCase); + } + + public void hideGeneValues() { + populationPane.hideValues(); } } diff --git a/src/jcgp/gui/console/GUIConsole.java b/src/jcgp/gui/console/GUIConsole.java index 8148ec8..e77e222 100644 --- a/src/jcgp/gui/console/GUIConsole.java +++ b/src/jcgp/gui/console/GUIConsole.java @@ -82,8 +82,8 @@ public class GUIConsole extends AnchorPane implements Console { AnchorPane.setRightAnchor(textArea, 0.0); AnchorPane.setLeftAnchor(textArea, 0.0); - setMinHeight(GUI.CONSOLE_HEIGHT); - setPrefHeight(GUI.CONSOLE_HEIGHT); + setMinHeight(GUI.CONSOLE_MIN_HEIGHT); + setPrefHeight(GUI.CONSOLE_MIN_HEIGHT); getChildren().add(textArea); } diff --git a/src/jcgp/gui/population/ChromosomePane.java b/src/jcgp/gui/population/ChromosomePane.java index 1f67255..0279d09 100644 --- a/src/jcgp/gui/population/ChromosomePane.java +++ b/src/jcgp/gui/population/ChromosomePane.java @@ -10,7 +10,7 @@ import jcgp.backend.population.Connection; import jcgp.backend.population.Input; import jcgp.backend.population.Node; import jcgp.backend.resources.Resources; - +import jcgp.gui.GUI; public class ChromosomePane extends ScrollPane { @@ -21,15 +21,23 @@ public class ChromosomePane extends ScrollPane { private Pane content; private ArrayList connectionLines; - private ArrayList relock = new ArrayList(); - + + private int rows, columns; + private boolean target = false; + private boolean evaluating = false; - public ChromosomePane(Chromosome chromosome, Resources resources) { - super(); + public ChromosomePane(Chromosome chromosome, GUI gui) { + super(); + + final Resources resources = gui.getExperiment().getResources(); + + rows = resources.rows(); + columns = resources.columns(); + connectionLines = new ArrayList(); - + content = new Pane(); content.setId("content pane for genes"); @@ -42,10 +50,10 @@ public class ChromosomePane extends ScrollPane { content.getChildren().addAll(guiInputs[i]); } // nodes - guiNodes = new GUINode[resources.rows()][resources.columns()]; + guiNodes = new GUINode[rows][columns]; double angle, xPos, yPos; - for (int r = 0; r < guiNodes.length; r++) { - for (int c = 0; c < guiNodes[r].length; c++) { + for (int r = 0; r < rows; r++) { + for (int c = 0; c < columns; c++) { // make the connection lines Line lines[] = new Line[resources.arity()]; for (int l = 0; l < lines.length; l++) { @@ -59,7 +67,7 @@ public class ChromosomePane extends ScrollPane { connectionLines.add(lines[l]); } // make the GUI elements - guiNodes[r][c] = new GUINode(this, chromosome.getNode(r, c), lines); + guiNodes[r][c] = new GUINode(this, chromosome.getNode(r, c), lines, gui); } content.getChildren().addAll(guiNodes[r]); } @@ -74,7 +82,7 @@ public class ChromosomePane extends ScrollPane { line.setVisible(false); connectionLines.add(line); // make the GUI elements - guiOutputs[i] = new GUIOutput(this, chromosome.getOutput(i), line); + guiOutputs[i] = new GUIOutput(this, chromosome.getOutput(i), line, gui); content.getChildren().addAll(guiOutputs[i]); } @@ -83,7 +91,7 @@ public class ChromosomePane extends ScrollPane { setContent(content); } - public GUIGene getGuiGene(Connection gene) { + protected GUIGene getGuiGene(Connection gene) { if (gene instanceof Input) { return guiInputs[((Input) gene).getIndex()]; } else if (gene instanceof Node) { @@ -94,24 +102,28 @@ public class ChromosomePane extends ScrollPane { } } - public boolean isTarget() { + protected boolean isTarget() { return target; } - public void setTarget(boolean newValue) { + protected void setTarget(boolean newValue) { target = newValue; } public void updateGenes() { - for (int r = 0; r < guiNodes.length; r++) { - for (int c = 0; c < guiNodes[r].length; c++) { + for (int r = 0; r < rows; r++) { + for (int c = 0; c < columns; c++) { guiNodes[r][c].updateLines(); - guiNodes[r][c].updateFunction(); + guiNodes[r][c].updateText(); } } for (int i = 0; i < guiOutputs.length; i++) { guiOutputs[i].updateLines(); } + if (isEvaluating()) { + evaluate(0); + } + } public void unlockOutputs() { @@ -129,4 +141,50 @@ public class ChromosomePane extends ScrollPane { relock.get(i).lock(); } } + + public void setInputs(Object[] values) { + evaluating = true; + for (int i = 0; i < guiInputs.length; i++) { + guiInputs[i].setValue(values[i]); + guiInputs[i].updateText(); + } + evaluate(0); + } + + 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 hideValues() { + evaluating = false; + for (int i = 0; i < guiInputs.length; i++) { + guiInputs[i].updateText(); + } + 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(); + } + } + + /** + * @return the evaluating + */ + public boolean isEvaluating() { + return evaluating; + } } diff --git a/src/jcgp/gui/population/FunctionSelector.java b/src/jcgp/gui/population/FunctionSelector.java index 0a9606f..28eb54d 100644 --- a/src/jcgp/gui/population/FunctionSelector.java +++ b/src/jcgp/gui/population/FunctionSelector.java @@ -50,8 +50,7 @@ public class FunctionSelector extends VBox { l.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler() { @Override public void handle(MouseEvent event) { - target.getGene().setFunction(fs.getAllowedFunction(index)); - target.updateFunction(); + target.setFunction(fs.getAllowedFunction(index)); dismiss(); } }); diff --git a/src/jcgp/gui/population/GUIGene.java b/src/jcgp/gui/population/GUIGene.java index 6e9d098..5ce839e 100644 --- a/src/jcgp/gui/population/GUIGene.java +++ b/src/jcgp/gui/population/GUIGene.java @@ -10,18 +10,6 @@ import javafx.scene.text.TextAlignment; import jcgp.backend.population.Connection; import jcgp.backend.population.Gene; -enum GUIGeneState { - NEUTRAL, - HOVER, - INDIRECT_HOVER, - ACTIVE_HOVER, - LOCKED_HOVER, - SOURCE, - TARGET, - NO_CHANGE_TARGET, - FORBIDDEN_TARGET -} - public abstract class GUIGene extends Group { public static final double NODE_RADIUS = 30; @@ -32,6 +20,18 @@ public abstract class GUIGene extends Group { public static final double NODE_TEXT = NODE_RADIUS / 2.5; + public enum GUIGeneState { + NEUTRAL, + HOVER, + INDIRECT_HOVER, + ACTIVE_HOVER, + LOCKED_HOVER, + SOURCE, + TARGET, + NO_CHANGE_TARGET, + FORBIDDEN_TARGET + } + protected Text text = new Text(); protected Circle mainCircle = new Circle(NODE_RADIUS, Paint.valueOf("white")); @@ -40,6 +40,8 @@ public abstract class GUIGene extends Group { protected ChromosomePane parent; protected int locked = 0; + + protected Object value; public GUIGene() { text.setFont(Font.font("Arial", 12)); @@ -73,7 +75,7 @@ public abstract class GUIGene extends Group { public abstract void addLocks(int value); /** - * test + * * * @param value */ @@ -93,5 +95,9 @@ 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 b4eccaa..6a783db 100644 --- a/src/jcgp/gui/population/GUIInput.java +++ b/src/jcgp/gui/population/GUIInput.java @@ -23,8 +23,7 @@ public class GUIInput extends GUIGene { relocate(NODE_RADIUS, (input.getIndex() * (2 * NODE_RADIUS + SPACING)) + NODE_RADIUS); - text.setText("I: " + input.getIndex()); - + updateText(); Circle outputSocket = new Circle(NODE_RADIUS, 0, SOCKET_RADIUS, Paint.valueOf("white")); outputSocket.setId(String.valueOf(0)); @@ -232,5 +231,19 @@ public class GUIInput extends GUIGene { @Override public void setConnectionLine(GUIGene gene) { // nothing + } + + public void setValue(Object newValue) { + value = newValue; + input.setValue(newValue); + } + + @Override + public void updateText() { + if (parent.isEvaluating()) { + text.setText("I: " + input.getIndex() + "\nValue: " + value.toString()); + } else { + text.setText("I: " + input.getIndex()); + } } } diff --git a/src/jcgp/gui/population/GUINode.java b/src/jcgp/gui/population/GUINode.java index 8e2d8a0..2b953c0 100644 --- a/src/jcgp/gui/population/GUINode.java +++ b/src/jcgp/gui/population/GUINode.java @@ -7,24 +7,29 @@ import javafx.scene.input.MouseEvent; import javafx.scene.paint.Paint; import javafx.scene.shape.Circle; import javafx.scene.shape.Line; +import jcgp.backend.function.Function; import jcgp.backend.population.Connection; import jcgp.backend.population.Input; import jcgp.backend.population.Node; +import jcgp.backend.resources.Resources; import jcgp.gui.GUI; public class GUINode extends GUIGene { private Line[] lines; private Node node; + private Resources resources; private int connectionIndex = 0; + - public GUINode(ChromosomePane parentRef, final Node node, Line[] connectionLines) { + public GUINode(ChromosomePane parentRef, final Node node, Line[] connectionLines, final GUI gui) { super(); // store references this.parent = parentRef; this.node = node; this.lines = connectionLines; + this.resources = gui.getExperiment().getResources(); // move the GUIGene to the right position relocate(((node.getColumn() + 1) * (2 * NODE_RADIUS + SPACING)) + NODE_RADIUS, @@ -40,12 +45,12 @@ public class GUINode extends GUIGene { Circle output = new Circle(NODE_RADIUS, 0, SOCKET_RADIUS, Paint.valueOf("white")); output.setStroke(Paint.valueOf("black")); - text.setText(node.getFunction().getName()); + updateText(); - Circle[] sockets = new Circle[GUI.resources.arity()]; + Circle[] sockets = new Circle[resources.arity()]; double angle, xPos, yPos; for (int l = 0; l < sockets.length; l++) { - angle = (((l + 1) / ((double) (GUI.resources.arity() + 1))) * THETA) - (THETA / 2); + angle = (((l + 1) / ((double) (resources.arity() + 1))) * THETA) - (THETA / 2); xPos = -Math.cos(angle) * NODE_RADIUS; yPos = Math.sin(angle) * NODE_RADIUS; @@ -128,7 +133,7 @@ public class GUINode extends GUIGene { addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler() { @Override public void handle(MouseEvent event) { - GUI.functionSelector.relocateAndShow(event, (GUINode) event.getSource()); + gui.bringFunctionSelector(event, (GUINode) event.getSource()); } }); @@ -189,17 +194,16 @@ public class GUINode extends GUIGene { // the user released the drag gesture on this node, react appropriately if (isAllowed((GUIGene) event.getGestureSource(), (GUIGene) event.getSource())) { if (source.isLocked()) { - // remove locks from the old connection, add the to the new + // remove locks from the old connection, add the to setConnethe new // note that the old connection may still have locks after this parent.getGuiGene(source.getChangingConnection()).removeLocks(source.getLocks()); - source.setChangingConnection(node); addLocks(source.getLocks()); } else { if (source instanceof GUIOutput) { source.resetState(); } - source.setChangingConnection(node); } + source.setChangingConnection(node); } source.updateLines(); @@ -316,7 +320,7 @@ public class GUINode extends GUIGene { } else if (target instanceof GUINode) { // target and source are nodes, let's look at levels back Node t = ((GUINode) target).getGene(), s = ((GUINode) source).getGene(); - if (s.getColumn() - t.getColumn() > 0 && s.getColumn() - t.getColumn() <= GUI.resources.levelsBack()) { + if (s.getColumn() - t.getColumn() > 0 && s.getColumn() - t.getColumn() <= resources.levelsBack()) { return true; } return false; @@ -395,6 +399,9 @@ public class GUINode extends GUIGene { @Override public void setChangingConnection(Connection newConnection) { node.setConnection(connectionIndex, newConnection); + if (parent.isEvaluating()) { + parent.evaluate(node.getColumn()); + } } @@ -445,7 +452,26 @@ public class GUINode extends GUIGene { lines[connectionIndex].setEndY(gene.getLayoutY()); } - public void updateFunction() { - text.setText(node.getFunction().getName()); + public void updateText() { + if (parent.isEvaluating()) { + text.setText(node.getFunction().getName() + "\nValue: " + value.toString()); + } else { + text.setText(node.getFunction().getName()); + } + } + + public void calculate() { + value = node.getValue(); + } + + public void setFunction(Function function) { + node.setFunction(function); + if (parent.isEvaluating()) { + calculate(); + parent.evaluate(node.getColumn()); + } else { + updateText(); + } + } } diff --git a/src/jcgp/gui/population/GUIOutput.java b/src/jcgp/gui/population/GUIOutput.java index c5b889e..89c12b3 100644 --- a/src/jcgp/gui/population/GUIOutput.java +++ b/src/jcgp/gui/population/GUIOutput.java @@ -16,23 +16,21 @@ import jcgp.gui.GUI; public class GUIOutput extends GUIGene { private Line sourceLine; - private Output output; - public GUIOutput(ChromosomePane parentRef, final Output output, Line line) { + public GUIOutput(ChromosomePane parentRef, final Output output, Line line, GUI gui) { super(); this.parent = parentRef; this.output = output; this.sourceLine = line; - relocate(((GUI.resources.columns() + 1) * (2 * NODE_RADIUS + SPACING)) + NODE_RADIUS, + relocate(((gui.getExperiment().getResources().columns() + 1) * (2 * NODE_RADIUS + SPACING)) + NODE_RADIUS, (output.getIndex() * (2 * NODE_RADIUS + SPACING)) + NODE_RADIUS); // set the line ends correctly updateLines(); - - text.setText("O: " + output.getIndex()); + updateText(); Circle socket = new Circle(-NODE_RADIUS, 0, SOCKET_RADIUS, Paint.valueOf("white")); socket.setId(String.valueOf(0)); @@ -274,6 +272,10 @@ public class GUIOutput extends GUIGene { @Override public void setChangingConnection(Connection newConnection) { output.setConnection(0, newConnection); + if (parent.isEvaluating()) { + calculate(); + updateText(); + } } @Override @@ -311,5 +313,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() + "\nValue: " + value.toString()); + } else { + text.setText("O: " + output.getIndex()); + } + + } } diff --git a/src/jcgp/gui/population/PopulationPane.java b/src/jcgp/gui/population/PopulationPane.java index 5232eb5..64b61c4 100644 --- a/src/jcgp/gui/population/PopulationPane.java +++ b/src/jcgp/gui/population/PopulationPane.java @@ -3,26 +3,29 @@ package jcgp.gui.population; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; import jcgp.JCGP; -import jcgp.backend.population.Population; -import jcgp.backend.resources.Resources; +import jcgp.backend.modules.problem.TestCaseProblem; +import jcgp.backend.modules.problem.TestCaseProblem.TestCase; +import jcgp.gui.GUI; public class PopulationPane extends TabPane { - public PopulationPane(JCGP jcgp) { + private GUI gui; + + public PopulationPane(GUI gui) { super(); - + this.gui = gui; setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE); - - remakeTabs(jcgp.getPopulation(), jcgp.getResources()); + remakeTabs(); } - public void remakeTabs(Population population, Resources resources) { + public void remakeTabs() { getTabs().clear(); + JCGP jcgp = gui.getExperiment(); Tab tab; ChromosomePane cp; - for (int i = 0; i < resources.populationSize(); i++) { - cp = new ChromosomePane(population.getChromosome(i), resources); + for (int i = 0; i < jcgp.getResources().populationSize(); i++) { + cp = new ChromosomePane(jcgp.getPopulation().getChromosome(i), gui); tab = new Tab("Chr " + i); tab.setContent(cp); getTabs().add(tab); @@ -46,4 +49,23 @@ public class PopulationPane extends TabPane { ((ChromosomePane) getTabs().get(i).getContent()).relockOutputs(); } } + + public void evaluateTestCase(TestCase testCase) { + if (gui.getExperiment().getProblem() instanceof TestCaseProblem) { + if (testCase.getInputs().length == gui.getExperiment().getResources().inputs()) { + for (int i = 0; i < getTabs().size(); i++) { + ((ChromosomePane) getTabs().get(i).getContent()).setInputs(testCase.getInputs()); + } + } else { + throw new IllegalArgumentException("Test case has " + testCase.getInputs().length + + " inputs and chromosome has " + gui.getExperiment().getResources().inputs()); + } + } + } + + public void hideValues() { + for (int i = 0; i < getTabs().size(); i++) { + ((ChromosomePane) getTabs().get(i).getContent()).hideValues(); + } + } } diff --git a/src/jcgp/gui/settings/SettingsPane.java b/src/jcgp/gui/settings/SettingsPane.java index e455846..9fcee8f 100644 --- a/src/jcgp/gui/settings/SettingsPane.java +++ b/src/jcgp/gui/settings/SettingsPane.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Insets; +import javafx.scene.Group; import javafx.scene.control.Button; import javafx.scene.control.CheckBox; import javafx.scene.control.ComboBox; @@ -18,11 +19,13 @@ import javafx.scene.text.Text; import jcgp.JCGP; import jcgp.backend.function.FunctionSet; import jcgp.backend.modules.es.EvolutionaryStrategy; -import jcgp.backend.modules.fitness.Problem; import jcgp.backend.modules.mutator.Mutator; +import jcgp.backend.modules.problem.Problem; +import jcgp.backend.modules.problem.TestCaseProblem; import jcgp.backend.resources.parameters.Parameter; import jcgp.gui.GUI; import jcgp.gui.settings.parameters.GUIParameter; +import jcgp.gui.settings.testcase.TestCaseTable; public class SettingsPane extends AnchorPane { @@ -33,23 +36,29 @@ public class SettingsPane extends AnchorPane { private Button loadParameters = new Button("Load parameters"), loadChromosome = new Button("Load chromosome"), saveChromosome = new Button("Save chromosome"); private ArrayList> parameters = new ArrayList>(); + + private TestCaseTable testCaseTable; + + private GUI gui; - public SettingsPane(JCGP cgp, GUI gui) { + public SettingsPane(GUI gui) { super(); + this.gui = gui; + final JCGP jcgp = gui.getExperiment(); mainContainer = new VBox(8); mainContainer.setPadding(new Insets(5, GUI.RESIZE_MARGIN, 0, 2)); - setMinWidth(GUI.SETTINGS_WIDTH); - setPrefWidth(GUI.SETTINGS_WIDTH); + setMinWidth(GUI.SETTINGS_MIN_WIDTH); + setPrefWidth(GUI.SETTINGS_MIN_WIDTH); - initialiseBaseParameters(cgp); + initialiseBaseParameters(jcgp); - initialiseEAParameters(cgp); + initialiseEAParameters(jcgp); - initialiseMutatorParameters(cgp); + initialiseMutatorParameters(jcgp); - initialiseProblemTypeParameters(cgp, gui); + initialiseProblemTypeParameters(jcgp, gui); createControls(gui); @@ -66,7 +75,7 @@ public class SettingsPane extends AnchorPane { getChildren().add(scroll); } - private void initialiseBaseParameters(JCGP cgp) { + private void initialiseBaseParameters(JCGP jcgp) { baseParameterPane = new VBox(2); Text header = new Text("Base Parameters"); @@ -75,32 +84,32 @@ public class SettingsPane extends AnchorPane { baseParameterPane.getChildren().add(header); - parameters.add(GUIParameter.create(cgp.getResources().getRowsParameter(), this)); - parameters.add(GUIParameter.create(cgp.getResources().getColumnsParameter(), this)); - parameters.add(GUIParameter.create(cgp.getResources().getInputsParameter(), this)); - parameters.add(GUIParameter.create(cgp.getResources().getOutputsParameter(), this)); - parameters.add(GUIParameter.create(cgp.getResources().getLevelsBackParameter(), this)); + parameters.add(GUIParameter.create(jcgp.getResources().getRowsParameter(), this)); + parameters.add(GUIParameter.create(jcgp.getResources().getColumnsParameter(), this)); + parameters.add(GUIParameter.create(jcgp.getResources().getInputsParameter(), this)); + parameters.add(GUIParameter.create(jcgp.getResources().getOutputsParameter(), this)); + parameters.add(GUIParameter.create(jcgp.getResources().getLevelsBackParameter(), this)); - GUIParameter gp = GUIParameter.create(cgp.getResources().getPopulationSizeParameter(), this); + GUIParameter gp = GUIParameter.create(jcgp.getResources().getPopulationSizeParameter(), this); gp.setPadding(new Insets(0, 0, 10, 0)); parameters.add(gp); - parameters.add(GUIParameter.create(cgp.getResources().getCurrentGenerationParameter(), this)); - parameters.add(GUIParameter.create(cgp.getResources().getGenerationsParameter(), this)); - parameters.add(GUIParameter.create(cgp.getResources().getCurrentRunParameter(), this)); + parameters.add(GUIParameter.create(jcgp.getResources().getCurrentGenerationParameter(), this)); + parameters.add(GUIParameter.create(jcgp.getResources().getGenerationsParameter(), this)); + parameters.add(GUIParameter.create(jcgp.getResources().getCurrentRunParameter(), this)); - gp = GUIParameter.create(cgp.getResources().getRunsParameter(), this); + gp = GUIParameter.create(jcgp.getResources().getRunsParameter(), this); gp.setPadding(new Insets(0, 0, 10, 0)); parameters.add(gp); - parameters.add(GUIParameter.create(cgp.getResources().getSeedParameter(), this)); - parameters.add(GUIParameter.create(cgp.getResources().getReportParameter(), this)); + parameters.add(GUIParameter.create(jcgp.getResources().getSeedParameter(), this)); + parameters.add(GUIParameter.create(jcgp.getResources().getReportParameter(), this)); baseParameterPane.getChildren().addAll(parameters); mainContainer.getChildren().add(baseParameterPane); } - private void initialiseEAParameters(final JCGP cgp) { + private void initialiseEAParameters(final JCGP jcgp) { eaPane = new VBox(2); Text header = new Text("Evolutionary Strategy"); @@ -108,21 +117,21 @@ public class SettingsPane extends AnchorPane { header.setUnderline(true); final ComboBox eaCBox = new ComboBox(); - eaCBox.getItems().addAll(cgp.getEvolutionaryStrategies()); - eaCBox.getSelectionModel().select(cgp.getEvolutionaryStrategy()); + eaCBox.getItems().addAll(jcgp.getEvolutionaryStrategies()); + eaCBox.getSelectionModel().select(jcgp.getEvolutionaryStrategy()); eaCBox.prefWidthProperty().bind(mainContainer.widthProperty()); final VBox eaParameters = new VBox(); eaParameters.setSpacing(2); - if (cgp.getEvolutionaryStrategy().getLocalParameters() != null) { - refreshParameters(cgp.getEvolutionaryStrategy().getLocalParameters(), eaParameters); + if (jcgp.getEvolutionaryStrategy().getLocalParameters() != null) { + refreshParameters(jcgp.getEvolutionaryStrategy().getLocalParameters(), eaParameters); } eaCBox.setOnAction(new EventHandler() { @Override public void handle(ActionEvent event) { - cgp.setEvolutionaryStrategy(eaCBox.getSelectionModel().getSelectedIndex()); + jcgp.setEvolutionaryStrategy(eaCBox.getSelectionModel().getSelectedIndex()); if (eaCBox.getSelectionModel().getSelectedItem().getLocalParameters() != null) { refreshParameters(eaCBox.getSelectionModel().getSelectedItem().getLocalParameters(), eaParameters); } @@ -133,7 +142,7 @@ public class SettingsPane extends AnchorPane { mainContainer.getChildren().add(eaPane); } - private void initialiseMutatorParameters(final JCGP cgp) { + private void initialiseMutatorParameters(final JCGP jcgp) { mutatorPane = new VBox(2); Text header = new Text("Mutator"); @@ -141,20 +150,20 @@ public class SettingsPane extends AnchorPane { header.setUnderline(true); final ComboBox mutatorCBox = new ComboBox(); - mutatorCBox.getItems().addAll(cgp.getMutators()); - mutatorCBox.getSelectionModel().select(cgp.getMutator()); + mutatorCBox.getItems().addAll(jcgp.getMutators()); + mutatorCBox.getSelectionModel().select(jcgp.getMutator()); mutatorCBox.prefWidthProperty().bind(mainContainer.widthProperty()); final VBox mutatorParameters = new VBox(); mutatorParameters.setSpacing(2); - if (cgp.getEvolutionaryStrategy().getLocalParameters() != null) { - refreshParameters(cgp.getMutator().getLocalParameters(), mutatorParameters); + if (jcgp.getEvolutionaryStrategy().getLocalParameters() != null) { + refreshParameters(jcgp.getMutator().getLocalParameters(), mutatorParameters); } mutatorCBox.setOnAction(new EventHandler() { @Override public void handle(ActionEvent event) { - cgp.setMutator(mutatorCBox.getSelectionModel().getSelectedIndex()); + jcgp.setMutator(mutatorCBox.getSelectionModel().getSelectedIndex()); if (mutatorCBox.getSelectionModel().getSelectedItem().getLocalParameters() != null) { refreshParameters(mutatorCBox.getSelectionModel().getSelectedItem().getLocalParameters(), mutatorParameters); } @@ -165,45 +174,72 @@ public class SettingsPane extends AnchorPane { mainContainer.getChildren().add(mutatorPane); } - private void initialiseProblemTypeParameters(final JCGP cgp, final GUI gui) { + private void initialiseProblemTypeParameters(final JCGP jcgp, final GUI gui) { problemPane= new VBox(2); Text header = new Text("Problem Type"); header.setFont(Font.font("Arial", 14)); header.setUnderline(true); - final ComboBox ffCBox = new ComboBox(); - ffCBox.getItems().addAll(cgp.getProblems()); - ffCBox.getSelectionModel().select(cgp.getProblem()); - ffCBox.prefWidthProperty().bind(mainContainer.widthProperty()); + final ComboBox problemCBox = new ComboBox(); + problemCBox.getItems().addAll(jcgp.getProblems()); + problemCBox.getSelectionModel().select(jcgp.getProblem()); + problemCBox.prefWidthProperty().bind(mainContainer.widthProperty()); + + final VBox problemParameters = new VBox(); + problemParameters.setSpacing(2); + problemParameters.setPadding(new Insets(0, 0, 4, 0)); + if (jcgp.getProblem().getLocalParameters() != null) { + refreshParameters(jcgp.getProblem().getLocalParameters(), problemParameters); + } - final VBox ffParameters = new VBox(); - ffParameters.setSpacing(2); - if (cgp.getProblem().getLocalParameters() != null) { - refreshParameters(cgp.getProblem().getLocalParameters(), ffParameters); + final Group showTestCaseContainer = new Group(); + final Button showTestCaseButton = makeTestCaseButton(); + if (jcgp.getProblem() instanceof TestCaseProblem) { + showTestCaseContainer.getChildren().add(showTestCaseButton); + + testCaseTable = new TestCaseTable((TestCaseProblem) jcgp.getProblem(), gui); } final VBox nodeFunctions = new VBox(); nodeFunctions.setSpacing(2); - refreshFunctions(cgp.getResources().getFunctionSet(), nodeFunctions, gui); + nodeFunctions.setPadding(new Insets(0, 0, 4, 0)); + refreshFunctions(jcgp.getResources().getFunctionSet(), nodeFunctions, gui); - ffCBox.setOnAction(new EventHandler() { + problemCBox.setOnAction(new EventHandler() { @Override public void handle(ActionEvent event) { - cgp.setProblem(ffCBox.getSelectionModel().getSelectedIndex()); - if (ffCBox.getSelectionModel().getSelectedItem().getLocalParameters() != null) { - refreshParameters(cgp.getProblem().getLocalParameters(), ffParameters); - refreshFunctions(cgp.getProblem().getFunctionSet(), nodeFunctions, gui); + jcgp.setProblem(problemCBox.getSelectionModel().getSelectedIndex()); + if (jcgp.getProblem().getLocalParameters() != null) { + showTestCaseContainer.getChildren().clear(); + refreshParameters(jcgp.getProblem().getLocalParameters(), problemParameters); + refreshFunctions(jcgp.getProblem().getFunctionSet(), nodeFunctions, gui); + testCaseTable.close(); + if (jcgp.getProblem() instanceof TestCaseProblem) { + showTestCaseContainer.getChildren().add(showTestCaseButton); + testCaseTable = new TestCaseTable((TestCaseProblem) jcgp.getProblem(), gui); + } } gui.reset(); } }); - problemPane.getChildren().addAll(header, ffCBox, ffParameters, nodeFunctions); + problemPane.getChildren().addAll(header, problemCBox, problemParameters, nodeFunctions, showTestCaseContainer); mainContainer.getChildren().add(problemPane); } + private Button makeTestCaseButton() { + Button b = new Button("Show test cases"); + b.setOnAction(new EventHandler() { + @Override + public void handle(ActionEvent event) { + testCaseTable.show(); + } + }); + return b; + } + private void createControls(final GUI gui) { Text header = new Text("Experiment controls"); header.setFont(Font.font("Arial", 14)); @@ -291,31 +327,43 @@ public class SettingsPane extends AnchorPane { revalidateParameters(); } - private void refreshFunctions(final FunctionSet fs, VBox vb, final GUI gui) { - vb.getChildren().clear(); + /** + * This method handles a problem type change by updating the list of allowed + * node functions. + * + * @param functionSet + * @param container + * @param gui + */ + private void refreshFunctions(final FunctionSet functionSet, VBox container, final GUI gui) { + container.getChildren().clear(); CheckBox cb; - for (int i = 0; i < fs.getTotalFunctionCount(); i++) { - cb = new CheckBox(fs.getFunction(i).getName()); + for (int i = 0; i < functionSet.getTotalFunctionCount(); i++) { + cb = new CheckBox(functionSet.getFunction(i).getName()); cb.setId(String.valueOf(i)); - cb.setSelected(fs.isEnabled(fs.getFunction(i))); + cb.setSelected(functionSet.isEnabled(functionSet.getFunction(i))); final int index = i; cb.setOnAction(new EventHandler() { @Override public void handle(ActionEvent event) { if (((CheckBox) event.getSource()).isSelected()) { - fs.enableFunction(index); + functionSet.enableFunction(index); } else { - fs.disableFunction(index); + functionSet.disableFunction(index); } - GUI.updateFunctionSelector(); + gui.updateFunctionSelector(); } }); - vb.getChildren().add(cb); + container.getChildren().add(cb); - GUI.updateFunctionSelector(); + gui.updateFunctionSelector(); } } + public boolean isExperimentRunning() { + return gui.isWorking(); + } + /** * * @return true if the experiment needs to be reset, false otherwise. @@ -329,6 +377,9 @@ public class SettingsPane extends AnchorPane { return false; } + /** + * @return true if no parameters have their status set to ParameterStatus.INVALID. + */ public boolean areParametersValid() { for (GUIParameter parameter : parameters) { if (!parameter.isValid()) { @@ -338,6 +389,11 @@ public class SettingsPane extends AnchorPane { return true; } + /** + * Calls validate() on every parameter. This is called whenever a parameter changes, + * so that other parameters update their status in case they were dependent on the + * changed parameter. + */ public void revalidateParameters() { runPause.setDisable(false); for (GUIParameter parameter : parameters) { @@ -348,12 +404,24 @@ public class SettingsPane extends AnchorPane { } } + /** + * Calls applyValue() on every parameter. This is called when a reset occurs, so that + * the new value will be used as a reference instead of the old reference value. + */ public void applyParameters() { for (GUIParameter parameter : parameters) { parameter.applyValue(); } } + /** + * Updates all of the controls to their appropriate state based on the status of the + * experiment, in order to prevent inappropriate operations if the experiment is + * running or finished. + * + * @param running true if the experiment is running + * @param finished true if the experiment is finished + */ public void updateControls(boolean running, boolean finished) { baseParameterPane.setDisable(running); eaPane.setDisable(running); @@ -361,8 +429,13 @@ public class SettingsPane extends AnchorPane { problemPane.setDisable(running); runPause.setText(running ? "Pause" : "Run"); + System.out.println("[updateControls] run pause disable: " + finished); runPause.setDisable(finished); step.setDisable(running || finished); reset.setDisable(running); } + + public TestCaseTable getTestCaseTable() { + return testCaseTable; + } } diff --git a/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java b/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java index bada5d4..e708c53 100644 --- a/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java @@ -6,7 +6,6 @@ import javafx.scene.control.CheckBox; import javafx.scene.control.Control; import jcgp.backend.resources.parameters.BooleanParameter; import jcgp.backend.resources.parameters.ParameterStatus; -import jcgp.gui.GUI; import jcgp.gui.settings.SettingsPane; /** @@ -40,7 +39,7 @@ public class GUIBooleanParameter extends GUIParameter { } @Override - protected void setControlListeners(final SettingsPane sp) { + protected void setControlListeners() { /* pass the CheckBox value back to the parameter whenever it gets * modified, provided the experiment isn't running */ checkBox.selectedProperty().addListener(new ChangeListener() { @@ -48,9 +47,9 @@ public class GUIBooleanParameter extends GUIParameter { public void changed( ObservableValue observable, Boolean oldValue, Boolean newValue) { - if (!GUI.isWorking()) { + if (!settingsPane.isExperimentRunning()) { parameter.set(newValue); - sp.revalidateParameters(); + settingsPane.revalidateParameters(); } } }); diff --git a/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java b/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java index 87e7b69..29648ca 100644 --- a/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java @@ -9,7 +9,6 @@ import javafx.scene.control.TextField; import javafx.scene.input.KeyEvent; import jcgp.backend.resources.parameters.Parameter; import jcgp.backend.resources.parameters.ParameterStatus; -import jcgp.gui.GUI; import jcgp.gui.settings.SettingsPane; /** @@ -44,7 +43,7 @@ public class GUIDoubleParameter extends GUIParameter { } @Override - protected void setControlListeners(final SettingsPane sp) { + protected void setControlListeners() { /* filter keypresses and ignore anything that is not a number * and any decimal point beyond the first ones */ textField.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler() { @@ -63,9 +62,9 @@ public class GUIDoubleParameter extends GUIParameter { public void changed( ObservableValue observable, String oldValue, String newValue) { - if (!newValue.isEmpty() && !GUI.isWorking()) { + if (!newValue.isEmpty() && !settingsPane.isExperimentRunning()) { parameter.set(Double.parseDouble(newValue)); - sp.revalidateParameters(); + settingsPane.revalidateParameters(); } } diff --git a/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java b/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java index 6927f26..da2c11f 100644 --- a/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java @@ -9,7 +9,6 @@ import javafx.scene.control.TextField; import javafx.scene.input.KeyEvent; import jcgp.backend.resources.parameters.Parameter; import jcgp.backend.resources.parameters.ParameterStatus; -import jcgp.gui.GUI; import jcgp.gui.settings.SettingsPane; /** @@ -44,7 +43,7 @@ public class GUIIntegerParameter extends GUIParameter { } @Override - protected void setControlListeners(final SettingsPane settingsPane) { + protected void setControlListeners() { /* filter keypresses and ignore anything that is not a number */ textField.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler() { @Override @@ -62,7 +61,7 @@ public class GUIIntegerParameter extends GUIParameter { public void changed( ObservableValue observable, String oldValue, String newValue) { - if (!newValue.isEmpty() && !GUI.isWorking()) { + if (!newValue.isEmpty() && !settingsPane.isExperimentRunning()) { parameter.set(Double.parseDouble(newValue)); settingsPane.revalidateParameters(); } diff --git a/src/jcgp/gui/settings/parameters/GUIParameter.java b/src/jcgp/gui/settings/parameters/GUIParameter.java index b7afb74..a8a8c4a 100644 --- a/src/jcgp/gui/settings/parameters/GUIParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIParameter.java @@ -49,6 +49,7 @@ public abstract class GUIParameter extends HBox { private Text name; private Control valueControl; + protected SettingsPane settingsPane; protected Tooltip tooltip; protected Parameter parameter; @@ -70,24 +71,24 @@ public abstract class GUIParameter extends HBox { * @param parameter a Parameter for which to generate a GUIParameter * @param sp a reference to the SettingsPane */ - protected GUIParameter(Parameter parameter, final SettingsPane sp) { + protected GUIParameter(Parameter parameter, final SettingsPane settingsPane) { this.parameter = parameter; this.referenceValue = parameter.get(); + this.settingsPane = settingsPane; + setAlignment(Pos.CENTER_LEFT); setSpacing(5); name = new Text(parameter.getName()); // set text width to half of the total width of the GUIParameter name.wrappingWidthProperty().bind(widthProperty().divide(2)); - // allow the text to grow to always fill half of the GUIParameter // the tooltip is the hover-over label containing status information, when appropriate tooltip = new Tooltip(); tooltip.setStyle("-fx-background-color: white; -fx-border-color: black; .page-corner {-fx-background-color: transparent;}"); tooltip.setSkin(null); - valueControl = makeControl(); setHgrow(valueControl, Priority.ALWAYS); @@ -99,7 +100,7 @@ public abstract class GUIParameter extends HBox { // if parameter is not a monitor, make sure the control is constrained appropriately if (!parameter.isMonitor()) { - setControlListeners(sp); + setControlListeners(); } getChildren().addAll(name, valueControl); @@ -143,7 +144,7 @@ public abstract class GUIParameter extends HBox { ObservableValue observable, Object oldValue, Object newValue) { // only do this if the experiment is running - if (GUI.isWorking()) { + if (settingsPane.isExperimentRunning()) { /* here's the catch - atomically get the lock state and set it to true * the lock will only be false again when the runnable is finished executing, * preventing multiple runnables to concurrently update the same GUIParameter @@ -221,11 +222,11 @@ public abstract class GUIParameter extends HBox { * presses to ensure no invalid characters are inserted, applying the new * value to the underlying parameter and revalidating the parameters to * reflect the changes made. - * - * @param settingsPane the parent Pane on which revalidateParameters() should - * be called. + *

+ * Note that changelisteners registered to the main content property of the + * control should always call handleChange() to update the */ - protected abstract void setControlListeners(SettingsPane settingsPane); + protected abstract void setControlListeners(); /** * This method is called to style the GUIParameter according to the status of diff --git a/src/jcgp/gui/settings/testcase/TestCaseTable.java b/src/jcgp/gui/settings/testcase/TestCaseTable.java new file mode 100644 index 0000000..7e72cbd --- /dev/null +++ b/src/jcgp/gui/settings/testcase/TestCaseTable.java @@ -0,0 +1,90 @@ +package jcgp.gui.settings.testcase; + +import java.util.ArrayList; + +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.collections.ObservableList; +import javafx.event.EventHandler; +import javafx.scene.Scene; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableColumn.CellDataFeatures; +import javafx.scene.control.TableView; +import javafx.stage.Stage; +import javafx.stage.WindowEvent; +import javafx.util.Callback; +import jcgp.backend.modules.problem.TestCaseProblem; +import jcgp.backend.modules.problem.TestCaseProblem.TestCase; +import jcgp.gui.GUI; + +/** + * Dont forget to override toString()! + * + * + * @author Eduardo Pedroni + * + */ +public class TestCaseTable extends Stage { + + public TestCaseTable(final TestCaseProblem problem, final GUI gui) { + super(); + + TableView> tv = new TableView>(); + ObservableList> testCaseList = problem.getTestCases(); + + ArrayList, String>> inputs = new ArrayList, String>>(problem.getInputCount()); + ArrayList, String>> outputs = new ArrayList, String>>(problem.getOutputCount()); + + TableColumn, String> tc; + for (int i = 0; i < problem.getInputCount(); i++) { + tc = new TableColumn, String>("I: " + i); + inputs.add(tc); + final int index = i; + tc.setCellValueFactory(new Callback,String>, ObservableValue>() { + @Override + public ObservableValue call(CellDataFeatures, String> param) { + return new SimpleStringProperty(param.getValue().getInput(index).toString()); + } + }); + tc.setSortable(false); + tc.prefWidthProperty().bind(tv.widthProperty().divide(problem.getInputCount() + problem.getOutputCount())); + } + + for (int o = 0; o < problem.getOutputCount(); o++) { + tc = new TableColumn, String>("O: " + o); + outputs.add(tc); + final int index = o; + tc.setCellValueFactory(new Callback,String>, ObservableValue>() { + @Override + public ObservableValue call(CellDataFeatures, String> param) { + return new SimpleStringProperty(param.getValue().getOutput(index).toString()); + } + }); + tc.setSortable(false); + tc.prefWidthProperty().bind(tv.widthProperty().divide(problem.getInputCount() + problem.getOutputCount())); + } + + tv.getColumns().addAll(inputs); + tv.getColumns().addAll(outputs); + + tv.setItems(testCaseList); + + tv.getSelectionModel().selectedItemProperty().addListener(new ChangeListener>() { + @Override + public void changed( + ObservableValue> observable, TestCase oldValue, TestCase newValue) { + gui.evaluateTestCase(newValue); + } + }); + + setOnCloseRequest(new EventHandler() { + @Override + public void handle(WindowEvent event) { + gui.hideGeneValues(); + } + }); + + setScene(new Scene(tv)); + } +} -- cgit v1.2.3