aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEduardo Pedroni <ep625@york.ac.uk>2014-03-23 18:05:13 +0000
committerEduardo Pedroni <ep625@york.ac.uk>2014-03-23 18:05:13 +0000
commit0c288cc1952809294c8d70d86b9f41b04878ac2e (patch)
treeef9671b711fe665a3156594663c083595861a4e6
parentd3527a63e12c0e5288f1e7d2e2dc18e61d16b760 (diff)
Majorly refactored, node grid is fully implemented. About to attempt active path locking.
-rw-r--r--README20
-rw-r--r--src/jcgp/CGP.java309
-rw-r--r--src/jcgp/GUI.java111
-rw-r--r--src/jcgp/Main.java9
-rw-r--r--src/jcgp/TruthTable.java21
-rw-r--r--src/jcgp/Utilities.java98
-rw-r--r--src/jcgp/function/Arithmetic.java (renamed from src/jcgp/modules/function/Arithmetic.java)41
-rw-r--r--src/jcgp/function/BitwiseLogic.java (renamed from src/jcgp/modules/function/BitwiseLogic.java)57
-rw-r--r--src/jcgp/function/BooleanLogic.java (renamed from src/jcgp/modules/function/BooleanLogic.java)57
-rw-r--r--src/jcgp/function/Function.java (renamed from src/jcgp/modules/function/Function.java)5
-rw-r--r--src/jcgp/function/FunctionSet.java54
-rw-r--r--src/jcgp/gui/Console.java13
-rw-r--r--src/jcgp/gui/GUI.java14
-rw-r--r--src/jcgp/gui/SettingsPane.java11
-rw-r--r--src/jcgp/gui/population/ChromosomePane.java97
-rw-r--r--src/jcgp/gui/population/GUIGene.java56
-rw-r--r--src/jcgp/gui/population/GUIInput.java193
-rw-r--r--src/jcgp/gui/population/GUINode.java417
-rw-r--r--src/jcgp/gui/population/GUIOutput.java277
-rw-r--r--src/jcgp/modules/Module.java13
-rw-r--r--src/jcgp/modules/ea/EvolutionaryAlgorithm.java6
-rw-r--r--src/jcgp/modules/ea/StandardEA.java39
-rw-r--r--src/jcgp/modules/fitness/FitnessFunction.java6
-rw-r--r--src/jcgp/modules/fitness/TestCase.java19
-rw-r--r--src/jcgp/modules/fitness/TestCaseEvaluator.java34
-rw-r--r--src/jcgp/modules/fitness/TruthTableEvaluator.java30
-rw-r--r--src/jcgp/modules/function/FunctionSet.java45
-rw-r--r--src/jcgp/modules/mutator/Mutator.java6
-rw-r--r--src/jcgp/modules/mutator/PointMutator.java39
-rw-r--r--src/jcgp/parameters/BooleanParameter.java18
-rw-r--r--src/jcgp/parameters/DoubleParameter.java23
-rw-r--r--src/jcgp/parameters/IntegerParameter.java21
-rw-r--r--src/jcgp/parameters/Parameter.java27
-rw-r--r--src/jcgp/population/Chromosome.java85
-rw-r--r--src/jcgp/population/Gene.java5
-rw-r--r--src/jcgp/population/Input.java2
-rw-r--r--src/jcgp/population/MutableElement.java15
-rw-r--r--src/jcgp/population/Node.java75
-rw-r--r--src/jcgp/population/Output.java31
-rw-r--r--src/jcgp/population/Population.java27
-rw-r--r--src/jcgp/tests/ChromosomeTests.java93
-rw-r--r--src/jcgp/tests/NodeTests.java40
-rw-r--r--src/jcgp/tests/OutputTests.java27
-rw-r--r--src/jcgp/tests/PopulationTests.java94
44 files changed, 1986 insertions, 694 deletions
diff --git a/README b/README
index 982f18e..075b0bd 100644
--- a/README
+++ b/README
@@ -171,4 +171,22 @@ Adding GUI package, refactoring CGP class to interact appropriately with GUI.
15/3
Currently refactoring Parameters. It is now a part of CGP.class as stated in the phase report. The rest of the program will now be refactored to accommodate these changes. Inversion of
-will be employed to avoid static accesses to CGP, and the tests will be modified accordingly. This will allow multiple instances of CGP to co-exist. \ No newline at end of file
+will be employed to avoid static accesses to CGP, and the tests will be modified accordingly. This will allow multiple instances of CGP to co-exist.
+
+18/3
+
+The current state of parameters is not yet perfect. One more refactoring will be carried out to speed up GUI updates.
+Modules will be required to return a hashmap of parameters so the GUI can be re-built easily whenever modules change.
+Modules should instantiate their parameters in their constructor and use direct referencing internally for extra speed.
+The hashmap may be pre-built to save time when getParameters() is called, though this is not critical.
+
+19/3
+
+Refactoring is complete, population tests have been refactored as well. It is worth noting that the new resources
+framework leads to a lot of confusing casting. Utility methods will be written to hide away the casting in the CGP
+class and simplify the programmer's life.
+Registering parameters is no longer necessary. The CGP class will query modules for their parameters when necessary.
+
+
+21/3
+FunctionSet has been rewritten to allow only certain functions to be deactivated, the GUI will now be integrated. \ No newline at end of file
diff --git a/src/jcgp/CGP.java b/src/jcgp/CGP.java
index 16d2be7..6b68fd8 100644
--- a/src/jcgp/CGP.java
+++ b/src/jcgp/CGP.java
@@ -1,153 +1,234 @@
package jcgp;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.Random;
import javafx.beans.property.Property;
-import javafx.beans.property.SimpleIntegerProperty;
+import jcgp.function.Arithmetic;
+import jcgp.function.BitwiseLogic;
+import jcgp.function.BooleanLogic;
+import jcgp.function.Function;
+import jcgp.function.FunctionSet;
import jcgp.modules.ea.EvolutionaryAlgorithm;
import jcgp.modules.ea.StandardEA;
import jcgp.modules.fitness.FitnessFunction;
import jcgp.modules.fitness.TestCase;
-import jcgp.modules.fitness.TruthTableEvaluator;
-import jcgp.modules.function.Arithmetic;
-import jcgp.modules.function.BitwiseLogic;
-import jcgp.modules.function.BooleanLogic;
-import jcgp.modules.function.FunctionSet;
+import jcgp.modules.fitness.TestCaseEvaluator;
import jcgp.modules.mutator.Mutator;
import jcgp.modules.mutator.PointMutator;
import jcgp.parameters.BooleanParameter;
-import jcgp.parameters.DoubleParameter;
import jcgp.parameters.IntegerParameter;
import jcgp.parameters.Parameter;
import jcgp.population.Population;
public class CGP {
- // CGP components
+ /**
+ *
+ *
+ * @author Eduardo Pedroni
+ *
+ */
+ public static class Resources {
+ private HashMap<String, Parameter> moduleParameters = new HashMap<String, Parameter>();
+ private HashMap<String, Parameter> coreParameters = new HashMap<String, Parameter>();
+ private HashMap<String, Parameter> allParameters = new HashMap<String, Parameter>();
+
+ private Random numberGenerator;
+
+ // function sets
+ private FunctionSet[] functionSets = new FunctionSet[] {
+ new Arithmetic(),
+ new BitwiseLogic(),
+ new BooleanLogic() };
+ private FunctionSet functionSet = functionSets[0];
+
+ private TestCase[] testCases;
+
+ public Resources() {
+ createCoreParameters();
+
+ numberGenerator = new Random((int) get("seed"));
+ functionSet = functionSets[0];
+
+ set("arity", functionSet.getMaxArity());
+ }
+
+ public Object get(String key) {
+ return allParameters.get(key).getValue();
+ }
+
+ public void set(String key, Object value) {
+ allParameters.get(key).setValue(value);
+ }
+
+ public Property<?> getProperty(String key) {
+ return allParameters.get(key).valueProperty();
+ }
+
+ public void setManagedParameter(String key, boolean value) {
+ allParameters.get(key).setManaged(value);
+ }
+
+ public void setHiddenParameter(String key, boolean value) {
+ allParameters.get(key).setHidden(value);
+ }
+
+ public boolean contains(String key) {
+ return allParameters.containsKey(key);
+ }
+
+ private void createCoreParameters() {
+ coreParameters.put("rows", new IntegerParameter(9, "Rows"));
+ coreParameters.put("columns", new IntegerParameter(10, "Columns"));
+ coreParameters.put("inputs", new IntegerParameter(3, "Inputs"));
+ coreParameters.put("outputs", new IntegerParameter(3, "Outputs"));
+ coreParameters.put("popSize", new IntegerParameter(5, "Population"));
+ coreParameters.put("levelsBack", new IntegerParameter(2, "Levels back"));
+
+ coreParameters.put("nodes", new IntegerParameter(90, "Nodes", true, true));
+
+ coreParameters.put("generations", new IntegerParameter(1000000, "Generations"));
+ coreParameters.put("currentGen", new IntegerParameter(0, "Generation"));
+ coreParameters.put("runs", new IntegerParameter(5, "Runs"));
+
+ coreParameters.put("arity", new IntegerParameter(0, "Max arity", true, true));
+
+ coreParameters.put("seed", new IntegerParameter(123, "Random seed", true, true));
+
+ coreParameters.put("debug", new BooleanParameter(false, "Debug"));
+
+ allParameters.putAll(coreParameters);
+ }
+
+ private void resetParameters(EvolutionaryAlgorithm ea, Mutator mu, FitnessFunction ff) {
+ Iterator<Entry<String, Parameter>> it = coreParameters.entrySet().iterator();
+ while (it.hasNext()) {
+ ((Parameter) ((Map.Entry<String, Parameter>) it.next())).reset();
+ }
+
+ allParameters.clear();
+ allParameters.putAll(coreParameters);
+ addModuleParameters(ea, mu, ff);
+ }
+
+ private void addModuleParameters(EvolutionaryAlgorithm ea, Mutator mu, FitnessFunction ff) {
+ moduleParameters.clear();
+ moduleParameters.putAll(ea.activate(this));
+ moduleParameters.putAll(mu.activate(this));
+ moduleParameters.putAll(ff.activate(this));
+
+ allParameters.putAll(moduleParameters);
+ }
+
+ /*
+ * Utility functions
+ */
+ public int getRandomInt(int limit) {
+ return numberGenerator.nextInt(limit);
+ }
+
+ public double getRandomDouble(int limit) {
+ return numberGenerator.nextDouble() * limit;
+ }
+
+ public double getRandomDouble() {
+ return numberGenerator.nextDouble();
+ }
+
+ /*
+ * Function set functions
+ */
+ public Function getRandomFunction() {
+ return functionSet.getFunction(numberGenerator.nextInt(functionSet.getFunctionCount()));
+ }
+
+ public Function getFunction(int index) {
+ return functionSet.getFunction(index);
+ }
+
+ public void setFunctionSet(int index) {
+ functionSet = functionSets[index];
+ }
+
+ /*
+ * Test cases
+ */
+ public void setTestCases(TestCase ... testCases) {
+ this.testCases = testCases;
+ }
+
+ public TestCase getTestCase(int index) {
+ return testCases[index];
+ }
+
+ public int getTestCaseCount() {
+ return testCases.length;
+ }
+ }
+
+ private Resources resources = new Resources();
+
+ /*
+ * The following arrays contain all available modules. These collections are read by the GUI
+ * when generating menus, so modules not added here will *NOT* be selectable in the GUI.
+ *
+ * Each array is accompanied by a field which contains a reference to the currently selected
+ * module, 0 by default.
+ */
+ // mutators
private Mutator[] mutators = new Mutator[] {
- new PointMutator()};
+ new PointMutator() };
private Mutator mutator = mutators[0];
+ // evolutionary algorithms
private EvolutionaryAlgorithm[] evolutionaryAlgorithms = new EvolutionaryAlgorithm[] {
- new StandardEA()};
+ new StandardEA() };
private EvolutionaryAlgorithm evolutionaryAlgorithm = evolutionaryAlgorithms[0];
+ // fitness evaluators
private FitnessFunction[] fitnessFunctions = new FitnessFunction[] {
- new TruthTableEvaluator()};
+ new TestCaseEvaluator() };
private FitnessFunction fitnessFunction = fitnessFunctions[0];
- private FunctionSet[] functionSets = new FunctionSet[] {
- new FunctionSet("Arithmetic",
- new Arithmetic.Addition(),
- new Arithmetic.Subtraction(),
- new Arithmetic.Multiplication(),
- new Arithmetic.Division()),
-
- new FunctionSet("32-bit logic",
- new BitwiseLogic.And(),
- new BitwiseLogic.Or(),
- new BitwiseLogic.Nand(),
- new BitwiseLogic.Nor(),
- new BitwiseLogic.Xor(),
- new BitwiseLogic.Xnor(),
- new BitwiseLogic.Not()),
-
- new FunctionSet("1-bit logic",
- new BooleanLogic.And(),
- new BooleanLogic.Or(),
- new BooleanLogic.Nand(),
- new BooleanLogic.Nor(),
- new BooleanLogic.Xor(),
- new BooleanLogic.Xnor(),
- new BooleanLogic.Not())};
+ // the population of chromosomes
+ private Population population = new Population(resources);
-
-
- private Population population;
-
- private HashMap<String, Parameter> parameters = new HashMap<String, Parameter>();
public CGP() {
- createBaseParameters();
-
- for (int i = 0; i < (int) get("generations"); i++) {
-
- set("currentGen", ((int) get("currentGen")) + 1);
- fitnessFunction.evaluate(population);
- evolutionaryAlgorithm.evolve(population, mutator);
-
- if (evolutionaryAlgorithm.getFittestChromosome().getFitness() >= 3) {
- break;
- }
- }
- }
-
- private void createBaseParameters() {
- // make fundamental parameters
- parameters.put("rows", new IntegerParameter(3, "Rows"));
- parameters.put("columns", new IntegerParameter(3, "Columns"));
- parameters.put("inputs", new IntegerParameter(3, "Inputs"));
- parameters.put("outputs", new IntegerParameter(3, "Outputs"));
- parameters.put("popSize", new IntegerParameter(5, "Population"));
+ resources.addModuleParameters(evolutionaryAlgorithm, mutator, fitnessFunction);
- parameters.put("nodes", new IntegerParameter(9, "", true, true));
+ 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}));
- parameters.put("gens", new IntegerParameter(100, "Generations"));
- parameters.put("currentGen", new IntegerParameter(0, "Generation"));
- parameters.put("runs", new IntegerParameter(5, "Runs"));
-
- parameters.put("debug", new BooleanParameter(false, "Debug"));
+// for (int i = 0; i < (int) resources.get("generations"); i++) {
+//
+// resources.set("currentGen", ((int) resources.get("currentGen")) + 1);
+//
+// fitnessFunction.evaluate(population, resources);
+// evolutionaryAlgorithm.evolve(population, mutator, resources);
+//
+// System.out.println("fitness: " + evolutionaryAlgorithm.getFittestChromosome().getFitness());
+//
+// if (evolutionaryAlgorithm.getFittestChromosome().getFitness() >= 6) {
+// System.out.println("solution found");
+// evolutionaryAlgorithm.getFittestChromosome().printNodes();
+// break;
+// }
+// }
}
-// /**
-// *
-// */
-// private void loadModules() {
-// // initialise function set
-// FunctionSet functionSet = new FunctionSet(new Arithmetic.Addition(), new Arithmetic.Subtraction(), new Arithmetic.Multiplication());
-//
-// // initialise utilities
-// Utilities.setResources(new Random(1234), functionSet);
-//
-// // initialise fitness function and truth table
-// TruthTable.setTestCases(new TestCase(new Object[] {2, 5, 4}, new Object[] {1, 10, 15}));
-// fitnessFunction = new TruthTableEvaluator();
-//
-// // initialise EA
-// evolutionaryAlgorithm = new StandardEA();
-// mutator = new StandardMutator();
-//
-// // initialise population
-// population = new Population();
-// }
-
- // Parameter methods
- public void register(String key, Parameter value) {
- parameters.put(key, value);
- }
-
- /**
- * @param key
- * @return
- */
- public Object get(String key) {
- return parameters.get(key).getValue();
- }
-
- public void set(String key, Object value) {
- parameters.get(key).setValue(value);
- }
-
- @SuppressWarnings("rawtypes")
- public Property getProperty(String key) {
- return parameters.get(key).valueProperty();
+
+ public Resources getResources() {
+ return resources;
}
-
- public boolean contains(String key) {
- return parameters.containsKey(key);
+
+
+ public Population getPopulation() {
+ return population;
}
- // Utility methods
-
}
diff --git a/src/jcgp/GUI.java b/src/jcgp/GUI.java
new file mode 100644
index 0000000..1e1d4e4
--- /dev/null
+++ b/src/jcgp/GUI.java
@@ -0,0 +1,111 @@
+package jcgp;
+
+import javafx.application.Application;
+import javafx.event.EventHandler;
+import javafx.scene.Node;
+import javafx.scene.Scene;
+import javafx.scene.control.Tab;
+import javafx.scene.control.TabPane;
+import javafx.scene.control.TabPane.TabClosingPolicy;
+import javafx.scene.input.MouseDragEvent;
+import javafx.scene.layout.BorderPane;
+import javafx.stage.Stage;
+import jcgp.CGP.Resources;
+import jcgp.gui.Console;
+import jcgp.gui.SettingsPane;
+import jcgp.gui.population.ChromosomePane;
+import jcgp.gui.population.GUIGene;
+import jcgp.gui.population.GUINode;
+import jcgp.gui.population.GUIOutput;
+
+public class GUI extends Application {
+
+ public static final String NEUTRAL_COLOUR = "#FFFFFF";
+ public static final String HARD_HIGHLIGHT_COLOUR = "#89AAD6";
+ public static final String SOFT_HIGHLIGHT_COLOUR = "#C7DFFF";
+ public static final String GOOD_SELECTION_COLOUR = "#BDFFC2";
+ public static final String NEUTRAL_SELECTION_COLOUR = "#FBFFB8";
+ public static final String BAD_SELECTION_COLOUR = "#FF9C9C";
+
+ private static CGP cgp;
+ public static Resources resources;
+
+
+ private BorderPane window;
+
+ private ChromosomePane[] chromosomes;
+ private TabPane mainPane;
+
+ private static Console console;
+ private SettingsPane settings;
+
+ public static void main(String[] args) {
+
+ cgp = new CGP();
+ resources = cgp.getResources();
+
+ launch();
+
+ }
+
+ @Override
+ public void start(Stage primaryStage) throws Exception {
+
+ /*
+ * Instantiate the various GUI elements here.
+ *
+ *
+ */
+
+ mainPane = new TabPane();
+ mainPane.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);
+ chromosomes = new ChromosomePane[(int) cgp.getResources().get("popSize")];
+ Tab tab;
+ for (int i = 0; i < chromosomes.length; i++) {
+ chromosomes[i] = new ChromosomePane(cgp.getPopulation().getChromosome(i), cgp.getResources());
+ tab = new Tab("Chr " + i);
+ tab.setContent(chromosomes[i]);
+ mainPane.getTabs().add(tab);
+ }
+
+ mainPane.setPrefHeight(500);
+
+ window = new BorderPane();
+ window.setCenter(mainPane);
+
+ Scene scene = new Scene(window);
+
+ primaryStage.addEventFilter(MouseDragEvent.MOUSE_DRAG_RELEASED, new EventHandler<MouseDragEvent>() {
+ @Override
+ public void handle(MouseDragEvent event) {
+ // preemptively determine whether this event will reach a GUIGene
+ // sodding Controls...
+ if (event.getTarget() instanceof Node) {
+ Node source = ((Node) event.getTarget());
+ while (source != null) {
+ if (source instanceof GUIGene) {
+ return;
+ }
+ source = source.getParent();
+ }
+ }
+ event.consume();
+ ((GUIGene) event.getGestureSource()).resetState();
+ if (event.getGestureSource() instanceof GUINode) {
+ ((GUINode) event.getGestureSource()).updateLines();
+ } else if (event.getGestureSource() instanceof GUIOutput) {
+ ((GUIOutput) event.getGestureSource()).updateLine();
+ }
+ }
+ });
+
+ primaryStage.setMinHeight(600);
+ primaryStage.setMinWidth(800);
+
+ primaryStage.setScene(scene);
+ primaryStage.show();
+
+
+ }
+
+}
diff --git a/src/jcgp/Main.java b/src/jcgp/Main.java
deleted file mode 100644
index d2b438d..0000000
--- a/src/jcgp/Main.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package jcgp;
-
-public class Main {
-
- public static void main(String[] args) {
- CGP cgp = new CGP();
-
- }
-}
diff --git a/src/jcgp/TruthTable.java b/src/jcgp/TruthTable.java
deleted file mode 100644
index e62f56b..0000000
--- a/src/jcgp/TruthTable.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package jcgp;
-
-import jcgp.modules.fitness.TestCase;
-
-public class TruthTable {
-
- private static TestCase[] testCases;
-
- public static void setTestCases(TestCase ... testCases) {
- TruthTable.testCases = testCases;
- }
-
- public static TestCase getTestCase(int index) {
- return testCases[index];
- }
-
- public static int getTestCaseCount() {
- return testCases.length;
- }
-
-}
diff --git a/src/jcgp/Utilities.java b/src/jcgp/Utilities.java
deleted file mode 100644
index ff5387f..0000000
--- a/src/jcgp/Utilities.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package jcgp;
-
-import java.util.Random;
-
-import jcgp.modules.function.Function;
-import jcgp.modules.function.FunctionSet;
-import jcgp.parameters.Parameters;
-import jcgp.population.*;
-
-public class Utilities {
-
- private static Random numberGenerator;
- private static FunctionSet functionSet;
-
- public static void setResources(Random numberGenerator, FunctionSet functionSet) {
- Utilities.numberGenerator = numberGenerator;
- Utilities.functionSet = functionSet;
- }
-
- public static int getRandomInt(int limit){
- return numberGenerator.nextInt(limit);
- }
-
- public static double getRandomDouble(int limit){
- return numberGenerator.nextDouble() * limit;
- }
-
- /**
- * @param chromosome the chromosome to choose from
- * @return a random input
- */
- public static Input getRandomInput(Chromosome chromosome){
- return chromosome.getInput(getRandomInt(Parameters.getInputs()));
- }
-
- /**
- * Returns a random allowed node respecting levels back.
- *
- * This method will NOT pick inputs.
- *
- * @param chromosome the chromosome to pick from
- * @param column the column to use as reference
- * @return a random node
- */
- public static Node getRandomNode(Chromosome chromosome, int column){
- // work out the allowed range obeying levels back
- int allowedColumns = ((column >= Parameters.getLevelsBack()) ? Parameters.getLevelsBack() : column);
- int offset = column - allowedColumns;
-
- // pick a random allowed column and row
- int randomColumn = (getRandomInt(allowedColumns) + offset);
- int randomRow = (getRandomInt(Parameters.getRows()));
-
- return chromosome.getNode(randomRow, randomColumn);
- }
-
- /**
- * Returns a random allowed node.
- *
- * This method will NOT pick inputs.
- *
- * @param chromosome the chromosome to pick from
- * @param column the column to use as reference
- * @param levelsBack whether or not to respect levels back
- * @return a random node
- */
- public static Node getRandomNode(Chromosome chromosome, int column, boolean levelsBack){
- if (levelsBack) {
- return getRandomNode(chromosome, column);
- } else {
- // pick any random column before the given column
- int randomColumn = (getRandomInt(column));
- // pick a random rowgetColumns
- int randomRow = (getRandomInt(Parameters.getRows()));
- return chromosome.getNode(randomRow, randomColumn);
- }
- }
-
-
- /**
- * pick from any column - use this for setting outputs
- *
- * @param chromosome
- * @return
- */
- public static Node getRandomNode(Chromosome chromosome) {
- return chromosome.getNode(getRandomInt(Parameters.getRows()), getRandomInt(Parameters.getColumns()));
- }
-
- public static Function getRandomFunction() {
- return functionSet.getFunction(Utilities.getRandomInt(functionSet.getFunctionCount()));
- }
-
- public static Function getFunction(int index) {
- return functionSet.getFunction(index);
- }
-
-}
diff --git a/src/jcgp/modules/function/Arithmetic.java b/src/jcgp/function/Arithmetic.java
index aa5e9bf..7ec1366 100644
--- a/src/jcgp/modules/function/Arithmetic.java
+++ b/src/jcgp/function/Arithmetic.java
@@ -1,10 +1,25 @@
-package jcgp.modules.function;
+package jcgp.function;
+
+import java.util.ArrayList;
+import java.util.Arrays;
import jcgp.exceptions.InvalidArgumentsException;
import jcgp.population.Connection;
-public class Arithmetic {
-
+public class Arithmetic extends FunctionSet {
+
+ public Arithmetic() {
+ maxArity = 2;
+ name = "Arithmetic";
+ functionList = new Function[]{
+ new Addition(),
+ new Subtraction(),
+ new Multiplication(),
+ new Division()};
+
+ allowedFunctions = new ArrayList<Function>(Arrays.asList(functionList));
+ }
+
public static class Addition extends Function {
private int arity = 2;
@@ -26,6 +41,11 @@ public class Arithmetic {
public int getArity() {
return arity;
}
+
+ @Override
+ public String getName() {
+ return "Addition";
+ }
}
public static class Subtraction extends Function {
@@ -49,6 +69,11 @@ public class Arithmetic {
public int getArity() {
return arity;
}
+
+ @Override
+ public String getName() {
+ return "Subtraction";
+ }
}
public static class Multiplication extends Function {
@@ -72,6 +97,11 @@ public class Arithmetic {
public int getArity() {
return arity;
}
+
+ @Override
+ public String getName() {
+ return "Multiplication";
+ }
}
public static class Division extends Function {
@@ -100,6 +130,11 @@ public class Arithmetic {
public int getArity() {
return arity;
}
+
+ @Override
+ public String getName() {
+ return "Division";
+ }
}
}
diff --git a/src/jcgp/modules/function/BitwiseLogic.java b/src/jcgp/function/BitwiseLogic.java
index c8452e6..033534d 100644
--- a/src/jcgp/modules/function/BitwiseLogic.java
+++ b/src/jcgp/function/BitwiseLogic.java
@@ -1,9 +1,27 @@
-package jcgp.modules.function;
+package jcgp.function;
+
+import java.util.ArrayList;
+import java.util.Arrays;
import jcgp.exceptions.InvalidArgumentsException;
import jcgp.population.Connection;
-public class BitwiseLogic {
+public class BitwiseLogic extends FunctionSet {
+
+ public BitwiseLogic() {
+ maxArity = 2;
+ name = "32-bit Logic";
+ functionList = new Function[]{
+ new And(),
+ new Or(),
+ new Not(),
+ new Xor(),
+ new Nand(),
+ new Nor(),
+ new Xnor()};
+
+ allowedFunctions = new ArrayList<Function>(Arrays.asList(functionList));
+ }
public static class And extends Function {
private int arity = 2;
@@ -25,6 +43,11 @@ public class BitwiseLogic {
public int getArity() {
return arity;
}
+
+ @Override
+ public String getName() {
+ return "AND";
+ }
}
public static class Or extends Function {
@@ -47,6 +70,11 @@ public class BitwiseLogic {
public int getArity() {
return arity;
}
+
+ @Override
+ public String getName() {
+ return "OR";
+ }
}
public static class Not extends Function {
@@ -68,6 +96,11 @@ public class BitwiseLogic {
public int getArity() {
return arity;
}
+
+ @Override
+ public String getName() {
+ return "NOT";
+ }
}
public static class Xor extends Function {
@@ -90,6 +123,11 @@ public class BitwiseLogic {
public int getArity() {
return arity;
}
+
+ @Override
+ public String getName() {
+ return "XOR";
+ }
}
public static class Nand extends Function {
@@ -112,6 +150,11 @@ public class BitwiseLogic {
public int getArity() {
return arity;
}
+
+ @Override
+ public String getName() {
+ return "NAND";
+ }
}
public static class Nor extends Function {
@@ -134,6 +177,11 @@ public class BitwiseLogic {
public int getArity() {
return arity;
}
+
+ @Override
+ public String getName() {
+ return "NOR";
+ }
}
public static class Xnor extends Function {
@@ -156,6 +204,11 @@ public class BitwiseLogic {
public int getArity() {
return arity;
}
+
+ @Override
+ public String getName() {
+ return "XNOR";
+ }
}
diff --git a/src/jcgp/modules/function/BooleanLogic.java b/src/jcgp/function/BooleanLogic.java
index f98d1db..d31b798 100644
--- a/src/jcgp/modules/function/BooleanLogic.java
+++ b/src/jcgp/function/BooleanLogic.java
@@ -1,9 +1,27 @@
-package jcgp.modules.function;
+package jcgp.function;
+
+import java.util.ArrayList;
+import java.util.Arrays;
import jcgp.exceptions.InvalidArgumentsException;
import jcgp.population.Connection;
-public class BooleanLogic {
+public class BooleanLogic extends FunctionSet {
+
+ public BooleanLogic() {
+ maxArity = 2;
+ name = "1-bit Logic";
+ functionList = new Function[]{
+ new And(),
+ new Or(),
+ new Not(),
+ new Xor(),
+ new Nand(),
+ new Nor(),
+ new Xnor()};
+
+ allowedFunctions = new ArrayList<Function>(Arrays.asList(functionList));
+ }
public static class And extends Function {
private int arity = 2;
@@ -25,6 +43,11 @@ public class BooleanLogic {
public int getArity() {
return arity;
}
+
+ @Override
+ public String getName() {
+ return "AND";
+ }
}
public static class Or extends Function {
@@ -47,6 +70,11 @@ public class BooleanLogic {
public int getArity() {
return arity;
}
+
+ @Override
+ public String getName() {
+ return "OR";
+ }
}
public static class Not extends Function {
@@ -68,6 +96,11 @@ public class BooleanLogic {
public int getArity() {
return arity;
}
+
+ @Override
+ public String getName() {
+ return "NOT";
+ }
}
public static class Xor extends Function {
@@ -90,6 +123,11 @@ public class BooleanLogic {
public int getArity() {
return arity;
}
+
+ @Override
+ public String getName() {
+ return "XOR";
+ }
}
public static class Nand extends Function {
@@ -112,6 +150,11 @@ public class BooleanLogic {
public int getArity() {
return arity;
}
+
+ @Override
+ public String getName() {
+ return "NAND";
+ }
}
public static class Nor extends Function {
@@ -134,6 +177,11 @@ public class BooleanLogic {
public int getArity() {
return arity;
}
+
+ @Override
+ public String getName() {
+ return "NOR";
+ }
}
public static class Xnor extends Function {
@@ -156,6 +204,11 @@ public class BooleanLogic {
public int getArity() {
return arity;
}
+
+ @Override
+ public String getName() {
+ return "XNOR";
+ }
}
diff --git a/src/jcgp/modules/function/Function.java b/src/jcgp/function/Function.java
index 3314c2f..64dd206 100644
--- a/src/jcgp/modules/function/Function.java
+++ b/src/jcgp/function/Function.java
@@ -1,12 +1,13 @@
-package jcgp.modules.function;
+package jcgp.function;
import jcgp.exceptions.InvalidArgumentsException;
import jcgp.population.Connection;
public abstract class Function {
-
+
public abstract Object run(Connection ... connections) throws InvalidArgumentsException;
public abstract int getArity();
+ public abstract String getName();
}
diff --git a/src/jcgp/function/FunctionSet.java b/src/jcgp/function/FunctionSet.java
new file mode 100644
index 0000000..07b4ea4
--- /dev/null
+++ b/src/jcgp/function/FunctionSet.java
@@ -0,0 +1,54 @@
+package jcgp.function;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ *
+ * @author Eduardo Pedroni
+ *
+ */
+public abstract class FunctionSet {
+ protected Function[] functionList;
+ protected ArrayList<Function> allowedFunctions;
+ protected int maxArity;
+ protected String name;
+
+// public int getTotalFunctionCount() {
+// return functionList.length;
+// }
+
+ public int getFunctionCount() {
+ return allowedFunctions.size();
+ }
+
+ public Function getFunction(int index) {
+ return allowedFunctions.get(index);
+ }
+
+ public int getMaxArity(){
+ return maxArity;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void disableFunction(int index) {
+ for (Iterator<Function> iterator = allowedFunctions.iterator(); iterator.hasNext();) {
+ if (index < functionList.length) {
+ Function function = (Function) iterator.next();
+ if (function == functionList[index]) {
+ iterator.remove();
+ }
+ }
+
+ }
+ }
+
+ public void enableFunction(int index) {
+ if (!allowedFunctions.contains(functionList[index])) {
+ allowedFunctions.add(functionList[index]);
+ }
+ }
+ } \ No newline at end of file
diff --git a/src/jcgp/gui/Console.java b/src/jcgp/gui/Console.java
new file mode 100644
index 0000000..9008531
--- /dev/null
+++ b/src/jcgp/gui/Console.java
@@ -0,0 +1,13 @@
+package jcgp.gui;
+
+import javafx.scene.control.TextArea;
+
+public class Console extends TextArea {
+
+ public Console() {
+ super();
+ setDisable(true);
+ setPrefHeight(100);
+ }
+
+}
diff --git a/src/jcgp/gui/GUI.java b/src/jcgp/gui/GUI.java
deleted file mode 100644
index 0e4b91a..0000000
--- a/src/jcgp/gui/GUI.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package jcgp.gui;
-
-import javafx.application.Application;
-import javafx.stage.Stage;
-
-public class GUI extends Application {
-
- @Override
- public void start(Stage primaryStage) throws Exception {
-
-
- }
-
-}
diff --git a/src/jcgp/gui/SettingsPane.java b/src/jcgp/gui/SettingsPane.java
new file mode 100644
index 0000000..53056d3
--- /dev/null
+++ b/src/jcgp/gui/SettingsPane.java
@@ -0,0 +1,11 @@
+package jcgp.gui;
+
+import javafx.scene.layout.VBox;
+
+public class SettingsPane extends VBox {
+
+ public SettingsPane() {
+ super();
+ setPrefSize(180, 500);
+ }
+}
diff --git a/src/jcgp/gui/population/ChromosomePane.java b/src/jcgp/gui/population/ChromosomePane.java
new file mode 100644
index 0000000..546314e
--- /dev/null
+++ b/src/jcgp/gui/population/ChromosomePane.java
@@ -0,0 +1,97 @@
+package jcgp.gui.population;
+
+import java.util.ArrayList;
+
+import javafx.scene.control.ScrollPane;
+import javafx.scene.layout.Pane;
+import javafx.scene.shape.Line;
+import jcgp.CGP.Resources;
+import jcgp.population.Chromosome;
+
+
+public class ChromosomePane extends ScrollPane {
+
+ private GUINode[][] guiNodes;
+ private GUIInput[] guiInputs;
+ private GUIOutput[] guiOutputs;
+
+ private Pane content;
+
+ private ArrayList<Line> connectionLines;
+
+ public ChromosomePane(Chromosome chromosome, Resources resources) {
+ super();
+ connectionLines = new ArrayList<Line>();
+
+ content = new Pane();
+ content.setId("content pane for genes");
+
+ // generate the GUIGenes
+ // inputs
+ guiInputs = new GUIInput[(int) resources.get("inputs")];
+ for (int i = 0; i < guiInputs.length; i++) {
+ // make the GUI elements
+ guiInputs[i] = new GUIInput(this, chromosome.getInput(i));
+ content.getChildren().addAll(guiInputs[i]);
+ }
+ // nodes
+ guiNodes = new GUINode[(int) resources.get("rows")][(int) resources.get("columns")];
+ double angle, xPos, yPos;
+ for (int r = 0; r < guiNodes.length; r++) {
+ for (int c = 0; c < guiNodes[r].length; c++) {
+ // make the connection lines
+ Line lines[] = new Line[(int) resources.get("arity")];
+ for (int l = 0; l < lines.length; l++) {
+ angle = ((((double) (l + 1)) / ((double) (lines.length + 1))) * GUIGene.THETA) - (GUIGene.THETA / 2);
+ xPos = (-Math.cos(angle) * GUIGene.NODE_RADIUS) + (((c + 1) * (2 * GUIGene.NODE_RADIUS + GUIGene.SPACING)) + GUIGene.NODE_RADIUS);
+ yPos = (Math.sin(angle) * GUIGene.NODE_RADIUS) + ((r * (2 * GUIGene.NODE_RADIUS + GUIGene.SPACING)) + GUIGene.NODE_RADIUS);
+
+ lines[l] = new Line(xPos, yPos, xPos, yPos);
+ lines[l].setMouseTransparent(true);
+ lines[l].setVisible(false);
+ connectionLines.add(lines[l]);
+ }
+ // make the GUI elements
+ guiNodes[r][c] = new GUINode(this, chromosome.getNode(r, c), resources, lines);
+ }
+ content.getChildren().addAll(guiNodes[r]);
+ }
+
+ // outputs
+ guiOutputs = new GUIOutput[(int) resources.get("outputs")];
+ for (int i = 0; i < guiOutputs.length; i++) {
+ xPos = (((int) resources.get("columns") + 1) * (2 * GUIGene.NODE_RADIUS + GUIGene.SPACING));
+ yPos = (chromosome.getOutput(i).getIndex() * (2 * GUIGene.NODE_RADIUS + GUIGene.SPACING)) + GUIGene.NODE_RADIUS;
+ // make the line
+ Line line = new Line(xPos, yPos, xPos, yPos);
+ line.setMouseTransparent(true);
+ line.setVisible(false);
+ connectionLines.add(line);
+ // make the GUI elements
+ guiOutputs[i] = new GUIOutput(this, chromosome.getOutput(i), resources, line);
+ content.getChildren().addAll(guiOutputs[i]);
+ }
+
+ content.getChildren().addAll(connectionLines);
+ setPrefWidth(620);
+ setContent(content);
+ }
+
+
+ public GUINode getGuiNode(int row, int column) {
+ return guiNodes[row][column];
+ }
+
+ public GUIInput getGuiInput(int index) {
+ return guiInputs[index];
+ }
+
+ public GUIOutput getGuiOutput(int index) {
+ return guiOutputs[index];
+ }
+
+ public Pane getContentPane() {
+ return content;
+ }
+
+}
diff --git a/src/jcgp/gui/population/GUIGene.java b/src/jcgp/gui/population/GUIGene.java
new file mode 100644
index 0000000..ef44110
--- /dev/null
+++ b/src/jcgp/gui/population/GUIGene.java
@@ -0,0 +1,56 @@
+package jcgp.gui.population;
+
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.scene.Group;
+import javafx.scene.shape.Circle;
+import javafx.scene.text.Text;
+import jcgp.population.Gene;
+
+enum GUIGeneState {
+ NEUTRAL,
+ HOVER,
+ INDIRECT_HOVER,
+ ACTIVE_HOVER,
+ SOURCE,
+ TARGET,
+ NO_CHANGE_TARGET,
+ FORBIDDEN_TARGET
+}
+
+public abstract class GUIGene extends Group {
+
+ public static final double NODE_RADIUS = 30;
+ public static final double SPACING = 15;
+
+ public static final double THETA = Math.PI / 1.4;
+ public static final double SOCKET_RADIUS = Math.sqrt(NODE_RADIUS) / 1.8;
+
+ public static final double NODE_TEXT = 0;
+
+ protected Text text;
+ protected Circle mainCircle;
+
+ protected SimpleObjectProperty<GUIGeneState> stateProperty = new SimpleObjectProperty<GUIGeneState>(GUIGeneState.NEUTRAL);
+
+ protected ChromosomePane parent;
+
+ public SimpleObjectProperty<GUIGeneState> stateProperty() {
+ return stateProperty;
+ }
+
+ public void setState(GUIGeneState newState) {
+ stateProperty.set(newState);
+ }
+
+ public void showText(boolean value) {
+ text.setVisible(value);
+ }
+
+ public abstract Gene getGene();
+
+ public abstract void setConnections(GUIGeneState newState);
+
+ public abstract void resetState();
+
+
+}
diff --git a/src/jcgp/gui/population/GUIInput.java b/src/jcgp/gui/population/GUIInput.java
new file mode 100644
index 0000000..6f7a523
--- /dev/null
+++ b/src/jcgp/gui/population/GUIInput.java
@@ -0,0 +1,193 @@
+package jcgp.gui.population;
+
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.event.EventHandler;
+import javafx.geometry.VPos;
+import javafx.scene.input.MouseDragEvent;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.paint.Paint;
+import javafx.scene.shape.Circle;
+import javafx.scene.text.Font;
+import javafx.scene.text.Text;
+import javafx.scene.text.TextAlignment;
+import jcgp.GUI;
+import jcgp.population.Input;
+import jcgp.population.Node;
+import jcgp.population.Output;
+
+public class GUIInput extends GUIGene {
+
+ private Circle outputSocket;
+ private Input input;
+
+ public GUIInput(ChromosomePane parentRef, final Input input) {
+
+ this.parent = parentRef;
+ this.input = input;
+
+ relocate(NODE_RADIUS,
+ (input.getIndex() * (2 * NODE_RADIUS + SPACING)) + NODE_RADIUS);
+
+ mainCircle = new Circle(NODE_RADIUS, Paint.valueOf("white"));
+ mainCircle.setStroke(Paint.valueOf("black"));
+
+ text = new Text("I: " + input.getIndex());
+ text.setFont(Font.font("Arial", 12));
+ text.setTextOrigin(VPos.CENTER);
+ text.setTextAlignment(TextAlignment.CENTER);
+ text.setWrappingWidth(NODE_RADIUS * 2);
+ text.setX(-NODE_RADIUS);
+ text.setVisible(true);
+
+ outputSocket = new Circle(NODE_RADIUS, 0, SOCKET_RADIUS, Paint.valueOf("white"));
+ outputSocket.setId(String.valueOf(0));
+ outputSocket.setStroke(Paint.valueOf("black"));
+
+ getChildren().addAll(mainCircle, text, outputSocket);
+
+ /*
+ * Mouse event handlers on whole gene
+ */
+ addEventFilter(MouseDragEvent.MOUSE_DRAG_ENTERED, new EventHandler<MouseDragEvent>() {
+ @Override
+ public void handle(MouseDragEvent event) {
+ // the drag has entered this node, react appropriately
+ // this happens even if we are the source of the drag
+ if (event.getGestureSource() instanceof GUINode) {
+ Node source = ((GUINode) event.getGestureSource()).getGene();
+ for (int i = 0; i < (int) GUI.resources.get("arity"); i++) {
+ if (input == source.getConnection(i)) {
+ stateProperty.set(GUIGeneState.NO_CHANGE_TARGET);
+ return;
+ }
+ }
+ } else if (event.getGestureSource() instanceof GUIOutput) {
+ Output source = ((GUIOutput) event.getGestureSource()).getGene();
+ if (((GUIGene) event.getSource()).getGene() == source.getSource()) {
+ ((GUIGene) event.getSource()).setState(GUIGeneState.NO_CHANGE_TARGET);
+ }
+ }
+ stateProperty.set(GUIGeneState.TARGET);
+ }
+ });
+
+ addEventFilter(MouseDragEvent.MOUSE_DRAG_OVER, new EventHandler<MouseDragEvent>() {
+ @Override
+ public void handle(MouseDragEvent event) {
+ // the user is dragging over this node, react appropriately
+ // this happens even if we are the source of the drag
+
+ }
+ });
+
+ addEventFilter(MouseDragEvent.MOUSE_DRAG_EXITED, new EventHandler<MouseDragEvent>() {
+ @Override
+ public void handle(MouseDragEvent event) {
+ // the drag has exited this node, react appropriately
+ // this happens even if we are the source of the drag
+ if (stateProperty.get() == GUIGeneState.NO_CHANGE_TARGET) {
+ stateProperty.set(GUIGeneState.INDIRECT_HOVER);
+ } else {
+ stateProperty.set(GUIGeneState.NEUTRAL);
+ }
+ }
+
+ });
+
+ addEventFilter(MouseDragEvent.MOUSE_DRAG_RELEASED, new EventHandler<MouseDragEvent>() {
+ @Override
+ public void handle(MouseDragEvent event) {
+ // set states to reflect the new situation
+ ((GUIGene) event.getGestureSource()).setState(GUIGeneState.NEUTRAL);
+ ((GUIGene) event.getGestureSource()).setConnections(GUIGeneState.NEUTRAL);
+ stateProperty.set(GUIGeneState.HOVER);
+ // the user released the drag gesture on this node, react appropriately
+ if (event.getGestureSource() instanceof GUINode) {
+ ((GUINode) event.getGestureSource()).setChangingConnection(input);
+ } else if (event.getGestureSource() instanceof GUIOutput) {
+ ((GUIOutput) event.getGestureSource()).getGene().setConnection(0, input);
+ }
+ }
+ });
+
+ addEventFilter(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ // cursor has entered this node without dragging, or it is dragging and this is the source
+ if (stateProperty.get() == GUIGeneState.NEUTRAL) {
+ stateProperty.set(GUIGeneState.INDIRECT_HOVER);
+ }
+ }
+ });
+
+ addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ // cursor has left this node without dragging, or it is dragging and this is the source
+ if (stateProperty.get() == GUIGeneState.INDIRECT_HOVER) {
+ stateProperty.set(GUIGeneState.NEUTRAL);
+ setConnections(GUIGeneState.NEUTRAL);
+ }
+ }
+ });
+
+ stateProperty.addListener(new ChangeListener<GUIGeneState>() {
+ @Override
+ public void changed(ObservableValue<? extends GUIGeneState> observable, GUIGeneState oldValue, GUIGeneState newValue) {
+
+ switch (newValue) {
+ case ACTIVE_HOVER:
+ mainCircle.setFill(Paint.valueOf(GUI.SOFT_HIGHLIGHT_COLOUR));
+ break;
+ case FORBIDDEN_TARGET:
+ mainCircle.setFill(Paint.valueOf(GUI.BAD_SELECTION_COLOUR));
+ break;
+ case HOVER:
+ mainCircle.setFill(Paint.valueOf(GUI.HARD_HIGHLIGHT_COLOUR));
+ break;
+ case INDIRECT_HOVER:
+ mainCircle.setFill(Paint.valueOf(GUI.SOFT_HIGHLIGHT_COLOUR));
+ break;
+ case NEUTRAL:
+ mainCircle.setFill(Paint.valueOf(GUI.NEUTRAL_COLOUR));
+ break;
+ case NO_CHANGE_TARGET:
+ mainCircle.setFill(Paint.valueOf(GUI.NEUTRAL_SELECTION_COLOUR));
+ break;
+ case SOURCE:
+ mainCircle.setFill(Paint.valueOf(GUI.HARD_HIGHLIGHT_COLOUR));
+ break;
+ case TARGET:
+ mainCircle.setFill(Paint.valueOf(GUI.GOOD_SELECTION_COLOUR));
+ break;
+ default:
+ break;
+
+ }
+ }
+ });
+
+ }
+
+ @Override
+ public Input getGene() {
+ return input;
+ }
+
+ /**
+ * Set all connections to a given state.
+ *
+ * @param newState the state to set connections to.
+ */
+ @Override
+ public void setConnections(GUIGeneState newState) {
+ // nothing
+ }
+
+ @Override
+ public void resetState() {
+ stateProperty.set(GUIGeneState.NEUTRAL);
+
+ }
+}
diff --git a/src/jcgp/gui/population/GUINode.java b/src/jcgp/gui/population/GUINode.java
new file mode 100644
index 0000000..4350e52
--- /dev/null
+++ b/src/jcgp/gui/population/GUINode.java
@@ -0,0 +1,417 @@
+package jcgp.gui.population;
+
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.event.EventHandler;
+import javafx.geometry.VPos;
+import javafx.scene.control.Label;
+import javafx.scene.input.MouseDragEvent;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.paint.Paint;
+import javafx.scene.shape.Circle;
+import javafx.scene.shape.Line;
+import javafx.scene.text.Font;
+import javafx.scene.text.Text;
+import javafx.scene.text.TextAlignment;
+import jcgp.CGP.Resources;
+import jcgp.GUI;
+import jcgp.function.Function;
+import jcgp.population.Connection;
+import jcgp.population.Input;
+import jcgp.population.Node;
+import jcgp.population.Output;
+
+public class GUINode extends GUIGene {
+
+ private Circle[] sockets;
+ private Circle output;
+
+ private Line[] lines;
+
+ private Label connectionNumber;
+
+ private Node node;
+
+ private int connectionIndex;
+
+ public GUINode(ChromosomePane parentRef, final Node node, Resources resources, Line[] connectionLines) {
+
+ // store references
+ this.parent = parentRef;
+ this.node = node;
+ this.lines = connectionLines;
+
+ // move the GUIGene to the right position
+ relocate(((node.getColumn() + 1) * (2 * NODE_RADIUS + SPACING)) + NODE_RADIUS,
+ (node.getRow() * (2 * NODE_RADIUS + SPACING)) + NODE_RADIUS);
+
+ // set the line ends correctly
+ updateLines();
+
+ connectionNumber = new Label();
+ connectionNumber.setStyle("-fx-background-color:rgb(255, 255, 255); -fx-border-color:rgba(0, 0, 0, 0.5); ");
+ connectionNumber.setVisible(false);
+
+ output = new Circle(NODE_RADIUS, 0, SOCKET_RADIUS, Paint.valueOf("white"));
+ output.setStroke(Paint.valueOf("black"));
+
+ mainCircle = new Circle(NODE_RADIUS, Paint.valueOf("white"));
+ mainCircle.setStroke(Paint.valueOf("black"));
+
+ text = new Text(node.getFunction().getName());
+ this.node.functionProperty().addListener(new ChangeListener<Function>() {
+ @Override
+ public void changed(ObservableValue<? extends Function> observable,
+ Function oldValue, Function newValue) {
+ text.setText(newValue.getName());
+ }
+ });
+ text.setFont(Font.font("Arial", 12));
+ text.setTextOrigin(VPos.CENTER);
+ text.setTextAlignment(TextAlignment.CENTER);
+ text.setWrappingWidth(NODE_RADIUS * 2);
+ text.setX(-NODE_RADIUS);
+ text.setVisible(true);
+
+ sockets = new Circle[(int) resources.get("arity")];
+ double angle, xPos, yPos;
+ for (int l = 0; l < sockets.length; l++) {
+ angle = ((((double) (l + 1)) / ((double) ((int) resources.get("arity") + 1))) * THETA) - (THETA / 2);
+ xPos = -Math.cos(angle) * NODE_RADIUS;
+ yPos = Math.sin(angle) * NODE_RADIUS;
+
+ sockets[l] = new Circle(xPos, yPos, GUIGene.SOCKET_RADIUS, Paint.valueOf("white"));
+ sockets[l].setId(String.valueOf(l));
+ sockets[l].setStroke(Paint.valueOf("black"));
+
+ final Circle s = sockets[l];
+ final int index = l;
+
+ /*
+ * Mouse event handlers on sockets
+ *
+ */
+ s.addEventFilter(MouseDragEvent.DRAG_DETECTED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ // the mouse has been dragged out of the socket, this means a full drag is in progress
+ startFullDrag();
+ }
+ });
+
+ s.addEventFilter(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ // user is hovering over connection socket
+ connectionNumber.setText("C: " + s.getId());
+ connectionNumber.relocate(s.getCenterX() + 5, s.getCenterY() - 10);
+ connectionNumber.setVisible(true);
+ }
+ });
+
+ s.addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ // user exits the connection socket
+ connectionNumber.setVisible(false);
+ }
+ });
+
+ s.addEventFilter(MouseDragEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ // mouse was pressed on the socket
+ stateProperty.set(GUIGeneState.SOURCE);
+ connectionIndex = index;
+ }
+ });
+
+ s.addEventFilter(MouseDragEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ lines[connectionIndex].setEndX(event.getX() + ((Circle) event.getSource()).getParent().getLayoutX());
+ lines[connectionIndex].setEndY(event.getY() + ((Circle) event.getSource()).getParent().getLayoutY());
+ }
+ });
+
+ s.addEventFilter(MouseDragEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ if (event.isDragDetect()) {
+ // mouse was released before dragging out of the socket
+ updateLine(index);
+ stateProperty.set(GUIGeneState.HOVER);
+ }
+ }
+ });
+ }
+
+ /*
+ * Mouse event handlers on whole gene
+ */
+ addEventFilter(MouseDragEvent.MOUSE_DRAG_ENTERED, new EventHandler<MouseDragEvent>() {
+ @Override
+ public void handle(MouseDragEvent event) {
+ // the drag has entered this node, react appropriately
+ // this happens even if we are the source of the drag
+ if (isAllowed((GUIGene) event.getGestureSource(), (GUIGene) event.getSource())) {
+ if (event.getGestureSource() instanceof GUINode) {
+ Node source = ((GUINode) event.getGestureSource()).getGene();
+ for (int i = 0; i < lines.length; i++) {
+ if (node == source.getConnection(i)) {
+ stateProperty.set(GUIGeneState.NO_CHANGE_TARGET);
+ return;
+ }
+ }
+ } else if (event.getGestureSource() instanceof GUIOutput) {
+ Output source = ((GUIOutput) event.getGestureSource()).getGene();
+ if (((GUIGene) event.getSource()).getGene() == source.getSource()) {
+ ((GUIGene) event.getSource()).setState(GUIGeneState.NO_CHANGE_TARGET);
+ }
+ }
+ stateProperty.set(GUIGeneState.TARGET);
+ } else {
+ stateProperty.set(GUIGeneState.FORBIDDEN_TARGET);
+ }
+ }
+ });
+
+ addEventFilter(MouseDragEvent.MOUSE_DRAG_OVER, new EventHandler<MouseDragEvent>() {
+ @Override
+ public void handle(MouseDragEvent event) {
+ // the user is dragging over this node, react appropriately
+ // this happens even if we are the source of the drag
+
+ }
+ });
+
+ addEventFilter(MouseDragEvent.MOUSE_DRAG_EXITED, new EventHandler<MouseDragEvent>() {
+ @Override
+ public void handle(MouseDragEvent event) {
+ // the drag has exited this node, react appropriately
+ // this happens even if we are the source of the drag
+ if (event.isPrimaryButtonDown()) {
+ if (event.getGestureSource() == event.getSource()) {
+ stateProperty.set(GUIGeneState.SOURCE);
+ } else {
+ if (stateProperty.get() == GUIGeneState.NO_CHANGE_TARGET) {
+ stateProperty.set(GUIGeneState.INDIRECT_HOVER);
+ } else {
+ stateProperty.set(GUIGeneState.NEUTRAL);
+ }
+ }
+ }
+ }
+ });
+
+ addEventFilter(MouseDragEvent.MOUSE_DRAG_RELEASED, new EventHandler<MouseDragEvent>() {
+ @Override
+ public void handle(MouseDragEvent event) {
+ // set states to reflect the new situation
+ ((GUIGene) event.getGestureSource()).setState(GUIGeneState.NEUTRAL);
+ ((GUIGene) event.getGestureSource()).setConnections(GUIGeneState.NEUTRAL);
+ stateProperty.set(GUIGeneState.HOVER);
+ // the user released the drag gesture on this node, react appropriately
+ if (isAllowed((GUIGene) event.getGestureSource(), (GUIGene) event.getSource())) {
+ if (event.getGestureSource() instanceof GUINode) {
+ ((GUINode) event.getGestureSource()).setChangingConnection(node);
+ } else if (event.getGestureSource() instanceof GUIOutput) {
+ ((GUIOutput) event.getGestureSource()).getGene().setConnection(0, node);
+ }
+ }
+ if (event.getGestureSource() instanceof GUINode) {
+ ((GUINode) event.getGestureSource()).updateLines();
+ } else if (event.getGestureSource() instanceof GUIOutput) {
+ ((GUIOutput) event.getGestureSource()).updateLine();
+ }
+
+ }
+ });
+
+
+ addEventFilter(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ // cursor has entered this node without dragging, or it is dragging and this is the source
+ if (stateProperty.get() == GUIGeneState.NEUTRAL) {
+ stateProperty.set(GUIGeneState.HOVER);
+ }
+ }
+ });
+
+ addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ // cursor has left this node without dragging, or it is dragging and this is the source
+ if (stateProperty.get() == GUIGeneState.HOVER) {
+ stateProperty.set(GUIGeneState.NEUTRAL);
+ setConnections(GUIGeneState.NEUTRAL);
+ }
+ }
+ });
+
+ getChildren().addAll(mainCircle, text);
+ getChildren().addAll(sockets);
+ getChildren().addAll(output, connectionNumber);
+
+ stateProperty.addListener(new ChangeListener<GUIGeneState>() {
+ @Override
+ public void changed(ObservableValue<? extends GUIGeneState> observable, GUIGeneState oldValue, GUIGeneState newValue) {
+
+ switch (newValue) {
+ case ACTIVE_HOVER:
+ mainCircle.setFill(Paint.valueOf(GUI.SOFT_HIGHLIGHT_COLOUR));
+ showLines(true);
+ setConnections(GUIGeneState.ACTIVE_HOVER);
+ break;
+ case FORBIDDEN_TARGET:
+ mainCircle.setFill(Paint.valueOf(GUI.BAD_SELECTION_COLOUR));
+ break;
+ case HOVER:
+ mainCircle.setFill(Paint.valueOf(GUI.HARD_HIGHLIGHT_COLOUR));
+ showLines(true);
+ setConnections(GUIGeneState.INDIRECT_HOVER);
+ break;
+ case INDIRECT_HOVER:
+ mainCircle.setFill(Paint.valueOf(GUI.SOFT_HIGHLIGHT_COLOUR));
+ break;
+ case NEUTRAL:
+ mainCircle.setFill(Paint.valueOf(GUI.NEUTRAL_COLOUR));
+ showLines(false);
+ if (oldValue == GUIGeneState.ACTIVE_HOVER) {
+ setConnections(GUIGeneState.NEUTRAL);
+ }
+ break;
+ case NO_CHANGE_TARGET:
+ mainCircle.setFill(Paint.valueOf(GUI.NEUTRAL_SELECTION_COLOUR));
+ break;
+ case SOURCE:
+ mainCircle.setFill(Paint.valueOf(GUI.HARD_HIGHLIGHT_COLOUR));
+ break;
+ case TARGET:
+ mainCircle.setFill(Paint.valueOf(GUI.GOOD_SELECTION_COLOUR));
+ break;
+ default:
+ break;
+
+ }
+ }
+ });
+
+ for (int c = 0; c < lines.length; c++) {
+ final int i = c;
+ node.connections().get(c).addListener(new ChangeListener<Connection>() {
+ @Override
+ public void changed(ObservableValue<? extends Connection> observable,
+ Connection oldValue, Connection newValue) {
+ updateLine(i);
+ }
+ });
+ }
+
+ }
+
+
+ private boolean isAllowed(GUIGene source, GUIGene target) {
+ if (source instanceof GUINode) {
+ // if the source is a node, all inputs and some nodes are valid
+ if (target instanceof GUIInput) {
+ return true;
+ } 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() <= (int) GUI.resources.get("levelsBack")) {
+ return true;
+ }
+ return false;
+ } else if (target instanceof GUIOutput) {
+ return false;
+ } else {
+ throw new ClassCastException("Target was neither GUINode nor GUIInput nor GUIOutput.");
+ }
+ } else if (source instanceof GUIOutput) {
+ // if the source is an output, any node or input is valid
+ if (target instanceof GUINode || target instanceof GUIInput) {
+ return true;
+ } else if (target instanceof GUIOutput) {
+ return false;
+ } else {
+ throw new ClassCastException("Target was neither GUINode nor GUIInput nor GUIOutput.");
+ }
+ }
+ // if the source was neither node nor output, something bad is happening
+ throw new ClassCastException("Source was neither GUINode nor GUIOutput.");
+ }
+
+
+ /**
+ * Place the end of the specified line on the output of the associated connection.
+ *
+ * @param index the line to be updated.
+ */
+ public void updateLine(int index) {
+ if (node.getConnection(index) instanceof Node) {
+ int row = ((Node) node.getConnection(index)).getRow(),
+ column = ((Node) node.getConnection(index)).getColumn();
+ lines[index].setEndX(((column + 1) * (2 * NODE_RADIUS + SPACING)) + 2 * NODE_RADIUS);
+ lines[index].setEndY((row * (2 * NODE_RADIUS + SPACING)) + NODE_RADIUS);
+ } else if (node.getConnection(index) instanceof Input) {
+ int inputIndex = ((Input) node.getConnection(index)).getIndex();
+ lines[index].setEndX(2 * NODE_RADIUS);
+ lines[index].setEndY(inputIndex * (2 * NODE_RADIUS + SPACING) + NODE_RADIUS);
+ }
+ }
+
+ /**
+ * Updates the end of all lines to match the associated connections.
+ */
+ public void updateLines() {
+ for (int c = 0; c < lines.length; c++) {
+ updateLine(c);
+ }
+ }
+
+ /**
+ * Toggle visibility of all connection lines.
+ *
+ * @param value whether to show the lines or not.
+ */
+ private void showLines(boolean value) {
+ for (int i = 0; i < lines.length; i++) {
+ lines[i].setVisible(value);
+ }
+ }
+
+ @Override
+ public Node getGene() {
+ return node;
+ }
+
+
+ @Override
+ public void setConnections(GUIGeneState newState) {
+ for (int i = 0; i < lines.length; i++) {
+ if (node.getConnection(i) instanceof Node) {
+ parent.getGuiNode(((Node) node.getConnection(i)).getRow(), ((Node) node.getConnection(i)).getColumn()).setState(newState);
+ } else if (node.getConnection(i) instanceof Input) {
+ parent.getGuiInput(((Input) node.getConnection(i)).getIndex()).setState(newState);
+ }
+ }
+ }
+
+ /**
+ * This method sets the connection currently being changed to the
+ * specified value.
+ */
+ public void setChangingConnection(Connection newConnection) {
+ node.setConnection(connectionIndex, newConnection);
+ }
+
+
+ @Override
+ public void resetState() {
+ stateProperty.set(GUIGeneState.NEUTRAL);
+ setConnections(GUIGeneState.NEUTRAL);
+ }
+
+}
diff --git a/src/jcgp/gui/population/GUIOutput.java b/src/jcgp/gui/population/GUIOutput.java
new file mode 100644
index 0000000..602a022
--- /dev/null
+++ b/src/jcgp/gui/population/GUIOutput.java
@@ -0,0 +1,277 @@
+package jcgp.gui.population;
+
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.event.EventHandler;
+import javafx.geometry.VPos;
+import javafx.scene.control.Label;
+import javafx.scene.input.MouseDragEvent;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.paint.Paint;
+import javafx.scene.shape.Circle;
+import javafx.scene.shape.Line;
+import javafx.scene.text.Font;
+import javafx.scene.text.Text;
+import javafx.scene.text.TextAlignment;
+import jcgp.GUI;
+import jcgp.CGP.Resources;
+import jcgp.population.Connection;
+import jcgp.population.Input;
+import jcgp.population.Node;
+import jcgp.population.Output;
+
+public class GUIOutput extends GUIGene {
+
+ private Circle socket;
+
+ private Label connectionLabel;
+
+ private Line sourceLine;
+
+ private Output output;
+
+ public GUIOutput(ChromosomePane parentRef, final Output output, Resources resources, Line line) {
+
+ this.parent = parentRef;
+ this.output = output;
+ this.sourceLine = line;
+
+ relocate((((int) resources.get("columns") + 1) * (2 * NODE_RADIUS + SPACING)) + NODE_RADIUS,
+ (output.getIndex() * (2 * NODE_RADIUS + SPACING)) + NODE_RADIUS);
+
+ // set the line ends correctly
+ updateLine();
+
+ mainCircle = new Circle(NODE_RADIUS, Paint.valueOf("white"));
+ mainCircle.setStroke(Paint.valueOf("black"));
+
+ text = new Text("O: " + output.getIndex());
+ text.setFont(Font.font("Arial", 12));
+ text.setTextOrigin(VPos.CENTER);
+ text.setTextAlignment(TextAlignment.CENTER);
+ text.setWrappingWidth(NODE_RADIUS * 2);
+ text.setX(-NODE_RADIUS);
+ text.setVisible(true);
+
+ socket = new Circle(-NODE_RADIUS, 0, SOCKET_RADIUS, Paint.valueOf("white"));
+ socket.setId(String.valueOf(0));
+ socket.setStroke(Paint.valueOf("black"));
+
+ connectionLabel = new Label("S");
+ connectionLabel.setStyle("-fx-background-color:rgb(255, 255, 255); -fx-border-color:rgba(0, 0, 0, 0.5); ");
+ connectionLabel.relocate(socket.getCenterX() + 5, socket.getCenterY() - 10);
+ connectionLabel.setVisible(false);
+
+ /*
+ * Mouse event handlers on sockets
+ *
+ */
+ socket.addEventFilter(MouseDragEvent.DRAG_DETECTED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ // the mouse has been dragged out of the socket, this means a full drag is in progress
+ startFullDrag();
+ }
+ });
+
+ socket.addEventFilter(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ // user is hovering over connection socket
+ connectionLabel.setVisible(true);
+ }
+ });
+
+ socket.addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ // user exits the connection socket
+ connectionLabel.setVisible(false);
+ }
+ });
+
+ socket.addEventFilter(MouseDragEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ // mouse was pressed on the socket
+ stateProperty.set(GUIGeneState.SOURCE);
+ }
+ });
+
+ socket.addEventFilter(MouseDragEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ sourceLine.setEndX(event.getX() + ((Circle) event.getSource()).getParent().getLayoutX());
+ sourceLine.setEndY(event.getY() + ((Circle) event.getSource()).getParent().getLayoutY());
+ }
+ });
+
+ socket.addEventFilter(MouseDragEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ if (event.isDragDetect()) {
+ // mouse was released before dragging out of the socket
+ updateLine();
+ stateProperty.set(GUIGeneState.HOVER);
+ }
+ }
+ });
+
+
+ /*
+ * Mouse event handlers on whole gene
+ */
+ addEventFilter(MouseDragEvent.MOUSE_DRAG_ENTERED, new EventHandler<MouseDragEvent>() {
+ @Override
+ public void handle(MouseDragEvent event) {
+ // the drag has entered this node, react appropriately
+ stateProperty.set(GUIGeneState.FORBIDDEN_TARGET);
+ }
+ });
+
+ addEventFilter(MouseDragEvent.MOUSE_DRAG_OVER, new EventHandler<MouseDragEvent>() {
+ @Override
+ public void handle(MouseDragEvent event) {
+ // the user is dragging over this node, react appropriately
+ // this happens even if we are the source of the drag
+
+ }
+ });
+
+ addEventFilter(MouseDragEvent.MOUSE_DRAG_EXITED, new EventHandler<MouseDragEvent>() {
+ @Override
+ public void handle(MouseDragEvent event) {
+ // the drag has exited this node, react appropriately
+ // this happens even if we are the source of the drag
+ if (event.isPrimaryButtonDown()) {
+ if (event.getGestureSource() == event.getSource()) {
+ stateProperty.set(GUIGeneState.SOURCE);
+ } else {
+ if (stateProperty.get() == GUIGeneState.NO_CHANGE_TARGET) {
+ stateProperty.set(GUIGeneState.INDIRECT_HOVER);
+ } else {
+ stateProperty.set(GUIGeneState.NEUTRAL);
+ }
+ }
+ }
+ }
+ });
+
+ addEventFilter(MouseDragEvent.MOUSE_DRAG_RELEASED, new EventHandler<MouseDragEvent>() {
+ @Override
+ public void handle(MouseDragEvent event) {
+ // making a connection to an output is illegal
+ // set states to reflect the new situation
+ ((GUIGene) event.getGestureSource()).setState(GUIGeneState.NEUTRAL);
+ ((GUIGene) event.getGestureSource()).setConnections(GUIGeneState.NEUTRAL);
+ stateProperty.set(GUIGeneState.HOVER);
+ }
+ });
+
+
+ addEventFilter(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ // cursor has entered this node without dragging, or it is dragging and this is the source
+ if (stateProperty.get() == GUIGeneState.NEUTRAL) {
+ stateProperty.set(GUIGeneState.HOVER);
+ }
+ }
+ });
+
+ addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ // cursor has left this node without dragging, or it is dragging and this is the source
+ if (stateProperty.get() == GUIGeneState.HOVER) {
+ stateProperty.set(GUIGeneState.NEUTRAL);
+ setConnections(GUIGeneState.NEUTRAL);
+ }
+ }
+ });
+
+
+ getChildren().addAll(mainCircle, text, socket, connectionLabel);
+
+ stateProperty.addListener(new ChangeListener<GUIGeneState>() {
+ @Override
+ public void changed(ObservableValue<? extends GUIGeneState> observable, GUIGeneState oldValue, GUIGeneState newValue) {
+
+
+ switch (newValue) {
+ case ACTIVE_HOVER:
+ break;
+ case FORBIDDEN_TARGET:
+ mainCircle.setFill(Paint.valueOf(GUI.BAD_SELECTION_COLOUR));
+ break;
+ case HOVER:
+ mainCircle.setFill(Paint.valueOf(GUI.HARD_HIGHLIGHT_COLOUR));
+ sourceLine.setVisible(true);
+ setConnections(GUIGeneState.ACTIVE_HOVER);
+ break;
+ case INDIRECT_HOVER:
+ mainCircle.setFill(Paint.valueOf(GUI.SOFT_HIGHLIGHT_COLOUR));
+ break;
+ case NEUTRAL:
+ mainCircle.setFill(Paint.valueOf(GUI.NEUTRAL_COLOUR));
+ sourceLine.setVisible(false);
+ break;
+ case NO_CHANGE_TARGET:
+ mainCircle.setFill(Paint.valueOf(GUI.NEUTRAL_SELECTION_COLOUR));
+ break;
+ case SOURCE:
+ mainCircle.setFill(Paint.valueOf(GUI.HARD_HIGHLIGHT_COLOUR));
+ break;
+ case TARGET:
+ mainCircle.setFill(Paint.valueOf(GUI.GOOD_SELECTION_COLOUR));
+ break;
+ default:
+ break;
+
+ }
+ }
+ });
+
+ output.sourceProperty().addListener(new ChangeListener<Connection>() {
+ @Override
+ public void changed(ObservableValue<? extends Connection> observable,
+ Connection oldValue, Connection newValue) {
+ updateLine();
+ }
+ });
+
+ }
+
+ public void updateLine() {
+ if (output.getSource() instanceof Node) {
+ int row = ((Node) output.getSource()).getRow(),
+ column = ((Node) output.getSource()).getColumn();
+ sourceLine.setEndX(((column + 1) * (2 * NODE_RADIUS + SPACING)) + 2 * NODE_RADIUS);
+ sourceLine.setEndY((row * (2 * NODE_RADIUS + SPACING)) + NODE_RADIUS);
+ } else if (output.getSource() instanceof Input) {
+ int inputIndex = ((Input) output.getSource()).getIndex();
+ sourceLine.setEndX(2 * NODE_RADIUS);
+ sourceLine.setEndY(inputIndex * (2 * NODE_RADIUS + SPACING) + NODE_RADIUS);
+ }
+ }
+
+ @Override
+ public Output getGene() {
+ return output;
+ }
+
+ @Override
+ public void setConnections(GUIGeneState newState) {
+ if (output.getSource() instanceof Node) {
+ parent.getGuiNode(((Node) output.getSource()).getRow(), ((Node) output.getSource()).getColumn()).setState(newState);
+ } else if (output.getSource() instanceof Input) {
+ parent.getGuiInput(((Input) output.getSource()).getIndex()).setState(newState);
+ }
+ }
+
+ @Override
+ public void resetState() {
+ stateProperty.set(GUIGeneState.NEUTRAL);
+ setConnections(GUIGeneState.NEUTRAL);
+ }
+}
diff --git a/src/jcgp/modules/Module.java b/src/jcgp/modules/Module.java
index b8d44d2..5ce96b9 100644
--- a/src/jcgp/modules/Module.java
+++ b/src/jcgp/modules/Module.java
@@ -1,14 +1,11 @@
package jcgp.modules;
+import java.util.HashMap;
+
+import jcgp.CGP.Resources;
import jcgp.parameters.Parameter;
-import jcgp.parameters.Parameters;
-public abstract class Module {
+public interface Module {
- /**
- * Register a new parameter
- */
- protected final void registerParameter(String key, Parameter value) {
- Parameters.add(key, value);
- };
+ public HashMap<String, Parameter> activate(Resources parameters);
}
diff --git a/src/jcgp/modules/ea/EvolutionaryAlgorithm.java b/src/jcgp/modules/ea/EvolutionaryAlgorithm.java
index 8de8c87..d3fa709 100644
--- a/src/jcgp/modules/ea/EvolutionaryAlgorithm.java
+++ b/src/jcgp/modules/ea/EvolutionaryAlgorithm.java
@@ -1,12 +1,14 @@
package jcgp.modules.ea;
+import jcgp.CGP.Resources;
+import jcgp.modules.Module;
import jcgp.modules.mutator.Mutator;
import jcgp.population.Chromosome;
import jcgp.population.Population;
-public interface EvolutionaryAlgorithm {
+public interface EvolutionaryAlgorithm extends Module {
- public abstract void evolve(Population population, Mutator mutator);
+ public abstract void evolve(Population population, Mutator mutator, Resources parameters);
public abstract Chromosome getFittestChromosome();
diff --git a/src/jcgp/modules/ea/StandardEA.java b/src/jcgp/modules/ea/StandardEA.java
index 2db8776..f2473f5 100644
--- a/src/jcgp/modules/ea/StandardEA.java
+++ b/src/jcgp/modules/ea/StandardEA.java
@@ -1,14 +1,16 @@
package jcgp.modules.ea;
+import java.util.HashMap;
+
+import jcgp.CGP.Resources;
import jcgp.modules.mutator.Mutator;
-import jcgp.parameters.Parameters;
import jcgp.parameters.IntegerParameter;
-import jcgp.parameters.BooleanParameter;
+import jcgp.parameters.Parameter;
import jcgp.population.Chromosome;
import jcgp.population.Population;
/**
- * (1 + λ) EA.
+ * (μ + λ) EA.
*
*
* @author Eduardo Pedroni
@@ -17,28 +19,38 @@ import jcgp.population.Population;
public class StandardEA implements EvolutionaryAlgorithm {
private Chromosome fittestChromosome;
+
+ private IntegerParameter parents, offspring;
+ private HashMap<String, Parameter> localParameters;
+
+ public StandardEA() {
+ parents = new IntegerParameter(1, "Parents");
+ offspring = new IntegerParameter(4, "Offspring");
+
+ localParameters = new HashMap<String, Parameter>();
+
+ localParameters.put("mu", parents);
+ localParameters.put("lambda", offspring);
+ }
@Override
- public void evolve(Population population, Mutator mutator) {
+ public void evolve(Population population, Mutator mutator, Resources parameters) {
// select fittest chromosome
int fittest = 0;
- for (int i = 1; i < ((IntegerParameter) Parameters.get("population")).getValue(); i++) {
+ for (int i = 1; i < (int) parameters.get("popSize"); i++) {
if (population.getChromosome(i).getFitness() >= population.getChromosome(fittest).getFitness()) {
fittest = i;
}
}
fittestChromosome = population.getChromosome(fittest);
population.setBestIndividual(fittest);
- if (((BooleanParameter) Parameters.get("debug")).getValue()) {
- System.out.println("Best fitness: " + fittestChromosome.getFitness());
- }
// create copies of fittest chromosome, mutate them
Chromosome fc = population.getChromosome(fittest);
- for (int i = 0; i < ((IntegerParameter) Parameters.get("population")).getValue(); i++) {
+ for (int i = 0; i < (int) parameters.get("popSize"); i++) {
if (i != fittest) {
population.getChromosome(i).copyConnections(fc);
- mutator.mutate(population.getChromosome(i));
+ mutator.mutate(population.getChromosome(i), parameters);
}
}
}
@@ -47,4 +59,11 @@ public class StandardEA implements EvolutionaryAlgorithm {
public Chromosome getFittestChromosome() {
return fittestChromosome;
}
+
+ @Override
+ public HashMap<String, Parameter> activate(Resources parameters) {
+ parameters.setManagedParameter("popSize", true);
+
+ return localParameters;
+ }
}
diff --git a/src/jcgp/modules/fitness/FitnessFunction.java b/src/jcgp/modules/fitness/FitnessFunction.java
index 8ed1b56..509c230 100644
--- a/src/jcgp/modules/fitness/FitnessFunction.java
+++ b/src/jcgp/modules/fitness/FitnessFunction.java
@@ -1,9 +1,11 @@
package jcgp.modules.fitness;
+import jcgp.CGP.Resources;
+import jcgp.modules.Module;
import jcgp.population.Population;
-public interface FitnessFunction {
+public interface FitnessFunction extends Module {
- public void evaluate(Population population);
+ public void evaluate(Population population, Resources resources);
}
diff --git a/src/jcgp/modules/fitness/TestCase.java b/src/jcgp/modules/fitness/TestCase.java
index 0cb09f1..081a257 100644
--- a/src/jcgp/modules/fitness/TestCase.java
+++ b/src/jcgp/modules/fitness/TestCase.java
@@ -1,26 +1,13 @@
package jcgp.modules.fitness;
-import jcgp.exceptions.ParameterMismatchException;
-import jcgp.parameters.Parameters;
-
public class TestCase {
private Object[] inputs;
private Object[] outputs;
- public TestCase(Object[] inputs, Object[] outputs) throws ParameterMismatchException {
- if (inputs.length == Parameters.getInputs()) {
- this.inputs = inputs;
- } else {
- throw new ParameterMismatchException();
- }
-
- if (outputs.length == Parameters.getOutputs()) {
- this.outputs = outputs;
- } else {
- throw new ParameterMismatchException();
- }
-
+ public TestCase(Object[] inputs, Object[] outputs) {
+ this.inputs = inputs;
+ this.outputs = outputs;
}
public Object getInput(int index) {
diff --git a/src/jcgp/modules/fitness/TestCaseEvaluator.java b/src/jcgp/modules/fitness/TestCaseEvaluator.java
new file mode 100644
index 0000000..1dade74
--- /dev/null
+++ b/src/jcgp/modules/fitness/TestCaseEvaluator.java
@@ -0,0 +1,34 @@
+package jcgp.modules.fitness;
+
+import java.util.HashMap;
+
+import jcgp.CGP.Resources;
+import jcgp.parameters.Parameter;
+import jcgp.population.Population;
+
+public class TestCaseEvaluator implements FitnessFunction {
+
+ @Override
+ public void evaluate(Population population, Resources resources) {
+ // for every chromosome in the population
+ for (int i = 0; i < (int) resources.get("popSize"); i++) {
+ int fitness = 0;
+ // for every test case
+ for (int t = 0; t < resources.getTestCaseCount(); t++) {
+ population.getChromosome(i).setInputs(resources.getTestCase(t).getInputs());
+ // check every output
+ for (int o = 0; o < (int) resources.get("outputs"); o++) {
+ if (population.getChromosome(i).getOutput(o).calculate() == resources.getTestCase(t).getOutput(o)) {
+ fitness++;
+ }
+ }
+ }
+ population.getChromosome(i).setFitness(fitness);
+ }
+ }
+
+ @Override
+ public HashMap<String, Parameter> activate(Resources parameters) {
+ return new HashMap<String, Parameter>();
+ }
+}
diff --git a/src/jcgp/modules/fitness/TruthTableEvaluator.java b/src/jcgp/modules/fitness/TruthTableEvaluator.java
deleted file mode 100644
index a69de96..0000000
--- a/src/jcgp/modules/fitness/TruthTableEvaluator.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package jcgp.modules.fitness;
-
-import jcgp.TruthTable;
-import jcgp.parameters.Parameters;
-import jcgp.population.Population;
-
-public class TruthTableEvaluator implements FitnessFunction {
-
- @Override
- public void evaluate(Population population) {
- // for every chromosome in the population
- for (int i = 0; i < Parameters.getPopulationSize(); i++) {
- int fitness = 0;
- // for every test case
- for (int t = 0; t < TruthTable.getTestCaseCount(); t++) {
- population.getChromosome(i).setInputs(TruthTable.getTestCase(t).getInputs());
- // check every output
- for (int o = 0; o < Parameters.getOutputs(); o++) {
- if (population.getChromosome(i).getOutput(o).calculate() == TruthTable.getTestCase(t).getOutput(o)) {
- fitness++;
- }
- }
- }
- population.getChromosome(i).setFitness(fitness);
- if (Parameters.getDebug()) {
- System.out.println("active nodes: " + population.getChromosome(i).getActiveNodes().size());
- }
- }
- }
-}
diff --git a/src/jcgp/modules/function/FunctionSet.java b/src/jcgp/modules/function/FunctionSet.java
deleted file mode 100644
index fb3724f..0000000
--- a/src/jcgp/modules/function/FunctionSet.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package jcgp.modules.function;
-
-/**
- *
- * TODO: if function set flexibility is desired (i.e. add more functions as the program runs)
- * an add function method should be created
- * this would lead to concurrency problems, so tread lightly!
- *
- *
- * @author Eduardo Pedroni
- *
- */
-public class FunctionSet {
- private Function[] functionList;
- private int maxArity = 0;
- private String name;
-
- public FunctionSet(String name, Function ... functions) {
- functionList = functions;
- for (Function function : functionList) {
- if (function.getArity() > maxArity) {
- maxArity = function.getArity();
- }
- }
-
- this.name = name;
-
- }
-
- public int getFunctionCount() {
- return functionList.length;
- }
-
- public Function getFunction(int index) {
- return functionList[index];
- }
-
- public int getMaxArity(){
- return maxArity;
- }
-
- public String getName() {
- return name;
- }
- } \ No newline at end of file
diff --git a/src/jcgp/modules/mutator/Mutator.java b/src/jcgp/modules/mutator/Mutator.java
index 10df8cd..5234f45 100644
--- a/src/jcgp/modules/mutator/Mutator.java
+++ b/src/jcgp/modules/mutator/Mutator.java
@@ -1,9 +1,11 @@
package jcgp.modules.mutator;
+import jcgp.CGP.Resources;
+import jcgp.modules.Module;
import jcgp.population.Chromosome;
-public interface Mutator {
+public interface Mutator extends Module {
- void mutate(Chromosome chromosome);
+ void mutate(Chromosome chromosome, Resources parameters);
}
diff --git a/src/jcgp/modules/mutator/PointMutator.java b/src/jcgp/modules/mutator/PointMutator.java
index b06d678..ea1ed32 100644
--- a/src/jcgp/modules/mutator/PointMutator.java
+++ b/src/jcgp/modules/mutator/PointMutator.java
@@ -1,36 +1,49 @@
package jcgp.modules.mutator;
-import jcgp.Utilities;
-import jcgp.parameters.IntegerParameter;
-import jcgp.parameters.Parameters;
+import java.util.HashMap;
+
+import jcgp.parameters.DoubleParameter;
+import jcgp.parameters.Parameter;
+import jcgp.CGP.Resources;
import jcgp.population.Chromosome;
import jcgp.population.MutableElement;
import jcgp.population.Node;
import jcgp.population.Output;
public class PointMutator implements Mutator {
-
+
+ private Parameter mutationRate;
+ private HashMap<String, Parameter> localParameters;
+
public PointMutator() {
- Parameters.add("mutRate", new IntegerParameter(10, "Mutation rate"));
+ mutationRate = new DoubleParameter(0.5, "Percent mutation");
+
+ localParameters = new HashMap<String, Parameter>();
+ localParameters.put("mutRate", mutationRate);
}
-
+
@Override
- public void mutate(Chromosome chromosome) {
- int mutations = (int) (((int) Parameters.get("mutRate").getValue()) * ((((double) Parameters.get("nodes").getValue()) + ((double) Parameters.get("outputs").getValue())) / 100));
-
+ public void mutate(Chromosome chromosome, Resources resources) {
+ int mutations = (int) Math.ceil((((double) mutationRate.getValue()) * (((((Integer) resources.get("nodes")).doubleValue() + ((Integer) resources.get("outputs")).doubleValue())) / (double) 100)));
for (int i = 0; i < mutations; i++) {
MutableElement m = chromosome.getRandomMutableElement();
if (m instanceof Output) {
- m.setConnection(chromosome.getRandomConnection());
+ m.setConnection(0, chromosome.getRandomConnection());
} else if (m instanceof Node) {
- int geneType = Utilities.getRandomInt(1 + ((int) Parameters.get("Max arity").getValue()));
+ int geneType = resources.getRandomInt(1 + ((int) resources.get("arity")));
if (geneType < 1) {
- ((Node) m).setFunction(Utilities.getRandomFunction());
+ ((Node) m).setFunction(resources.getRandomFunction());
} else {
- m.setConnection(chromosome.getRandomConnection(((Node) m).getColumn()));
+ m.setConnection(resources.getRandomInt((int) resources.get("arity")), chromosome.getRandomConnection(((Node) m).getColumn()));
}
}
}
}
+
+ @Override
+ public HashMap<String, Parameter> activate(Resources parameters) {
+ return localParameters;
+ }
+
}
diff --git a/src/jcgp/parameters/BooleanParameter.java b/src/jcgp/parameters/BooleanParameter.java
index aff0bc5..0f339a1 100644
--- a/src/jcgp/parameters/BooleanParameter.java
+++ b/src/jcgp/parameters/BooleanParameter.java
@@ -3,34 +3,30 @@ package jcgp.parameters;
import javafx.beans.property.SimpleBooleanProperty;
public class BooleanParameter extends Parameter {
-
- SimpleBooleanProperty value;
-
+
public BooleanParameter(boolean value, String name) {
+ super(name);
this.value = new SimpleBooleanProperty(value);
- this.name = name;
}
public BooleanParameter(boolean value, String name, boolean managed, boolean hidden) {
+ super(name, managed, hidden);
this.value = new SimpleBooleanProperty(value);
- this.name = name;
- this.managed = managed;
- this.hidden = hidden;
}
@Override
- public Object getValue() {
- return this.value.get();
+ public Boolean getValue() {
+ return ((SimpleBooleanProperty) this.value).get();
}
@Override
public SimpleBooleanProperty valueProperty() {
- return value;
+ return (SimpleBooleanProperty) value;
}
@Override
public void setValue(Object value) {
- this.value.set((boolean) value);
+ ((SimpleBooleanProperty) this.value).set((boolean) value);
}
}
diff --git a/src/jcgp/parameters/DoubleParameter.java b/src/jcgp/parameters/DoubleParameter.java
index 2725457..ade99cc 100644
--- a/src/jcgp/parameters/DoubleParameter.java
+++ b/src/jcgp/parameters/DoubleParameter.java
@@ -3,37 +3,30 @@ package jcgp.parameters;
import javafx.beans.property.SimpleDoubleProperty;
public class DoubleParameter extends Parameter {
-
- SimpleDoubleProperty value;
-
+
public DoubleParameter(double value, String name) {
+ super(name);
this.value = new SimpleDoubleProperty(value);
- this.name = name;
}
public DoubleParameter(double value, String name, boolean managed, boolean hidden) {
+ super(name, managed, hidden);
this.value = new SimpleDoubleProperty(value);
- this.name = name;
- this.managed = managed;
- this.hidden = hidden;
- }
-
- public void setValue(double value) {
- this.value.set(value);
}
@Override
- public Object getValue() {
- return this.value.get();
+ public Double getValue() {
+ return ((SimpleDoubleProperty) value).get();
}
+ @Override
public SimpleDoubleProperty valueProperty() {
- return value;
+ return (SimpleDoubleProperty) value;
}
@Override
public void setValue(Object value) {
- this.value.set((double) value);
+ ((SimpleDoubleProperty) this.value).set((double) value);
}
diff --git a/src/jcgp/parameters/IntegerParameter.java b/src/jcgp/parameters/IntegerParameter.java
index f0109f7..cc60179 100644
--- a/src/jcgp/parameters/IntegerParameter.java
+++ b/src/jcgp/parameters/IntegerParameter.java
@@ -3,39 +3,30 @@ package jcgp.parameters;
import javafx.beans.property.SimpleIntegerProperty;
public class IntegerParameter extends Parameter {
-
- SimpleIntegerProperty value;
public IntegerParameter(int value, String name) {
+ super(name);
this.value = new SimpleIntegerProperty(value);
- this.name = name;
}
public IntegerParameter(int value, String name, boolean managed, boolean hidden) {
+ super(name, managed, hidden);
this.value = new SimpleIntegerProperty(value);
- this.name = name;
- this.managed = managed;
- this.hidden = hidden;
- }
-
- public void setValue(int value) {
- this.value.set(value);
}
@Override
- public Object getValue() {
- return this.value.get();
+ public Integer getValue() {
+ return ((SimpleIntegerProperty) this.value).get();
}
@Override
public SimpleIntegerProperty valueProperty() {
- return value;
+ return (SimpleIntegerProperty) value;
}
@Override
public void setValue(Object value) {
- this.value.set((int) value);
-
+ ((SimpleIntegerProperty) this.value).set((int) value);
}
}
diff --git a/src/jcgp/parameters/Parameter.java b/src/jcgp/parameters/Parameter.java
index 64b19c8..fcff9fd 100644
--- a/src/jcgp/parameters/Parameter.java
+++ b/src/jcgp/parameters/Parameter.java
@@ -1,13 +1,28 @@
package jcgp.parameters;
import javafx.beans.property.Property;
-
public abstract class Parameter {
protected boolean managed = false;
protected boolean hidden = false;
+
+ protected boolean originalManaged = false;
+ protected boolean originalHidden = false;
+
+ protected Property<?> value;
+
protected String name;
+ public Parameter(String name, boolean managed, boolean hidden) {
+ this.name = name;
+ this.managed = originalManaged = managed;
+ this.hidden = originalHidden = hidden;
+ }
+
+ public Parameter(String name) {
+ this.name = name;
+ }
+
public void setManaged(boolean value) {
managed = value;
}
@@ -32,6 +47,12 @@ public abstract class Parameter {
public abstract void setValue(Object value);
- @SuppressWarnings("rawtypes")
- public abstract Property valueProperty();
+ public Property<?> valueProperty() {
+ return value;
+ }
+
+ public void reset() {
+ hidden = originalHidden;
+ managed = originalManaged;
+ }
}
diff --git a/src/jcgp/population/Chromosome.java b/src/jcgp/population/Chromosome.java
index 1f0b312..a7f2ed3 100644
--- a/src/jcgp/population/Chromosome.java
+++ b/src/jcgp/population/Chromosome.java
@@ -2,11 +2,12 @@ package jcgp.population;
import java.util.ArrayList;
-import jcgp.Utilities;
+import jcgp.CGP.Resources;
import jcgp.exceptions.ParameterMismatchException;
-import jcgp.parameters.Parameters;
public class Chromosome {
+
+ private Resources resources;
private Input[] inputs;
private Node[][] nodes;
@@ -21,13 +22,12 @@ public class Chromosome {
* Initialise a chromosome with the specified parameters. Random valid connections
* are created.
*
- * @param outputs
- * @param columns
- * @param rows
- * @param inputs
*
*/
- public Chromosome() {
+ public Chromosome(Resources resources) {
+ // store a reference to the parameters
+ this.resources = resources;
+
// allocate memory for all elements of the chromosome
instantiateElements();
// set random connections so that the chromosome can be evaluated
@@ -42,6 +42,9 @@ public class Chromosome {
* @param clone the chromosome to be copied
*/
public Chromosome(Chromosome clone) {
+ // store a reference to the parameters
+ this.resources = clone.getParameters();
+
// allocate memory for all elements of the chromosome
instantiateElements();
// initialise all connections based on argument
@@ -52,20 +55,22 @@ public class Chromosome {
*
*/
private void instantiateElements() {
- inputs = new Input[((int) Parameters.get("inputs").getValue())];
- for (int i = 0; i < ((int) Parameters.get("inputs").getValue()); i++) {
+ inputs = new Input[((int) resources.get("inputs"))];
+ for (int i = 0; i < ((int) resources.get("inputs")); i++) {
inputs[i] = new Input(i);
}
+ int arity = (int) resources.get("arity");
+
// rows first
- nodes = new Node[((int) Parameters.get("rows").getValue())][((int) Parameters.get("columns").getValue())];
- for (int r = 0; r < ((int) Parameters.get("rows").getValue()); r++) {
- for (int c = 0; c < ((int) Parameters.get("columns").getValue()); c++) {
- nodes[r][c] = new Node(this, r, c);
+ nodes = new Node[((int) resources.get("rows"))][((int) resources.get("columns"))];
+ for (int r = 0; r < ((int) resources.get("rows")); r++) {
+ for (int c = 0; c < ((int) resources.get("columns")); c++) {
+ nodes[r][c] = new Node(this, r, c, arity);
}
}
- outputs = new Output[((int) Parameters.get("outputs").getValue())];
- for (int o = 0; o < ((int) Parameters.get("outputs").getValue()); o++) {
+ outputs = new Output[((int) resources.get("outputs"))];
+ for (int o = 0; o < ((int) resources.get("outputs")); o++) {
outputs[o] = new Output(this, o);
}
}
@@ -75,19 +80,21 @@ public class Chromosome {
*/
private void initialiseConnections() {
+ int arity = (int) resources.get("arity");
+
// initialise nodes - [rows][columns]
for (int r = 0; r < nodes.length; r++) {
for (int c = 0; c < nodes[r].length; c++) {
- Connection[] connections = new Connection[((int) Parameters.get("maxArity").getValue())];
+ Connection[] connections = new Connection[arity];
for (int i = 0; i < connections.length; i++) {
connections[i] = getRandomConnection(c);
}
- nodes[r][c].initialise(Utilities.getRandomFunction(), connections);
+ nodes[r][c].initialise(resources.getRandomFunction(), connections);
}
}
for (Output output : outputs) {
- output.setConnection(getRandomConnection());
+ output.setConnection(0, getRandomConnection());
}
}
@@ -96,11 +103,13 @@ public class Chromosome {
* @param clone
*/
public void copyConnections(Chromosome clone) {
+ int arity = (int) resources.get("arity");
+
// copy nodes - [rows][columns]
for (int r = 0; r < nodes.length; r++) {
for (int c = 0; c < nodes[r].length; c++) {
// make array of connections to initialise with
- Connection[] connections = new Connection[((int) Parameters.get("maxArity").getValue())];
+ Connection[] connections = new Connection[arity];
// populate with connections equivalent to clone
Connection copyConnection;
for (int i = 0; i < connections.length; i++) {
@@ -123,9 +132,9 @@ public class Chromosome {
for (int o = 0; o < outputs.length; o++) {
copyOutput = clone.getOutput(o).getSource();
if (copyOutput instanceof Input) {
- outputs[o].setConnection(inputs[((Input) copyOutput).getIndex()]);
+ outputs[o].setConnection(0, inputs[((Input) copyOutput).getIndex()]);
} else if (copyOutput instanceof Node) {
- outputs[o].setConnection(nodes[((Node) copyOutput).getRow()][((Node) copyOutput).getColumn()]);
+ outputs[o].setConnection(0, nodes[((Node) copyOutput).getRow()][((Node) copyOutput).getColumn()]);
} else {
// something bad happened
System.out.println("Warning: Connection of subtype " + copyOutput.getClass().toString() + " is not explicitly handled by copy constructor.");
@@ -177,7 +186,7 @@ public class Chromosome {
*/
public MutableElement getRandomMutableElement() {
// choose output or node
- int index = Utilities.getRandomInt(outputs.length + ((int) Parameters.get("rows").getValue()) * ((int) Parameters.get("columns").getValue()));
+ int index = resources.getRandomInt(outputs.length + ((int) resources.get("rows")) * ((int) resources.get("columns")));
if (index < outputs.length) {
// outputs
@@ -185,7 +194,7 @@ public class Chromosome {
} else {
// node
index -= outputs.length;
- return nodes[index / ((int) Parameters.get("columns").getValue())][index % ((int) Parameters.get("columns").getValue())];
+ return nodes[index / ((int) resources.get("columns"))][index % ((int) resources.get("columns"))];
}
}
@@ -199,11 +208,11 @@ public class Chromosome {
*/
public Connection getRandomConnection(int column) {
// work out the allowed range obeying levels back
- int allowedColumns = ((column >= ((int) Parameters.get("levelsBack").getValue())) ? ((int) Parameters.get("levelsBack").getValue()) : column);
+ int allowedColumns = ((column >= ((int) resources.get("levelsBack"))) ? ((int) resources.get("levelsBack")) : column);
int offset = ((column - allowedColumns) * nodes.length) - inputs.length;
// choose input or allowed node
- int index = Utilities.getRandomInt(inputs.length + (nodes.length * allowedColumns));
+ int index = resources.getRandomInt(inputs.length + (nodes.length * allowedColumns));
if (index < inputs.length) {
// input
return inputs[index];
@@ -226,7 +235,7 @@ public class Chromosome {
*/
public Connection getRandomConnection() {
// choose output or node
- int index = Utilities.getRandomInt(inputs.length + ((int) Parameters.get("columns").getValue()) * ((int) Parameters.get("rows").getValue()));
+ int index = resources.getRandomInt(inputs.length + ((int) resources.get("columns")) * ((int) resources.get("rows")));
if (index < inputs.length) {
// outputs
@@ -234,7 +243,7 @@ public class Chromosome {
} else {
// node
index -= inputs.length;
- return nodes[index / ((int) Parameters.get("columns").getValue())][index % ((int) Parameters.get("columns").getValue())];
+ return nodes[index / ((int) resources.get("columns"))][index % ((int) resources.get("columns"))];
}
}
@@ -269,15 +278,15 @@ public class Chromosome {
}
public boolean compareTo(Chromosome chromosome) {
- for (int r = 0; r < ((int) Parameters.get("rows").getValue()); r++) {
- for (int c = 0; c < ((int) Parameters.get("columns").getValue()); c++) {
+ for (int r = 0; r < ((int) resources.get("rows")); r++) {
+ for (int c = 0; c < ((int) resources.get("columns")); c++) {
if (!(nodes[r][c].copyOf(chromosome.getNode(r, c)))) {
return false;
}
}
}
- for (int o = 0; o < ((int) Parameters.get("outputs").getValue()); o++) {
+ for (int o = 0; o < ((int) resources.get("outputs")); o++) {
if (!(outputs[o].copyOf(chromosome.getOutput(o)))) {
return false;
}
@@ -302,20 +311,26 @@ public class Chromosome {
}
public void printNodes() {
- for (int r = 0; r < ((int) Parameters.get("rows").getValue()); r++) {
+ int arity = (int) resources.get("arity");
+
+ for (int r = 0; r < ((int) resources.get("rows")); r++) {
System.out.print("r: " + r + "\t");
- for (int c = 0; c < ((int) Parameters.get("columns").getValue()); c++) {
+ for (int c = 0; c < ((int) resources.get("columns")); c++) {
System.out.print("N: (" + r + ", " + c + ") ");
- for (int i = 0; i < ((int) Parameters.get("maxArity").getValue()); i++) {
+ for (int i = 0; i < arity; i++) {
System.out.print("C" + i + ": (" + nodes[r][c].getConnection(i).getDescription() + ") ");
}
- System.out.print("F: " + nodes[r][c].getFunction().toString() + "\t");
+ System.out.print("F: " + nodes[r][c].getFunction().getName() + "\t");
}
System.out.print("\n");
}
- for (int o = 0; o < ((int) Parameters.get("outputs").getValue()); o++) {
+ for (int o = 0; o < ((int) resources.get("outputs")); o++) {
System.out.print("o: " + o + " (" + outputs[o].getSource().getDescription() + ")\t");
}
}
+
+ public Resources getParameters() {
+ return resources;
+ }
}
diff --git a/src/jcgp/population/Gene.java b/src/jcgp/population/Gene.java
new file mode 100644
index 0000000..8865a01
--- /dev/null
+++ b/src/jcgp/population/Gene.java
@@ -0,0 +1,5 @@
+package jcgp.population;
+
+public abstract class Gene {
+
+}
diff --git a/src/jcgp/population/Input.java b/src/jcgp/population/Input.java
index 5c545d6..cfcb3ce 100644
--- a/src/jcgp/population/Input.java
+++ b/src/jcgp/population/Input.java
@@ -1,6 +1,6 @@
package jcgp.population;
-public class Input implements Connection {
+public class Input extends Gene implements Connection {
private Object value = 0;
private int index;
diff --git a/src/jcgp/population/MutableElement.java b/src/jcgp/population/MutableElement.java
index 114a4ab..12f6bd1 100644
--- a/src/jcgp/population/MutableElement.java
+++ b/src/jcgp/population/MutableElement.java
@@ -2,8 +2,16 @@ package jcgp.population;
public interface MutableElement {
- public void setConnection(Connection newConnection);
-
+ /**
+ * This method sets the indexed connection to the specified new connection.
+ * Implementing classes may choose to ignore the given index (such as in the
+ * case of outputs, which only have one connection).
+ *
+ * @param index
+ * @param newConnection
+ */
+ public void setConnection(int index, Connection newConnection);
+
/**
* This method returns true if and only if:</br>
* - the elements being compared are not the same instance;</br>
@@ -15,7 +23,8 @@ public interface MutableElement {
* The relationship computed by this method is:</br>
* - symmetric: a.copyOf(b) == b.copyOf(a);</br>
* - not reflexive: a.copyOf(a) returns false;</br>
- * - not transitive: if a.copyOf(b) is true and b.copyOf(c) is true, a.copyOf(c) is not necessarily true;</br>
+ * - not transitive: if a.copyOf(b) is true and b.copyOf(c) is true, a.copyOf(c) is
+ * not necessarily true since it is possible that a == c.</br>
*
* @param m
* @return
diff --git a/src/jcgp/population/Node.java b/src/jcgp/population/Node.java
index 9fafe32..d7013d3 100644
--- a/src/jcgp/population/Node.java
+++ b/src/jcgp/population/Node.java
@@ -1,51 +1,58 @@
package jcgp.population;
import java.util.ArrayList;
-import java.util.Arrays;
-import jcgp.Utilities;
+import javafx.beans.property.SimpleObjectProperty;
import jcgp.exceptions.InsufficientConnectionsException;
-import jcgp.modules.function.Function;
-import jcgp.parameters.Parameters;
+import jcgp.function.Function;
-public class Node implements MutableElement, Connection {
+public class Node extends Gene implements MutableElement, Connection {
- private Function function;
- private Connection[] connections;
+ private SimpleObjectProperty<Function> function;
+ private ArrayList<SimpleObjectProperty<Connection>> connections;
private int column, row;
private Chromosome chromosome;
- public Node(Chromosome chromosome, int row, int column) {
+ public Node(Chromosome chromosome, int row, int column, int arity) {
+ this.function = new SimpleObjectProperty<Function>();
this.chromosome = chromosome;
this.column = column;
this.row = row;
- this.connections = new Connection[((int) Parameters.get("maxArity").getValue())];
+ this.connections = new ArrayList<SimpleObjectProperty<Connection>>(arity);
+ for (int c = 0; c < arity; c++) {
+ connections.add(new SimpleObjectProperty<Connection>());
+ }
}
@Override
public Object getValue() {
//System.out.print("Calculating node: (" + row + ", " + column + ") > ");
- return function.run(Arrays.copyOfRange(connections, 0, function.getArity()));
+ Connection[] list = new Connection[function.get().getArity()];
+ for (int i = 0; i < list.length; i++) {
+ list[i] = connections.get(i).get();
+ }
+
+ return function.get().run(list);
}
public void setFunction(Function newFunction) {
- function = newFunction;
+ function.set(newFunction);
chromosome.recomputeActiveNodes();
}
@Override
- public void setConnection(Connection newConnection) {
- connections[Utilities.getRandomInt(connections.length)] = newConnection;
+ public void setConnection(int index, Connection newConnection) {
+ connections.get(index).set(newConnection);
chromosome.recomputeActiveNodes();
}
public void initialise(Function newFunction, Connection ... newConnections) throws InsufficientConnectionsException {
-
- function = newFunction;
-
- if (newConnections.length >= ((int) Parameters.get("maxArity").getValue())) {
- connections = newConnections;
+ function.set(newFunction);
+ if (newConnections.length >= connections.size()) {
+ for (int i = 0; i < connections.size(); i++) {
+ connections.get(i).set(newConnections[i]);
+ }
} else {
throw new InsufficientConnectionsException();
}
@@ -60,20 +67,28 @@ public class Node implements MutableElement, Connection {
}
public Function getFunction() {
+ return function.get();
+ }
+
+ public SimpleObjectProperty<Function> functionProperty() {
return function;
}
+ public ArrayList<SimpleObjectProperty<Connection>> connections() {
+ return connections;
+ }
+
public Connection getConnection(int index) {
- return connections[index];
+ return connections.get(index).get();
}
public void getActive(ArrayList<Node> activeNodes) {
if (!activeNodes.contains(this)) {
activeNodes.add(this);
}
- for (int i = 0; i < function.getArity(); i++) {
- if (connections[i] instanceof Node) {
- ((Node) connections[i]).getActive(activeNodes);
+ for (int i = 0; i < function.get().getArity(); i++) {
+ if (connections.get(i).get() instanceof Node) {
+ ((Node) connections.get(i).get()).getActive(activeNodes);
}
}
@@ -84,17 +99,17 @@ public class Node implements MutableElement, Connection {
if (this != m) {
if (m instanceof Node) {
Node n = (Node) m;
- if (function == n.getFunction()) {
+ if (function.get() == n.getFunction()) {
if (column == n.getColumn() && row == n.getRow()) {
- for (int i = 0; i < connections.length; i++) {
- if (connections[i] != n.getConnection(i)) {
- if (connections[i] instanceof Input && n.getConnection(i) instanceof Input) {
- if (((Input) connections[i]).getIndex() != ((Input) n.getConnection(i)).getIndex()) {
+ for (int i = 0; i < connections.size(); i++) {
+ if (connections.get(i).get() != n.getConnection(i)) {
+ if (connections.get(i).get() instanceof Input && n.getConnection(i) instanceof Input) {
+ if (((Input) connections.get(i).get()).getIndex() != ((Input) n.getConnection(i)).getIndex()) {
return false;
}
- } else if (connections[i] instanceof Node && n.getConnection(i) instanceof Node) {
- if (((Node) connections[i]).getRow() != ((Node) n.getConnection(i)).getRow() &&
- ((Node) connections[i]).getColumn() != ((Node) n.getConnection(i)).getColumn()) {
+ } else if (connections.get(i).get() instanceof Node && n.getConnection(i) instanceof Node) {
+ if (((Node) connections.get(i).get()).getRow() != ((Node) n.getConnection(i)).getRow() &&
+ ((Node) connections.get(i).get()).getColumn() != ((Node) n.getConnection(i)).getColumn()) {
return false;
}
} else {
diff --git a/src/jcgp/population/Output.java b/src/jcgp/population/Output.java
index ccecae0..b7c6128 100644
--- a/src/jcgp/population/Output.java
+++ b/src/jcgp/population/Output.java
@@ -2,15 +2,18 @@ package jcgp.population;
import java.util.ArrayList;
-public class Output implements MutableElement {
+import javafx.beans.property.SimpleObjectProperty;
+
+public class Output extends Gene implements MutableElement {
- private Connection source;
+ private SimpleObjectProperty<Connection> source;
private Chromosome chromosome;
private int index;
public Output(Chromosome chromosome, int index) {
this.chromosome = chromosome;
this.index = index;
+ this.source = new SimpleObjectProperty<Connection>();
}
public Object calculate() {
@@ -19,8 +22,8 @@ public class Output implements MutableElement {
}
@Override
- public void setConnection(Connection newConnection) {
- source = newConnection;
+ public void setConnection(int index, Connection newConnection) {
+ source.set(newConnection);
chromosome.recomputeActiveNodes();
}
@@ -29,12 +32,16 @@ public class Output implements MutableElement {
}
public Connection getSource() {
+ return source.get();
+ }
+
+ public SimpleObjectProperty<Connection> sourceProperty() {
return source;
}
public void getActiveNodes(ArrayList<Node> activeNodes) {
- if (source instanceof Node) {
- ((Node) source).getActive(activeNodes);
+ if (source.get() instanceof Node) {
+ ((Node) source.get()).getActive(activeNodes);
}
}
@@ -44,14 +51,14 @@ public class Output implements MutableElement {
if (m instanceof Output) {
Output o = (Output) m;
if (index == o.getIndex()) {
- if (source != o.getSource()) {
- if (source instanceof Input && o.getSource() instanceof Input) {
- if (((Input) source).getIndex() == ((Input) o.getSource()).getIndex()) {
+ if (source.get() != o.getSource()) {
+ if (source.get() instanceof Input && o.getSource() instanceof Input) {
+ if (((Input) source.get()).getIndex() == ((Input) o.getSource()).getIndex()) {
return true;
}
- } else if (source instanceof Node && o.getSource() instanceof Node) {
- if (((Node) source).getRow() == ((Node) o.getSource()).getRow() &&
- ((Node) source).getColumn() == ((Node) o.getSource()).getColumn()) {
+ } else if (source.get() instanceof Node && o.getSource() instanceof Node) {
+ if (((Node) source.get()).getRow() == ((Node) o.getSource()).getRow() &&
+ ((Node) source.get()).getColumn() == ((Node) o.getSource()).getColumn()) {
return true;
}
}
diff --git a/src/jcgp/population/Population.java b/src/jcgp/population/Population.java
index 5718515..bc9c9e8 100644
--- a/src/jcgp/population/Population.java
+++ b/src/jcgp/population/Population.java
@@ -1,14 +1,22 @@
package jcgp.population;
-import jcgp.parameters.Parameters;
+import jcgp.CGP.Resources;
+
public class Population {
private Chromosome[] chromosomes;
- private Chromosome bestIndividual;
+ private int fittest;
+
+ public Population(Resources parameters) {
+ chromosomes = new Chromosome[((int) parameters.get("popSize"))];
+ for (int c = 0; c < chromosomes.length; c++) {
+ chromosomes[c] = new Chromosome(parameters);
+ }
+ }
- public Population(Chromosome parent) {
- chromosomes = new Chromosome[((int) Parameters.get("population").getValue())];
+ public Population(Chromosome parent, Resources parameters) {
+ chromosomes = new Chromosome[((int) parameters.get("popSize"))];
// make a clone for safety
this.chromosomes[0] = new Chromosome(parent);
// generate the rest of the individuals
@@ -16,13 +24,6 @@ public class Population {
chromosomes[c] = new Chromosome(chromosomes[0]);
}
}
-
- public Population() {
- chromosomes = new Chromosome[((int) Parameters.get("population").getValue())];
- for (int c = 0; c < chromosomes.length; c++) {
- chromosomes[c] = new Chromosome();
- }
- }
/**
* Returns all chromosomes, parents first, then offspring.
@@ -35,10 +36,10 @@ public class Population {
}
public void setBestIndividual(int index) {
- bestIndividual = chromosomes[index];
+ fittest = index;
}
public Chromosome getBestIndividual() {
- return bestIndividual;
+ return chromosomes[fittest];
}
}
diff --git a/src/jcgp/tests/ChromosomeTests.java b/src/jcgp/tests/ChromosomeTests.java
index c61785e..6323b88 100644
--- a/src/jcgp/tests/ChromosomeTests.java
+++ b/src/jcgp/tests/ChromosomeTests.java
@@ -1,13 +1,8 @@
package jcgp.tests;
-import static org.junit.Assert.*;
-
-import java.util.Random;
-
-import jcgp.Utilities;
-import jcgp.modules.function.Arithmetic;
-import jcgp.modules.function.FunctionSet;
-import jcgp.parameters.Parameters;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import jcgp.CGP.Resources;
import jcgp.population.Chromosome;
import jcgp.population.Connection;
import jcgp.population.Input;
@@ -45,30 +40,16 @@ import org.junit.Test;
public class ChromosomeTests {
private Chromosome chromosome;
+ private static Resources resources;
@BeforeClass
public static void setUpBeforeClass() {
- // initialise function set
- FunctionSet functionSet = new FunctionSet(new Arithmetic.Addition(), new Arithmetic.Subtraction());
-
- // initialise utilities
- Utilities.setResources(new Random(1234), functionSet);
-
- // initialise parameters
- Parameters.setMutationRate(10);
- Parameters.setTotalGenerations(10);
- Parameters.setTotalRuns(5);
- Parameters.setMaxArity(functionSet.getMaxArity());
+ resources = new Resources();
}
@Before
public void setUp() throws Exception {
- Parameters.setColumns(5);
- Parameters.setRows(2);
- Parameters.setInputs(2);
- Parameters.setOutputs(4);
- Parameters.setLevelsBack(5);
- chromosome = new Chromosome();
+ chromosome = new Chromosome(resources);
}
/**
@@ -81,7 +62,7 @@ public class ChromosomeTests {
// compare all elements, one by one
// check outputs
- for (int o = 0; o < Parameters.getOutputs(); o++) {
+ for (int o = 0; o < (int) resources.get("outputs"); o++) {
// check that no cross-references exist between chromosomes
assertTrue("Cloned chromosome contains a reference to a member of the original chromosome.",
clone.getOutput(o) != chromosome.getOutput(o) &&
@@ -99,8 +80,8 @@ public class ChromosomeTests {
}
}
// check nodes, rows first
- for (int row = 0; row < Parameters.getRows(); row++) {
- for (int column = 0; column < Parameters.getColumns(); column++) {
+ for (int row = 0; row < (int) resources.get("rows"); row++) {
+ for (int column = 0; column < (int) resources.get("columns"); column++) {
// check that nodes are not pointers to the same instance
assertTrue("Both chromosomes contain a reference to the same node.", clone.getNode(row, column) != chromosome.getNode(row, column));
// check that both nodes reference their own position in the grid correctly
@@ -110,7 +91,7 @@ public class ChromosomeTests {
assertTrue("Equivalent nodes have different functions.", clone.getNode(row, column).getFunction() == chromosome.getNode(row, column).getFunction());
// compare each connection
- for (int connection = 0; connection < Parameters.getMaxArity(); connection++) {
+ for (int connection = 0; connection < (int) resources.get("arity"); connection++) {
// first look at whether they are actually the same instance
assertTrue("Nodes are connected to the same connection instance.",
clone.getNode(row, column).getConnection(connection) != chromosome.getNode(row, column).getConnection(connection));
@@ -149,12 +130,12 @@ public class ChromosomeTests {
clone.setInputs((Object[]) testInputs);
// check that both chromosomes have the same outputs
- for (int i = 0; i < Parameters.getOutputs(); i++) {
+ for (int i = 0; i < (int) resources.get("outputs"); i++) {
assertTrue("Incorrect output returned", ((Integer) chromosome.getOutput(i).calculate()) == ((Integer) clone.getOutput(i).calculate()));
}
// mutate an output in clone, check that the same node in chromosome produces a different output
- clone.getOutput(1).setConnection(clone.getInput(2));
+ clone.getOutput(1).setConnection(resources.getRandomInt((int) resources.get("arity")), clone.getInput(2));
assertTrue("Mutation affected nodes in both chromosomes.",
clone.getOutput(1).calculate() != chromosome.getOutput(1).calculate());
@@ -183,7 +164,7 @@ public class ChromosomeTests {
// get random connections with the last column as reference, check that they're all within range
int connectionNodes = 0, connectionOutOfRange = 0, connectionInputs = 0, connectionPicks = 100000;
- int chosenColumn = Parameters.getColumns() - 1;
+ int chosenColumn = (int) resources.get("columns") - 1;
for (int i = 0; i < connectionPicks; i++) {
Connection c = chromosome.getRandomConnection(chosenColumn);
if (c instanceof Node) {
@@ -199,10 +180,10 @@ public class ChromosomeTests {
}
}
- System.out.println("Out of " + connectionPicks + " connections picked from " + ((chosenColumn >= Parameters.getLevelsBack()) ? Parameters.getLevelsBack() : chosenColumn) * Parameters.getRows() +
- " allowed nodes and " + Parameters.getInputs() + " inputs, " + connectionNodes + " were nodes and " + connectionInputs + " were inputs.");
+ System.out.println("Out of " + connectionPicks + " connections picked from " + ((chosenColumn >= (int) resources.get("levelsBack")) ? (int) resources.get("levelsBack") : chosenColumn) * (int) resources.get("rows") +
+ " allowed nodes and " + (int) resources.get("inputs") + " inputs, " + connectionNodes + " were nodes and " + connectionInputs + " were inputs.");
- System.out.println("Node/input ratio: " + ((double) ((chosenColumn >= Parameters.getLevelsBack()) ? Parameters.getLevelsBack() : chosenColumn) * Parameters.getRows()) / (double) Parameters.getInputs() +
+ System.out.println("Node/input ratio: " + ((Integer) (((chosenColumn >= (int) resources.get("levelsBack")) ? (int) resources.get("levelsBack") : chosenColumn) * (Integer) resources.get("rows"))).doubleValue() / ((Integer) resources.get("inputs")).doubleValue() +
", picked ratio: " + (double) connectionNodes / (double) connectionInputs);
System.out.println(connectionOutOfRange + " nodes that disrespected levels back were picked.");
@@ -226,10 +207,10 @@ public class ChromosomeTests {
fail("Return is neither Node nor Output.");
}
}
- System.out.println("Out of " + mutablePicks + " mutable elements picked from " + Parameters.getNodeCount() +
- " nodes and " + Parameters.getOutputs() + " outputs, " + mutableNodes + " were nodes and " +
+ System.out.println("Out of " + mutablePicks + " mutable elements picked from " + (int) resources.get("nodes") +
+ " nodes and " + (int) resources.get("outputs") + " outputs, " + mutableNodes + " were nodes and " +
mutableOutputs + " were outputs.");
- System.out.println("Node/output ratio: " + (double) Parameters.getNodeCount() / (double) Parameters.getOutputs() +
+ System.out.println("Node/output ratio: " + ((Integer) resources.get("nodes")).doubleValue() / ((Integer) resources.get("outputs")).doubleValue() +
", picked ratio: " + (double) mutableNodes / (double) mutableOutputs + "\n");
}
@@ -253,12 +234,12 @@ public class ChromosomeTests {
@Test
public void setInputTest() {
// set input values, check that acquired values are correct
- Integer[] testInputs = new Integer[Parameters.getInputs()];
- for (int i = 0; i < Parameters.getInputs(); i++) {
+ Integer[] testInputs = new Integer[(int) resources.get("inputs")];
+ for (int i = 0; i < (int) resources.get("inputs"); i++) {
testInputs[i] = i * 2 - 3;
}
chromosome.setInputs((Object[]) testInputs);
- for (int i = 0; i < Parameters.getInputs(); i++) {
+ for (int i = 0; i < (int) resources.get("inputs"); i++) {
assertTrue("Incorrect input returned.", ((Integer) chromosome.getInput(i).getValue()) == i * 2 - 3);
}
}
@@ -269,8 +250,8 @@ public class ChromosomeTests {
@Test
public void getNodeTest() {
// get all nodes one by one, check that they are all correct
- for (int r = 0; r < Parameters.getRows(); r++) {
- for (int c = 0; c < Parameters.getColumns(); c++) {
+ for (int r = 0; r < (int) resources.get("rows"); r++) {
+ for (int c = 0; c < (int) resources.get("columns"); c++) {
assertTrue("Incorrect node returned.", chromosome.getNode(r, c).getColumn() == c &&
chromosome.getNode(r, c).getRow() == r);
}
@@ -304,7 +285,7 @@ public class ChromosomeTests {
assertTrue("Symmetry not obeyed.", c.compareActiveTo(chromosome));
// create a new random chromosome, this time they should not match
- c = new Chromosome();
+ c = new Chromosome(resources);
assertTrue("Active nodes did match.", !chromosome.compareActiveTo(c));
}
@@ -319,7 +300,7 @@ public class ChromosomeTests {
assertTrue("Symmetry not obeyed.", c.compareTo(chromosome));
// create a new random chromosome, this time they should not match
- c = new Chromosome();
+ c = new Chromosome(resources);
assertTrue("Chromosomes did match.", !chromosome.compareTo(c));
}
/**
@@ -333,20 +314,20 @@ public class ChromosomeTests {
*/
private Chromosome createKnownConfiguration() {
// with a small topology, build a chromosome of known connections and check outputs
- Parameters.setColumns(3);
- Parameters.setRows(3);
- Parameters.setInputs(3);
- Parameters.setOutputs(2);
- Parameters.setLevelsBack(3);
+ resources.set("columns", 3);
+ resources.set("rows", 3);
+ resources.set("inputs", 3);
+ resources.set("outputs", 2);
+ resources.set("levelsBack", 3);
- Chromosome c = new Chromosome();
+ Chromosome c = new Chromosome(resources);
- c.getNode(0, 0).initialise(Utilities.getFunction(0), c.getInput(0), c.getInput(1));
- c.getNode(1, 1).initialise(Utilities.getFunction(0), c.getNode(0, 0), c.getInput(1));
- c.getNode(1, 2).initialise(Utilities.getFunction(0), c.getNode(1, 1), c.getInput(2));
+ c.getNode(0, 0).initialise(resources.getFunction(0), c.getInput(0), c.getInput(1));
+ c.getNode(1, 1).initialise(resources.getFunction(0), c.getNode(0, 0), c.getInput(1));
+ c.getNode(1, 2).initialise(resources.getFunction(0), c.getNode(1, 1), c.getInput(2));
- c.getOutput(0).setConnection(c.getNode(0, 0));
- c.getOutput(1).setConnection(c.getNode(1, 2));
+ c.getOutput(0).setConnection(0, c.getNode(0, 0));
+ c.getOutput(1).setConnection(0, c.getNode(1, 2));
return c;
}
diff --git a/src/jcgp/tests/NodeTests.java b/src/jcgp/tests/NodeTests.java
index 74b8140..330ef7a 100644
--- a/src/jcgp/tests/NodeTests.java
+++ b/src/jcgp/tests/NodeTests.java
@@ -1,13 +1,9 @@
package jcgp.tests;
import static org.junit.Assert.assertTrue;
-
-import java.util.Random;
-
-import jcgp.Utilities;
-import jcgp.modules.function.Arithmetic;
-import jcgp.modules.function.Function;
-import jcgp.parameters.Parameters;
+import jcgp.CGP.Resources;
+import jcgp.function.Arithmetic;
+import jcgp.function.Function;
import jcgp.population.Chromosome;
import jcgp.population.Connection;
import jcgp.population.Node;
@@ -35,32 +31,22 @@ public class NodeTests {
private Node node;
private static Chromosome chromosome;
+ private static Resources resources;
// these numbers will be used by the node under test
private final int arg1 = 2;
private final int arg2 = 5;
@BeforeClass
public static void setUpBeforeClass() {
- // initialise utilities
- Utilities.setResources(new Random(1234), null);
-
- // initialise parameters
- Parameters.setColumns(0);
- Parameters.setRows(0);
- Parameters.setInputs(0);
- Parameters.setOutputs(0);
- Parameters.setLevelsBack(0);
- Parameters.setMutationRate(10);
- Parameters.setTotalGenerations(100);
- Parameters.setTotalRuns(5);
- Parameters.setMaxArity(2);
-
- chromosome = new Chromosome();
+
+ resources = new Resources();
+
+ chromosome = new Chromosome(resources);
}
@Before
public void setUp() throws Exception {
- node = new Node(chromosome, 0, 0);
+ node = new Node(chromosome, 0, 0, (int) resources.get("arity"));
// make node with anonymous addition function and hard-coded value connections
node.initialise(new Arithmetic.Addition(),
new Connection[]{new Connection() {
@@ -117,6 +103,12 @@ public class NodeTests {
// blank
return 0;
}
+
+ @Override
+ public String getName() {
+ // blank
+ return null;
+ }
};
node.setFunction(f);
@@ -194,7 +186,7 @@ public class NodeTests {
return null;
}
};
- node.setConnection(conn2);
+ node.setConnection(resources.getRandomInt((int) resources.get("arity")), conn2);
assertTrue("Connection was not found in node.", node.getConnection(0) == conn2 || node.getConnection(1) == conn2);
diff --git a/src/jcgp/tests/OutputTests.java b/src/jcgp/tests/OutputTests.java
index b8f7d96..00cfea3 100644
--- a/src/jcgp/tests/OutputTests.java
+++ b/src/jcgp/tests/OutputTests.java
@@ -2,10 +2,7 @@ package jcgp.tests;
import static org.junit.Assert.assertTrue;
-import java.util.Random;
-
-import jcgp.Utilities;
-import jcgp.parameters.Parameters;
+import jcgp.CGP.Resources;
import jcgp.population.Chromosome;
import jcgp.population.Connection;
import jcgp.population.Output;
@@ -30,27 +27,15 @@ public class OutputTests {
private Output output;
private static Chromosome chromosome;
+ private static Resources resources;
// these are the test values
private final int outputValue = 10;
private final int outputIndex = 2;
@BeforeClass
public static void setUpBeforeClass() {
- // initialise utilities
- Utilities.setResources(new Random(1234), null);
-
- // initialise parameters
- Parameters.setColumns(0);
- Parameters.setRows(0);
- Parameters.setInputs(0);
- Parameters.setOutputs(0);
- Parameters.setLevelsBack(0);
- Parameters.setMutationRate(10);
- Parameters.setTotalGenerations(100);
- Parameters.setTotalRuns(5);
- Parameters.setMaxArity(2);
-
- chromosome = new Chromosome();
+ resources = new Resources();
+ chromosome = new Chromosome(resources);
}
@Before
@@ -61,7 +46,7 @@ public class OutputTests {
@Test
public void evaluationsTest() {
// set source connection, check that the appropriate value is returned
- output.setConnection(new Connection() {
+ output.setConnection(0, new Connection() {
@Override
public Object getValue() {
@@ -96,7 +81,7 @@ public class OutputTests {
return null;
}
};
- output.setConnection(newConn);
+ output.setConnection(0, newConn);
assertTrue("Incorrect connection returned.", output.getSource() == newConn);
}
diff --git a/src/jcgp/tests/PopulationTests.java b/src/jcgp/tests/PopulationTests.java
index d646b90..474b8d5 100644
--- a/src/jcgp/tests/PopulationTests.java
+++ b/src/jcgp/tests/PopulationTests.java
@@ -1,13 +1,7 @@
package jcgp.tests;
-import static org.junit.Assert.*;
-
-import java.util.Random;
-
-import jcgp.Utilities;
-import jcgp.modules.function.Arithmetic;
-import jcgp.modules.function.FunctionSet;
-import jcgp.parameters.Parameters;
+import static org.junit.Assert.assertTrue;
+import jcgp.CGP.Resources;
import jcgp.population.Chromosome;
import jcgp.population.Population;
@@ -19,9 +13,7 @@ import org.junit.Test;
*
* Tests which cover the behaviour specified for a population.
*
- * - A population should be able to return parents and offspring separately.
- * - It should be possible to iterate through all the chromosomes in a population
- * with one indexing system - parents then offspring.
+ * - It should be possible to iterate through all the chromosomes in a population.
* - When constructed with no arguments, it should generate populationSize
* random chromosomes, distributed according to the EA parameters.
* - If one or more chromosomes are passed into the constructor, it should use them
@@ -34,81 +26,61 @@ import org.junit.Test;
public class PopulationTests {
private Population population;
+ private static Resources resources;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
- // initialise function set
- FunctionSet functionSet = new FunctionSet(new Arithmetic.Addition(), new Arithmetic.Subtraction());
-
- // initialise utilities
- Utilities.setResources(new Random(1234), functionSet);
-
- // initialise parameters
- Parameters.setColumns(20);
- Parameters.setRows(20);
- Parameters.setInputs(2);
- Parameters.setOutputs(4);
- Parameters.setLevelsBack(1);
- Parameters.setMutationRate(10);
- Parameters.setTotalGenerations(100);
- Parameters.setTotalRuns(5);
- Parameters.setPopulationSize(1, 4);
- Parameters.setMaxArity(functionSet.getMaxArity());
+ resources = new Resources();
+
+
+// // initialise function set
+// FunctionSet functionSet = new FunctionSet(new Arithmetic.Addition(), new Arithmetic.Subtraction());
+//
+// // initialise utilities
+// Utilities.setResources(new Random(1234), functionSet);
+//
+// // initialise parameters
+// Resources.setColumns(20);
+// Resources.setRows(20);
+// Resources.setInputs(2);
+// Resources.setOutputs(4);
+// Resources.setLevelsBack(1);
+// Resources.setMutationRate(10);
+// Resources.setTotalGenerations(100);
+// Resources.setTotalRuns(5);
+// Resources.setPopulationSize(1, 4);
+// Resources.setMaxArity(functionSet.getMaxArity());
}
@Before
public void setUp() throws Exception {
- population = new Population();
+ population = new Population(resources);
}
@Test
public void defaultPopulationTest() {
// check that the constructor really generates populationSize chromosomes when none is given
- int offspring = 0, parent = 0;
- while (true) {
- try {
- population.getOffspring(offspring);
- } catch (IndexOutOfBoundsException e) {
- break;
- }
- offspring++;
- }
+ int chromosomes = 0;
while (true) {
try {
- population.getParent(parent);
+ population.getChromosome(chromosomes);
} catch (IndexOutOfBoundsException e) {
break;
}
- parent++;
+ chromosomes++;
}
- assertTrue("Incorrect number of chromosomes generated.", offspring + parent == Parameters.getPopulationSize());
- }
-
- @Test
- public void offspringParentTest() {
- // the first parent should not be the same as the first offspring
- assertTrue("Same chromosome returned as parent and offspring", population.getOffspring(0) != population.getParent(0));
+
+ assertTrue("Incorrect number of chromosomes generated.", chromosomes == (int) resources.get("popSize"));
}
@Test
- public void singleIndexTest() {
- // assuming 1+4
- // the first chromosome should be the first (and only) parent
- assertTrue("Incorrect chromosome returned.", population.getChromosome(0) == population.getParent(0));
- // the next 4 chromosomes should be the offspring, in order
- for (int i = 0; i < Parameters.getOffspringCount(); i++) {
- assertTrue("Incorrect chromosome returned.", population.getChromosome(i + 1) == population.getOffspring(i));
- }
- }
-
- @Test
public void preinitialisedChromosomeTest() {
// the original chromosome that will be cloned
- Chromosome oc = new Chromosome();
+ Chromosome oc = new Chromosome(resources);
// initialise a population with a copy of it
- population = new Population(oc);
+ population = new Population(oc, resources);
// check that the first parent chromosome is identical to, but not the same instance as, the one given
- assertTrue("Incorrect chromosome in population.", population.getParent(0).compareTo(oc) && population.getParent(0) != oc);
+ assertTrue("Incorrect chromosome in population.", population.getChromosome(0).compareTo(oc) && population.getChromosome(0) != oc);
}
}