aboutsummaryrefslogtreecommitdiffstats
path: root/src/jcgp/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/jcgp/backend')
-rw-r--r--src/jcgp/backend/exceptions/InsufficientConnectionsException.java10
-rw-r--r--src/jcgp/backend/exceptions/InvalidArgumentsException.java20
-rw-r--r--src/jcgp/backend/exceptions/ManagedModuleException.java10
-rw-r--r--src/jcgp/backend/exceptions/ParameterMismatchException.java10
-rw-r--r--src/jcgp/backend/function/Arithmetic.java139
-rw-r--r--src/jcgp/backend/function/BitwiseLogic.java215
-rw-r--r--src/jcgp/backend/function/BooleanLogic.java215
-rw-r--r--src/jcgp/backend/function/Function.java13
-rw-r--r--src/jcgp/backend/function/FunctionSet.java74
-rw-r--r--src/jcgp/backend/modules/Module.java11
-rw-r--r--src/jcgp/backend/modules/ModuleStatus.java16
-rw-r--r--src/jcgp/backend/modules/ea/EvolutionaryAlgorithm.java15
-rw-r--r--src/jcgp/backend/modules/ea/MuPlusLambda.java80
-rw-r--r--src/jcgp/backend/modules/ea/TournamentSelection.java55
-rw-r--r--src/jcgp/backend/modules/fitness/FitnessFunction.java11
-rw-r--r--src/jcgp/backend/modules/fitness/TestCase.java29
-rw-r--r--src/jcgp/backend/modules/fitness/TestCaseEvaluator.java45
-rw-r--r--src/jcgp/backend/modules/mutator/Mutator.java11
-rw-r--r--src/jcgp/backend/modules/mutator/PointMutator.java73
-rw-r--r--src/jcgp/backend/parameters/BooleanParameter.java32
-rw-r--r--src/jcgp/backend/parameters/DoubleParameter.java33
-rw-r--r--src/jcgp/backend/parameters/IntegerParameter.java33
-rw-r--r--src/jcgp/backend/parameters/Parameter.java35
-rw-r--r--src/jcgp/backend/population/Chromosome.java335
-rw-r--r--src/jcgp/backend/population/Connection.java9
-rw-r--r--src/jcgp/backend/population/Gene.java5
-rw-r--r--src/jcgp/backend/population/Input.java29
-rw-r--r--src/jcgp/backend/population/MutableElement.java34
-rw-r--r--src/jcgp/backend/population/Node.java112
-rw-r--r--src/jcgp/backend/population/Output.java69
-rw-r--r--src/jcgp/backend/population/Population.java45
-rw-r--r--src/jcgp/backend/tests/ChromosomeTests.java334
-rw-r--r--src/jcgp/backend/tests/InputTests.java47
-rw-r--r--src/jcgp/backend/tests/NodeTests.java196
-rw-r--r--src/jcgp/backend/tests/OutputTests.java93
-rw-r--r--src/jcgp/backend/tests/PopulationTests.java86
36 files changed, 2579 insertions, 0 deletions
diff --git a/src/jcgp/backend/exceptions/InsufficientConnectionsException.java b/src/jcgp/backend/exceptions/InsufficientConnectionsException.java
new file mode 100644
index 0000000..15ac845
--- /dev/null
+++ b/src/jcgp/backend/exceptions/InsufficientConnectionsException.java
@@ -0,0 +1,10 @@
+package jcgp.backend.exceptions;
+
+public class InsufficientConnectionsException extends RuntimeException {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 660740514800883541L;
+
+}
diff --git a/src/jcgp/backend/exceptions/InvalidArgumentsException.java b/src/jcgp/backend/exceptions/InvalidArgumentsException.java
new file mode 100644
index 0000000..bbae976
--- /dev/null
+++ b/src/jcgp/backend/exceptions/InvalidArgumentsException.java
@@ -0,0 +1,20 @@
+package jcgp.backend.exceptions;
+
+public class InvalidArgumentsException extends RuntimeException {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 2675108124600817777L;
+ private String reason;
+
+ public InvalidArgumentsException(String reason) {
+ super();
+ this.reason = reason;
+ }
+
+ public String getReason() {
+ return reason;
+ }
+
+}
diff --git a/src/jcgp/backend/exceptions/ManagedModuleException.java b/src/jcgp/backend/exceptions/ManagedModuleException.java
new file mode 100644
index 0000000..3452d35
--- /dev/null
+++ b/src/jcgp/backend/exceptions/ManagedModuleException.java
@@ -0,0 +1,10 @@
+package jcgp.backend.exceptions;
+
+public class ManagedModuleException extends RuntimeException {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -3450063156524494967L;
+
+}
diff --git a/src/jcgp/backend/exceptions/ParameterMismatchException.java b/src/jcgp/backend/exceptions/ParameterMismatchException.java
new file mode 100644
index 0000000..3ab9fa1
--- /dev/null
+++ b/src/jcgp/backend/exceptions/ParameterMismatchException.java
@@ -0,0 +1,10 @@
+package jcgp.backend.exceptions;
+
+public class ParameterMismatchException extends RuntimeException {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -3161318886125868134L;
+
+}
diff --git a/src/jcgp/backend/function/Arithmetic.java b/src/jcgp/backend/function/Arithmetic.java
new file mode 100644
index 0000000..6971663
--- /dev/null
+++ b/src/jcgp/backend/function/Arithmetic.java
@@ -0,0 +1,139 @@
+package jcgp.backend.function;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import jcgp.backend.exceptions.InvalidArgumentsException;
+import jcgp.backend.population.Connection;
+
+public class Arithmetic extends FunctionSet {
+
+ public Arithmetic() {
+ 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;
+
+ @Override
+ public Integer run(Connection... connections) {
+ if (connections.length < arity) {
+ throw new InvalidArgumentsException("Not enough connections were given.");
+ } else {
+ Integer arg1 = ((Integer) connections[0].getValue());
+ Integer arg2 = ((Integer) connections[1].getValue());
+ Integer result = arg1 + arg2;
+
+ return result;
+ }
+ }
+
+ @Override
+ public int getArity() {
+ return arity;
+ }
+
+ @Override
+ public String getName() {
+ return "Addition";
+ }
+ }
+
+ public static class Subtraction extends Function {
+
+ private int arity = 2;
+
+ @Override
+ public Integer run(Connection... connections) {
+ if (connections.length < arity) {
+ throw new InvalidArgumentsException("Not enough connections were given.");
+ } else {
+ Integer arg1 = ((Integer) connections[0].getValue());
+ Integer arg2 = ((Integer) connections[1].getValue());
+ Integer result = arg1 - arg2;
+
+ return result;
+ }
+ }
+
+ @Override
+ public int getArity() {
+ return arity;
+ }
+
+ @Override
+ public String getName() {
+ return "Subtraction";
+ }
+ }
+
+ public static class Multiplication extends Function {
+
+ private int arity = 2;
+
+ @Override
+ public Integer run(Connection... connections) {
+ if (connections.length < arity) {
+ throw new InvalidArgumentsException("Not enough connections were given.");
+ } else {
+ Integer arg1 = ((Integer) connections[0].getValue());
+ Integer arg2 = ((Integer) connections[1].getValue());
+ Integer result = arg1 * arg2;
+
+ return result;
+ }
+ }
+
+ @Override
+ public int getArity() {
+ return arity;
+ }
+
+ @Override
+ public String getName() {
+ return "Multiplication";
+ }
+ }
+
+ public static class Division extends Function {
+
+ private int arity = 2;
+
+ @Override
+ public Integer run(Connection... connections) {
+ if (connections.length < arity) {
+ throw new InvalidArgumentsException("Not enough connections were given.");
+ } else {
+ Integer arg1 = ((Integer) connections[0].getValue());
+ Integer arg2 = ((Integer) connections[1].getValue());
+ Integer result;
+ if (arg2 == 0) {
+ result = 0;
+ } else {
+ result = arg1 / arg2;
+ }
+
+ return result;
+ }
+ }
+
+ @Override
+ public int getArity() {
+ return arity;
+ }
+
+ @Override
+ public String getName() {
+ return "Division";
+ }
+ }
+
+}
diff --git a/src/jcgp/backend/function/BitwiseLogic.java b/src/jcgp/backend/function/BitwiseLogic.java
new file mode 100644
index 0000000..bfe361f
--- /dev/null
+++ b/src/jcgp/backend/function/BitwiseLogic.java
@@ -0,0 +1,215 @@
+package jcgp.backend.function;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import jcgp.backend.exceptions.InvalidArgumentsException;
+import jcgp.backend.population.Connection;
+
+public class BitwiseLogic extends FunctionSet {
+
+ public BitwiseLogic() {
+ 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;
+
+ @Override
+ public Object run(Connection... connections) {
+ if (connections.length < arity) {
+ throw new InvalidArgumentsException("Not enough connections were given.");
+ } else {
+ int arg1 = ((int) connections[0].getValue());
+ int arg2 = ((int) connections[1].getValue());
+ int result = arg1 & arg2;
+
+ return result;
+ }
+ }
+
+ @Override
+ public int getArity() {
+ return arity;
+ }
+
+ @Override
+ public String getName() {
+ return "AND";
+ }
+ }
+
+ public static class Or extends Function {
+ private int arity = 2;
+
+ @Override
+ public Object run(Connection... connections) {
+ if (connections.length < arity) {
+ throw new InvalidArgumentsException("Not enough connections were given.");
+ } else {
+ int arg1 = ((int) connections[0].getValue());
+ int arg2 = ((int) connections[1].getValue());
+ int result = arg1 | arg2;
+
+ return result;
+ }
+ }
+
+ @Override
+ public int getArity() {
+ return arity;
+ }
+
+ @Override
+ public String getName() {
+ return "OR";
+ }
+ }
+
+ public static class Not extends Function {
+ private int arity = 1;
+
+ @Override
+ public Object run(Connection... connections) {
+ if (connections.length < arity) {
+ throw new InvalidArgumentsException("Not enough connections were given.");
+ } else {
+ int arg1 = ((int) connections[0].getValue());
+ int result = ~arg1;
+
+ return result;
+ }
+ }
+
+ @Override
+ public int getArity() {
+ return arity;
+ }
+
+ @Override
+ public String getName() {
+ return "NOT";
+ }
+ }
+
+ public static class Xor extends Function {
+ private int arity = 2;
+
+ @Override
+ public Object run(Connection... connections) {
+ if (connections.length < arity) {
+ throw new InvalidArgumentsException("Not enough connections were given.");
+ } else {
+ int arg1 = ((int) connections[0].getValue());
+ int arg2 = ((int) connections[1].getValue());
+ int result = arg1 ^ arg2;
+
+ return result;
+ }
+ }
+
+ @Override
+ public int getArity() {
+ return arity;
+ }
+
+ @Override
+ public String getName() {
+ return "XOR";
+ }
+ }
+
+ public static class Nand extends Function {
+ private int arity = 2;
+
+ @Override
+ public Object run(Connection... connections) {
+ if (connections.length < arity) {
+ throw new InvalidArgumentsException("Not enough connections were given.");
+ } else {
+ int arg1 = ((int) connections[0].getValue());
+ int arg2 = ((int) connections[1].getValue());
+ int result = arg1 & arg2;
+
+ return ~result;
+ }
+ }
+
+ @Override
+ public int getArity() {
+ return arity;
+ }
+
+ @Override
+ public String getName() {
+ return "NAND";
+ }
+ }
+
+ public static class Nor extends Function {
+ private int arity = 2;
+
+ @Override
+ public Object run(Connection... connections) {
+ if (connections.length < arity) {
+ throw new InvalidArgumentsException("Not enough connections were given.");
+ } else {
+ int arg1 = ((int) connections[0].getValue());
+ int arg2 = ((int) connections[1].getValue());
+ int result = arg1 | arg2;
+
+ return ~result;
+ }
+ }
+
+ @Override
+ public int getArity() {
+ return arity;
+ }
+
+ @Override
+ public String getName() {
+ return "NOR";
+ }
+ }
+
+ public static class Xnor extends Function {
+ private int arity = 2;
+
+ @Override
+ public Object run(Connection... connections) {
+ if (connections.length < arity) {
+ throw new InvalidArgumentsException("Not enough connections were given.");
+ } else {
+ int arg1 = ((int) connections[0].getValue());
+ int arg2 = ((int) connections[1].getValue());
+ int result = arg1 ^ arg2;
+
+ return ~result;
+ }
+ }
+
+ @Override
+ public int getArity() {
+ return arity;
+ }
+
+ @Override
+ public String getName() {
+ return "XNOR";
+ }
+ }
+
+
+
+}
diff --git a/src/jcgp/backend/function/BooleanLogic.java b/src/jcgp/backend/function/BooleanLogic.java
new file mode 100644
index 0000000..e0b5c9c
--- /dev/null
+++ b/src/jcgp/backend/function/BooleanLogic.java
@@ -0,0 +1,215 @@
+package jcgp.backend.function;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import jcgp.backend.exceptions.InvalidArgumentsException;
+import jcgp.backend.population.Connection;
+
+public class BooleanLogic extends FunctionSet {
+
+ public BooleanLogic() {
+ 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;
+
+ @Override
+ public Boolean run(Connection... connections) {
+ if (connections.length < arity) {
+ throw new InvalidArgumentsException("Not enough connections were given.");
+ } else {
+ Boolean arg1 = ((Boolean) connections[0].getValue());
+ Boolean arg2 = ((Boolean) connections[1].getValue());
+ Boolean result = arg1 && arg2;
+
+ return result;
+ }
+ }
+
+ @Override
+ public int getArity() {
+ return arity;
+ }
+
+ @Override
+ public String getName() {
+ return "AND";
+ }
+ }
+
+ public static class Or extends Function {
+ private int arity = 2;
+
+ @Override
+ public Boolean run(Connection... connections) {
+ if (connections.length < arity) {
+ throw new InvalidArgumentsException("Not enough connections were given.");
+ } else {
+ Boolean arg1 = ((Boolean) connections[0].getValue());
+ Boolean arg2 = ((Boolean) connections[1].getValue());
+ Boolean result = arg1 || arg2;
+
+ return result;
+ }
+ }
+
+ @Override
+ public int getArity() {
+ return arity;
+ }
+
+ @Override
+ public String getName() {
+ return "OR";
+ }
+ }
+
+ public static class Not extends Function {
+ private int arity = 1;
+
+ @Override
+ public Boolean run(Connection... connections) {
+ if (connections.length < arity) {
+ throw new InvalidArgumentsException("Not enough connections were given.");
+ } else {
+ Boolean arg1 = ((Boolean) connections[0].getValue());
+ Boolean result = !arg1;
+
+ return result;
+ }
+ }
+
+ @Override
+ public int getArity() {
+ return arity;
+ }
+
+ @Override
+ public String getName() {
+ return "NOT";
+ }
+ }
+
+ public static class Xor extends Function {
+ private int arity = 2;
+
+ @Override
+ public Boolean run(Connection... connections) {
+ if (connections.length < arity) {
+ throw new InvalidArgumentsException("Not enough connections were given.");
+ } else {
+ Boolean arg1 = ((Boolean) connections[0].getValue());
+ Boolean arg2 = ((Boolean) connections[1].getValue());
+ Boolean result = arg1 ^ arg2;
+
+ return result;
+ }
+ }
+
+ @Override
+ public int getArity() {
+ return arity;
+ }
+
+ @Override
+ public String getName() {
+ return "XOR";
+ }
+ }
+
+ public static class Nand extends Function {
+ private int arity = 2;
+
+ @Override
+ public Boolean run(Connection... connections) {
+ if (connections.length < arity) {
+ throw new InvalidArgumentsException("Not enough connections were given.");
+ } else {
+ Boolean arg1 = ((Boolean) connections[0].getValue());
+ Boolean arg2 = ((Boolean) connections[1].getValue());
+ Boolean result = arg1 && arg2;
+
+ return !result;
+ }
+ }
+
+ @Override
+ public int getArity() {
+ return arity;
+ }
+
+ @Override
+ public String getName() {
+ return "NAND";
+ }
+ }
+
+ public static class Nor extends Function {
+ private int arity = 2;
+
+ @Override
+ public Boolean run(Connection... connections) {
+ if (connections.length < arity) {
+ throw new InvalidArgumentsException("Not enough connections were given.");
+ } else {
+ Boolean arg1 = ((Boolean) connections[0].getValue());
+ Boolean arg2 = ((Boolean) connections[1].getValue());
+ Boolean result = arg1 || arg2;
+
+ return !result;
+ }
+ }
+
+ @Override
+ public int getArity() {
+ return arity;
+ }
+
+ @Override
+ public String getName() {
+ return "NOR";
+ }
+ }
+
+ public static class Xnor extends Function {
+ private int arity = 2;
+
+ @Override
+ public Boolean run(Connection... connections) {
+ if (connections.length < arity) {
+ throw new InvalidArgumentsException("Not enough connections were given.");
+ } else {
+ Boolean arg1 = ((Boolean) connections[0].getValue());
+ Boolean arg2 = ((Boolean) connections[1].getValue());
+ Boolean result = arg1 ^ arg2;
+
+ return !result;
+ }
+ }
+
+ @Override
+ public int getArity() {
+ return arity;
+ }
+
+ @Override
+ public String getName() {
+ return "XNOR";
+ }
+ }
+
+
+
+}
diff --git a/src/jcgp/backend/function/Function.java b/src/jcgp/backend/function/Function.java
new file mode 100644
index 0000000..30bbcf0
--- /dev/null
+++ b/src/jcgp/backend/function/Function.java
@@ -0,0 +1,13 @@
+package jcgp.backend.function;
+
+import jcgp.backend.exceptions.InvalidArgumentsException;
+import jcgp.backend.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/backend/function/FunctionSet.java b/src/jcgp/backend/function/FunctionSet.java
new file mode 100644
index 0000000..4470ac8
--- /dev/null
+++ b/src/jcgp/backend/function/FunctionSet.java
@@ -0,0 +1,74 @@
+package jcgp.backend.function;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+
+/**
+ *
+ * @author Eduardo Pedroni
+ *
+ */
+public abstract class FunctionSet {
+ protected Function[] functionList;
+ protected ArrayList<Function> allowedFunctions;
+ protected String name;
+
+ public int getAllowedFunctionCount() {
+ return allowedFunctions.size();
+ }
+
+ public int getTotalFunctionCount() {
+ return functionList.length;
+ }
+
+ public Function getAllowedFunction(int index) {
+ return allowedFunctions.get(index);
+ }
+
+ public Function getFunction(int index) {
+ return functionList[index];
+ }
+
+ public int getMaxArity(){
+ int arity = 0;
+ for (Function function : allowedFunctions) {
+ if (function.getArity() > arity) {
+ arity = function.getArity();
+ }
+ }
+ return arity;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void disableFunction(int index) {
+ if (index < functionList.length) {
+ for (Iterator<Function> iterator = allowedFunctions.iterator(); iterator.hasNext();) {
+ Function function = (Function) iterator.next();
+ if (function == functionList[index]) {
+ iterator.remove();
+ }
+ }
+ } else {
+ throw new IndexOutOfBoundsException("Function " + index + " does not exist, the set only has " + functionList.length + " functions.");
+ }
+ }
+
+ public void enableFunction(int index) {
+ if (!allowedFunctions.contains(functionList[index])) {
+ allowedFunctions.add(functionList[index]);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ public boolean isEnabled(Function f) {
+ return allowedFunctions.contains(f);
+ }
+ } \ No newline at end of file
diff --git a/src/jcgp/backend/modules/Module.java b/src/jcgp/backend/modules/Module.java
new file mode 100644
index 0000000..f9114de
--- /dev/null
+++ b/src/jcgp/backend/modules/Module.java
@@ -0,0 +1,11 @@
+package jcgp.backend.modules;
+
+import java.util.HashMap;
+
+import jcgp.JCGP.Resources;
+import jcgp.backend.parameters.Parameter;
+
+public interface Module {
+ public HashMap<String, Parameter> getLocalParameters();
+ public ModuleStatus getStatus(Resources resources);
+}
diff --git a/src/jcgp/backend/modules/ModuleStatus.java b/src/jcgp/backend/modules/ModuleStatus.java
new file mode 100644
index 0000000..fec8490
--- /dev/null
+++ b/src/jcgp/backend/modules/ModuleStatus.java
@@ -0,0 +1,16 @@
+package jcgp.backend.modules;
+
+public enum ModuleStatus {
+ ERROR, WARNING, READY;
+
+ private String details;
+
+ public void setDetails(String details) {
+ this.details = details;
+ }
+
+ public String getDetails() {
+ return details;
+ }
+
+}
diff --git a/src/jcgp/backend/modules/ea/EvolutionaryAlgorithm.java b/src/jcgp/backend/modules/ea/EvolutionaryAlgorithm.java
new file mode 100644
index 0000000..ce457ef
--- /dev/null
+++ b/src/jcgp/backend/modules/ea/EvolutionaryAlgorithm.java
@@ -0,0 +1,15 @@
+package jcgp.backend.modules.ea;
+
+import jcgp.JCGP.Resources;
+import jcgp.backend.modules.Module;
+import jcgp.backend.modules.mutator.Mutator;
+import jcgp.backend.population.Chromosome;
+import jcgp.backend.population.Population;
+
+public interface EvolutionaryAlgorithm extends Module {
+
+ public abstract void evolve(Population population, Mutator mutator, Resources parameters);
+
+ public abstract Chromosome getFittestChromosome();
+
+}
diff --git a/src/jcgp/backend/modules/ea/MuPlusLambda.java b/src/jcgp/backend/modules/ea/MuPlusLambda.java
new file mode 100644
index 0000000..fcfba05
--- /dev/null
+++ b/src/jcgp/backend/modules/ea/MuPlusLambda.java
@@ -0,0 +1,80 @@
+package jcgp.backend.modules.ea;
+
+import java.util.HashMap;
+
+import jcgp.JCGP.Resources;
+import jcgp.backend.modules.ModuleStatus;
+import jcgp.backend.modules.mutator.Mutator;
+import jcgp.backend.parameters.IntegerParameter;
+import jcgp.backend.parameters.Parameter;
+import jcgp.backend.population.Chromosome;
+import jcgp.backend.population.Population;
+
+/**
+ * (μ + λ) EA.
+ *
+ *
+ * @author Eduardo Pedroni
+ *
+ */
+public class MuPlusLambda implements EvolutionaryAlgorithm {
+
+ private Chromosome fittestChromosome;
+
+ private IntegerParameter parents, offspring;
+
+ private HashMap<String, Parameter> localParameters;
+
+ public MuPlusLambda() {
+ 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, Resources parameters) {
+ // select fittest chromosome
+ int fittest = 0;
+
+ for (int i = 1; i < parameters.getInt("popSize"); i++) {
+ if (population.getChromosome(i).getFitness() >= population.getChromosome(fittest).getFitness()) {
+ fittest = i;
+ }
+ }
+ fittestChromosome = population.getChromosome(fittest);
+ population.setBestIndividual(fittest);
+ // create copies of fittest chromosome, mutate them
+ Chromosome fc = population.getChromosome(fittest);
+ for (int i = 0; i < parameters.getInt("popSize"); i++) {
+ if (i != fittest) {
+ population.getChromosome(i).copyConnections(fc);
+ mutator.mutate(population.getChromosome(i), parameters);
+ }
+ }
+ }
+
+ @Override
+ public Chromosome getFittestChromosome() {
+ return fittestChromosome;
+ }
+
+ @Override
+ public HashMap<String, Parameter> getLocalParameters() {
+ return localParameters;
+ }
+
+ @Override
+ public String toString() {
+ return "(μ + λ)";
+ }
+
+ @Override
+ public ModuleStatus getStatus(Resources resources) {
+ return ModuleStatus.READY;
+ }
+
+}
diff --git a/src/jcgp/backend/modules/ea/TournamentSelection.java b/src/jcgp/backend/modules/ea/TournamentSelection.java
new file mode 100644
index 0000000..6cbaa45
--- /dev/null
+++ b/src/jcgp/backend/modules/ea/TournamentSelection.java
@@ -0,0 +1,55 @@
+package jcgp.backend.modules.ea;
+
+import java.util.HashMap;
+
+import jcgp.JCGP.Resources;
+import jcgp.backend.modules.ModuleStatus;
+import jcgp.backend.modules.mutator.Mutator;
+import jcgp.backend.parameters.IntegerParameter;
+import jcgp.backend.parameters.Parameter;
+import jcgp.backend.population.Chromosome;
+import jcgp.backend.population.Population;
+
+public class TournamentSelection implements EvolutionaryAlgorithm {
+
+ private Chromosome fittestChromosome;
+
+ private IntegerParameter tournament;
+ private HashMap<String, Parameter> localParameters;
+
+ public TournamentSelection() {
+ tournament = new IntegerParameter(1, "Tournament size");
+
+ localParameters = new HashMap<String, Parameter>();
+ localParameters.put("tournament", tournament);
+ }
+
+ @Override
+ public HashMap<String, Parameter> getLocalParameters() {
+ return localParameters;
+ }
+
+ @Override
+ public void evolve(Population population, Mutator mutator,
+ Resources parameters) {
+ tournament.set(tournament.get() + 1);
+ fittestChromosome = population.getChromosome(0);
+ // TODO implement this
+
+ }
+
+ @Override
+ public Chromosome getFittestChromosome() {
+ return fittestChromosome;
+ }
+
+ @Override
+ public String toString() {
+ return "Tournament";
+ }
+
+ @Override
+ public ModuleStatus getStatus(Resources resources) {
+ return null;
+ }
+}
diff --git a/src/jcgp/backend/modules/fitness/FitnessFunction.java b/src/jcgp/backend/modules/fitness/FitnessFunction.java
new file mode 100644
index 0000000..ffce9b7
--- /dev/null
+++ b/src/jcgp/backend/modules/fitness/FitnessFunction.java
@@ -0,0 +1,11 @@
+package jcgp.backend.modules.fitness;
+
+import jcgp.JCGP.Resources;
+import jcgp.backend.modules.Module;
+import jcgp.backend.population.Population;
+
+public interface FitnessFunction extends Module {
+
+ public void evaluate(Population population, Resources resources);
+
+}
diff --git a/src/jcgp/backend/modules/fitness/TestCase.java b/src/jcgp/backend/modules/fitness/TestCase.java
new file mode 100644
index 0000000..23f7ecf
--- /dev/null
+++ b/src/jcgp/backend/modules/fitness/TestCase.java
@@ -0,0 +1,29 @@
+package jcgp.backend.modules.fitness;
+
+public class TestCase {
+
+ private Object[] inputs;
+ private Object[] outputs;
+
+ public TestCase(Object[] inputs, Object[] outputs) {
+ this.inputs = inputs;
+ this.outputs = outputs;
+ }
+
+ public Object getInput(int index) {
+ return inputs[index];
+ }
+
+ public Object getOutput(int index) {
+ return outputs[index];
+ }
+
+ public Object[] getInputs() {
+ return inputs;
+ }
+
+ public Object[] getOutputs() {
+ return outputs;
+ }
+
+}
diff --git a/src/jcgp/backend/modules/fitness/TestCaseEvaluator.java b/src/jcgp/backend/modules/fitness/TestCaseEvaluator.java
new file mode 100644
index 0000000..3b67f28
--- /dev/null
+++ b/src/jcgp/backend/modules/fitness/TestCaseEvaluator.java
@@ -0,0 +1,45 @@
+package jcgp.backend.modules.fitness;
+
+import java.util.HashMap;
+
+import jcgp.JCGP.Resources;
+import jcgp.backend.modules.ModuleStatus;
+import jcgp.backend.parameters.Parameter;
+import jcgp.backend.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 < resources.getInt("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 < resources.getInt("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> getLocalParameters() {
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "Test case";
+ }
+
+ @Override
+ public ModuleStatus getStatus(Resources resources) {
+ return null;
+ }
+}
diff --git a/src/jcgp/backend/modules/mutator/Mutator.java b/src/jcgp/backend/modules/mutator/Mutator.java
new file mode 100644
index 0000000..fd7cd27
--- /dev/null
+++ b/src/jcgp/backend/modules/mutator/Mutator.java
@@ -0,0 +1,11 @@
+package jcgp.backend.modules.mutator;
+
+import jcgp.JCGP.Resources;
+import jcgp.backend.modules.Module;
+import jcgp.backend.population.Chromosome;
+
+public interface Mutator extends Module {
+
+ void mutate(Chromosome chromosome, Resources parameters);
+
+}
diff --git a/src/jcgp/backend/modules/mutator/PointMutator.java b/src/jcgp/backend/modules/mutator/PointMutator.java
new file mode 100644
index 0000000..62d827d
--- /dev/null
+++ b/src/jcgp/backend/modules/mutator/PointMutator.java
@@ -0,0 +1,73 @@
+package jcgp.backend.modules.mutator;
+
+import java.util.HashMap;
+
+import jcgp.backend.function.Function;
+import jcgp.backend.modules.ModuleStatus;
+import jcgp.backend.parameters.DoubleParameter;
+import jcgp.backend.parameters.Parameter;
+import jcgp.backend.population.Chromosome;
+import jcgp.backend.population.MutableElement;
+import jcgp.backend.population.Node;
+import jcgp.backend.population.Output;
+import jcgp.JCGP.Resources;
+
+public class PointMutator implements Mutator {
+
+ private DoubleParameter mutationRate;
+ private HashMap<String, Parameter> localParameters;
+
+ private ModuleStatus status = ModuleStatus.READY;
+
+ public PointMutator() {
+ mutationRate = new DoubleParameter(0.5, "Percent mutation", false, false, false);
+
+ localParameters = new HashMap<String, Parameter>();
+ localParameters.put("mutRate", mutationRate);
+ }
+
+ @Override
+ public void mutate(Chromosome chromosome, Resources resources) {
+ int mutations = (int) Math.ceil(((mutationRate.get()) * ((((resources.getDouble("nodes")) + (resources.getDouble("outputs")))) / (double) 100)));
+ for (int i = 0; i < mutations; i++) {
+ MutableElement m = chromosome.getRandomMutableElement();
+
+ if (m instanceof Output) {
+ m.setConnection(0, chromosome.getRandomConnection());
+ } else if (m instanceof Node) {
+ int geneType = resources.getRandomInt(1 + resources.getInt("arity"));
+ if (geneType < 1) {
+ Function f = resources.getRandomFunction();
+ ((Node) m).setFunction(f);
+ } else {
+ m.setConnection(resources.getRandomInt(resources.getInt("arity")), chromosome.getRandomConnection(((Node) m).getColumn()));
+ }
+ }
+ }
+ }
+
+ @Override
+ public HashMap<String, Parameter> getLocalParameters() {
+ return localParameters;
+ }
+
+ @Override
+ public String toString() {
+ return "Point mutation";
+ }
+
+ @Override
+ public ModuleStatus getStatus(Resources resources) {
+ if (mutationRate.get() <= 0 || mutationRate.get() > 100) {
+ status = ModuleStatus.ERROR;
+ status.setDetails("Mutation rate must be > 0 and <= 100");
+ } else if ((int) ((mutationRate.get() / 100) * resources.getDouble("nodes")) > 0) {
+ status = ModuleStatus.WARNING;
+ status.setDetails("With mutation rate " + mutationRate.get() + ", no mutations will occur.");
+ } else {
+ status = ModuleStatus.READY;
+ status.setDetails("");
+ }
+ return status;
+ }
+}
diff --git a/src/jcgp/backend/parameters/BooleanParameter.java b/src/jcgp/backend/parameters/BooleanParameter.java
new file mode 100644
index 0000000..d5904cd
--- /dev/null
+++ b/src/jcgp/backend/parameters/BooleanParameter.java
@@ -0,0 +1,32 @@
+package jcgp.backend.parameters;
+
+import javafx.beans.property.SimpleBooleanProperty;
+
+public class BooleanParameter extends Parameter {
+
+ private SimpleBooleanProperty value;
+
+ public BooleanParameter(boolean value, String name, boolean monitor, boolean hidden, boolean critical) {
+ super(name, monitor, hidden, critical);
+ this.value = new SimpleBooleanProperty(value);
+ }
+
+ public BooleanParameter(boolean value, String name) {
+ super(name, false, false, true);
+ this.value = new SimpleBooleanProperty(value);
+ }
+
+ public boolean get() {
+ return value.get();
+ }
+
+ public void set(boolean newValue) {
+ if (!value.isBound()) {
+ value.set(newValue);
+ }
+ }
+
+ public SimpleBooleanProperty valueProperty() {
+ return value;
+ }
+}
diff --git a/src/jcgp/backend/parameters/DoubleParameter.java b/src/jcgp/backend/parameters/DoubleParameter.java
new file mode 100644
index 0000000..26031be
--- /dev/null
+++ b/src/jcgp/backend/parameters/DoubleParameter.java
@@ -0,0 +1,33 @@
+package jcgp.backend.parameters;
+
+import javafx.beans.property.SimpleDoubleProperty;
+
+public class DoubleParameter extends Parameter {
+
+ protected SimpleDoubleProperty value;
+
+ public DoubleParameter(double value, String name, boolean monitor, boolean hidden, boolean critical) {
+ super(name, monitor, hidden, critical);
+ this.value = new SimpleDoubleProperty(value);
+ }
+
+ public DoubleParameter(double value, String name) {
+ super(name, false, false, true);
+ this.value = new SimpleDoubleProperty(value);
+ }
+
+ public double get() {
+ return value.get();
+ }
+
+ public void set(double newValue) {
+ if (!value.isBound()) {
+ value.set(newValue);
+ }
+ }
+
+ public SimpleDoubleProperty valueProperty() {
+ return value;
+ }
+
+}
diff --git a/src/jcgp/backend/parameters/IntegerParameter.java b/src/jcgp/backend/parameters/IntegerParameter.java
new file mode 100644
index 0000000..dbfa5c5
--- /dev/null
+++ b/src/jcgp/backend/parameters/IntegerParameter.java
@@ -0,0 +1,33 @@
+package jcgp.backend.parameters;
+
+import javafx.beans.property.SimpleIntegerProperty;
+
+public class IntegerParameter extends Parameter {
+
+ protected SimpleIntegerProperty value;
+
+ public IntegerParameter(int value, String name, boolean monitor, boolean hidden, boolean critical) {
+ super(name, monitor, hidden, critical);
+ this.value = new SimpleIntegerProperty(value);
+ }
+
+ public IntegerParameter(int value, String name) {
+ super(name, false, false, true);
+ this.value = new SimpleIntegerProperty(value);
+ }
+
+ public int get() {
+ return value.get();
+ }
+
+ public void set(int newValue) {
+ if (!value.isBound()) {
+ value.set(newValue);
+ }
+ }
+
+ public SimpleIntegerProperty valueProperty() {
+ return value;
+ }
+
+}
diff --git a/src/jcgp/backend/parameters/Parameter.java b/src/jcgp/backend/parameters/Parameter.java
new file mode 100644
index 0000000..2f584a4
--- /dev/null
+++ b/src/jcgp/backend/parameters/Parameter.java
@@ -0,0 +1,35 @@
+package jcgp.backend.parameters;
+
+import javafx.beans.property.Property;
+
+public abstract class Parameter {
+
+ protected boolean hidden, monitor, critical;
+
+ protected String name;
+
+ public Parameter(String name, boolean monitor, boolean hidden, boolean critical) {
+ this.name = name;
+ this.hidden = hidden;
+ this.monitor = monitor;
+ this.critical = critical;
+ }
+
+ public boolean isHidden() {
+ return hidden;
+ }
+
+ public boolean isMonitor() {
+ return monitor;
+ }
+
+ public boolean isCritical() {
+ return critical;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public abstract Property<?> valueProperty();
+}
diff --git a/src/jcgp/backend/population/Chromosome.java b/src/jcgp/backend/population/Chromosome.java
new file mode 100644
index 0000000..18ae9bb
--- /dev/null
+++ b/src/jcgp/backend/population/Chromosome.java
@@ -0,0 +1,335 @@
+package jcgp.backend.population;
+
+import java.util.ArrayList;
+
+import jcgp.JCGP.Resources;
+import jcgp.backend.exceptions.ParameterMismatchException;
+
+public class Chromosome {
+
+ private Resources resources;
+
+ private Input[] inputs;
+ private Node[][] nodes;
+ private Output[] outputs;
+
+ private ArrayList<Node> activeNodes;
+
+ private int fitness = 0;
+ private boolean recomputeActiveNodes = true;
+
+ /**
+ * Initialise a chromosome with the specified parameters. Random valid connections
+ * are created.
+ *
+ *
+ */
+ 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
+ initialiseConnections();
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * Initialise a new chromosome with the exact same connections as a given instance of 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
+ copyConnections(clone);
+ }
+
+ /**
+ *
+ */
+ private void instantiateElements() {
+ inputs = new Input[(resources.getInt("inputs"))];
+ for (int i = 0; i < (resources.getInt("inputs")); i++) {
+ inputs[i] = new Input(i);
+ }
+
+ int arity = resources.getInt("arity");
+
+ // rows first
+ nodes = new Node[(resources.getInt("rows"))][(resources.getInt("columns"))];
+ for (int r = 0; r < (resources.getInt("rows")); r++) {
+ for (int c = 0; c < (resources.getInt("columns")); c++) {
+ nodes[r][c] = new Node(this, r, c, arity);
+ }
+ }
+ outputs = new Output[(resources.getInt("outputs"))];
+ for (int o = 0; o < (resources.getInt("outputs")); o++) {
+ outputs[o] = new Output(this, o);
+ }
+ }
+
+ /**
+ *
+ */
+ private void initialiseConnections() {
+
+ int arity = resources.getInt("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[arity];
+ for (int i = 0; i < connections.length; i++) {
+ connections[i] = getRandomConnection(c);
+ }
+ nodes[r][c].initialise(resources.getRandomFunction(), connections);
+ }
+ }
+
+ for (Output output : outputs) {
+ output.setConnection(0, getRandomConnection());
+ }
+
+ }
+
+ /**
+ * @param clone
+ */
+ public void copyConnections(Chromosome clone) {
+ int arity = resources.getInt("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[arity];
+ // populate with connections equivalent to clone
+ Connection copyConnection;
+ for (int i = 0; i < connections.length; i++) {
+ copyConnection = clone.getNode(r, c).getConnection(i);
+ if (copyConnection instanceof Input) {
+ connections[i] = inputs[((Input) copyConnection).getIndex()];
+ } else if (copyConnection instanceof Node) {
+ connections[i] = nodes[((Node) copyConnection).getRow()][((Node) copyConnection).getColumn()];
+ } else {
+ System.out.println("Warning: Connection of subtype " + copyConnection.getClass().toString() + " is not explicitly handled by copy constructor.");
+ }
+ }
+ // initialise with copied arguments
+ nodes[r][c].initialise(clone.getNode(r, c).getFunction(), connections);
+ }
+ }
+
+ // do the same to outputs
+ Connection copyOutput;
+ for (int o = 0; o < outputs.length; o++) {
+ copyOutput = clone.getOutput(o).getSource();
+ if (copyOutput instanceof Input) {
+ outputs[o].setConnection(0, inputs[((Input) copyOutput).getIndex()]);
+ } else if (copyOutput instanceof Node) {
+ 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.");
+ }
+ }
+ }
+
+ public Node getNode(int row, int column) {
+ return nodes[row][column];
+ }
+
+ public Output getOutput(int index) {
+ return outputs[index];
+ }
+
+ public Input getInput(int index) {
+ return inputs[index];
+ }
+
+ public int getFitness() {
+ return fitness;
+ }
+
+ public void setFitness(int newFitness) {
+ fitness = newFitness;
+ }
+
+ /**
+ *
+ * @param values
+ * @throws ParameterMismatchException
+ */
+ public void setInputs(Object ... values) throws ParameterMismatchException {
+ // if the values provided don't match the specified number of inputs, the user should be warned
+ if (values.length == inputs.length) {
+ // set inputs for evaluation
+ for (int i = 0; i < values.length; i++) {
+ inputs[i].setValue(values[i]);
+ }
+ } else {
+ throw new ParameterMismatchException();
+ }
+ }
+
+ /**
+ * This method is useful for mutating chromosomes.
+ *
+ * @return a random element that can be mutated - Node or Output
+ */
+ public MutableElement getRandomMutableElement() {
+ // choose output or node
+ int index = resources.getRandomInt(outputs.length + (resources.getInt("rows")) * (resources.getInt("columns")));
+
+ if (index < outputs.length) {
+ // outputs
+ return outputs[index];
+ } else {
+ // node
+ index -= outputs.length;
+ return nodes[index / (resources.getInt("columns"))][index % (resources.getInt("columns"))];
+ }
+ }
+
+ /**
+ * Returns a random allowed connection respecting levels back.</br>
+ * This method may always pick inputs, as they can be picked
+ * regardless of the column.
+ *
+ * @param column the column to use as reference
+ * @return a random connection
+ */
+ public Connection getRandomConnection(int column) {
+ // work out the allowed range obeying levels back
+ int allowedColumns = ((column >= (resources.getInt("levelsBack"))) ? (resources.getInt("levelsBack")) : column);
+ int offset = ((column - allowedColumns) * nodes.length) - inputs.length;
+
+ // choose input or allowed node
+ int index = resources.getRandomInt(inputs.length + (nodes.length * allowedColumns));
+ if (index < inputs.length) {
+ // input
+ return inputs[index];
+ } else {
+ // node
+ // offset it to address the right columns
+ index += offset;
+ return nodes[index % nodes.length][index / nodes.length];
+ }
+ }
+
+ /**
+ * This method will pick a completely random connection, independently
+ * of levels back, including inputs.
+ *
+ * Useful for setting outputs.
+ *
+ *
+ * @return a random connection
+ */
+ public Connection getRandomConnection() {
+ // choose output or node
+ int index = resources.getRandomInt(inputs.length + (resources.getInt("columns")) * (resources.getInt("rows")));
+ if (index < inputs.length) {
+ // outputs
+ return inputs[index];
+ } else {
+ // node
+ index -= inputs.length;
+ return nodes[index / (resources.getInt("columns"))][index % (resources.getInt("columns"))];
+ }
+ }
+
+ /**
+ * This causes the list of active nodes to be recomputed lazily (once it is actually requested).
+ */
+ public void recomputeActiveNodes() {
+ recomputeActiveNodes = true;
+ }
+
+ /**
+ * This method computes a list of active connections (if necessary) and returns it.
+ *
+ * @return
+ */
+ public ArrayList<Node> getActiveNodes() {
+ computeActiveNodes();
+ return activeNodes;
+ }
+
+ private void computeActiveNodes() {
+ // lazy recomputation has been triggered, do it
+ if (recomputeActiveNodes) {
+ recomputeActiveNodes = false;
+ activeNodes = new ArrayList<Node>();
+
+ for (Output output : outputs) {
+ output.getActiveNodes(activeNodes);
+ }
+ }
+
+ }
+
+ public boolean compareTo(Chromosome chromosome) {
+ for (int r = 0; r < (resources.getInt("rows")); r++) {
+ for (int c = 0; c < (resources.getInt("columns")); c++) {
+ if (!(nodes[r][c].copyOf(chromosome.getNode(r, c)))) {
+ return false;
+ }
+ }
+ }
+
+ for (int o = 0; o < (resources.getInt("outputs")); o++) {
+ if (!(outputs[o].copyOf(chromosome.getOutput(o)))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public boolean compareActiveTo(Chromosome chromosome) {
+ // update list if it is out of date
+ computeActiveNodes();
+
+ if (activeNodes.size() == chromosome.getActiveNodes().size()) {
+ for (int i = 0; i < activeNodes.size(); i++) {
+ if (!(activeNodes.get(i).copyOf(chromosome.getActiveNodes().get(i)))){
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public void printNodes() {
+ int arity = resources.getInt("arity");
+
+ for (int r = 0; r < (resources.getInt("rows")); r++) {
+ System.out.print("r: " + r + "\t");
+ for (int c = 0; c < (resources.getInt("columns")); c++) {
+ System.out.print("N: (" + r + ", " + c + ") ");
+ 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().getName() + "\t");
+ }
+ System.out.print("\n");
+ }
+
+ for (int o = 0; o < (resources.getInt("outputs")); o++) {
+ System.out.print("o: " + o + " (" + outputs[o].getSource().getDescription() + ")\t");
+ }
+ }
+
+ public Resources getParameters() {
+ return resources;
+ }
+}
diff --git a/src/jcgp/backend/population/Connection.java b/src/jcgp/backend/population/Connection.java
new file mode 100644
index 0000000..ea4f10f
--- /dev/null
+++ b/src/jcgp/backend/population/Connection.java
@@ -0,0 +1,9 @@
+package jcgp.backend.population;
+
+public interface Connection {
+
+ public Object getValue();
+
+ public String getDescription();
+
+}
diff --git a/src/jcgp/backend/population/Gene.java b/src/jcgp/backend/population/Gene.java
new file mode 100644
index 0000000..6b90949
--- /dev/null
+++ b/src/jcgp/backend/population/Gene.java
@@ -0,0 +1,5 @@
+package jcgp.backend.population;
+
+public abstract class Gene {
+
+}
diff --git a/src/jcgp/backend/population/Input.java b/src/jcgp/backend/population/Input.java
new file mode 100644
index 0000000..83fba07
--- /dev/null
+++ b/src/jcgp/backend/population/Input.java
@@ -0,0 +1,29 @@
+package jcgp.backend.population;
+
+public class Input extends Gene implements Connection {
+
+ private Object value = 0;
+ private int index;
+
+ public Input(int index) {
+ this.index = index;
+ }
+
+ public void setValue(Object newValue) {
+ value = newValue;
+ }
+
+ @Override
+ public Object getValue() {
+ return value;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ @Override
+ public String getDescription() {
+ return "i: " + index;
+ }
+}
diff --git a/src/jcgp/backend/population/MutableElement.java b/src/jcgp/backend/population/MutableElement.java
new file mode 100644
index 0000000..8548e63
--- /dev/null
+++ b/src/jcgp/backend/population/MutableElement.java
@@ -0,0 +1,34 @@
+package jcgp.backend.population;
+
+public interface MutableElement {
+
+ /**
+ * 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>
+ * - the connections of the compared elements are not the same instance;</br>
+ * - the elements have the same function (in the case of Node);</br>
+ * - the grid position of the elements themselves are the same;</br>
+ * - the grid position of all equivalent connections are the same;</br></br>
+ *
+ * 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 since it is possible that a == c.</br>
+ *
+ * @param m
+ * @return
+ */
+ boolean copyOf(MutableElement m);
+
+}
diff --git a/src/jcgp/backend/population/Node.java b/src/jcgp/backend/population/Node.java
new file mode 100644
index 0000000..f1d37a1
--- /dev/null
+++ b/src/jcgp/backend/population/Node.java
@@ -0,0 +1,112 @@
+package jcgp.backend.population;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import jcgp.backend.exceptions.InsufficientConnectionsException;
+import jcgp.backend.function.Function;
+
+
+public class Node extends Gene implements MutableElement, Connection {
+
+ private Function function;
+ private Connection[] connections;
+ private int column, row;
+ private Chromosome chromosome;
+
+ public Node(Chromosome chromosome, int row, int column, int arity) {
+ this.chromosome = chromosome;
+ this.column = column;
+ this.row = row;
+ }
+
+ @Override
+ public Object getValue() {
+ return function.run(Arrays.copyOfRange(connections, 0, function.getArity()));
+ }
+
+ public void setFunction(Function newFunction) {
+ function = newFunction;
+ }
+
+ @Override
+ public void setConnection(int index, Connection newConnection) {
+ connections[index] = newConnection;
+ chromosome.recomputeActiveNodes();
+ }
+
+ public void initialise(Function newFunction, Connection ... newConnections) throws InsufficientConnectionsException {
+ function = newFunction;
+ if (newConnections.length == function.getArity()) {
+ connections = newConnections;
+ } else {
+ throw new InsufficientConnectionsException();
+ }
+ }
+
+ public int getColumn() {
+ return column;
+ }
+
+ public int getRow() {
+ return row;
+ }
+
+ public Function getFunction() {
+ return function;
+ }
+
+ public Connection getConnection(int index) {
+ return connections[index];
+ }
+
+ 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);
+ }
+
+ }
+ }
+
+ @Override
+ public boolean copyOf(MutableElement m) {
+ if (this != m) {
+ if (m instanceof Node) {
+ Node n = (Node) m;
+ if (function == 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()) {
+ 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()) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String getDescription() {
+ return "n: " + row + ", " + column;
+ }
+}
diff --git a/src/jcgp/backend/population/Output.java b/src/jcgp/backend/population/Output.java
new file mode 100644
index 0000000..aa94ab6
--- /dev/null
+++ b/src/jcgp/backend/population/Output.java
@@ -0,0 +1,69 @@
+package jcgp.backend.population;
+
+import java.util.ArrayList;
+
+public class Output extends Gene implements MutableElement {
+
+ private 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() {
+ Object result = source.getValue();
+ return result;
+ }
+
+ @Override
+ public void setConnection(int index, Connection newConnection) {
+ source = newConnection;
+ chromosome.recomputeActiveNodes();
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public Connection getSource() {
+ return source;
+ }
+
+// public SimpleObjectProperty<Connection> sourceProperty() {
+// return source;
+// }
+
+ public void getActiveNodes(ArrayList<Node> activeNodes) {
+ if (source instanceof Node) {
+ ((Node) source).getActive(activeNodes);
+ }
+ }
+
+ @Override
+ public boolean copyOf(MutableElement m) {
+ if (this != m) {
+ 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()) {
+ 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()) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/jcgp/backend/population/Population.java b/src/jcgp/backend/population/Population.java
new file mode 100644
index 0000000..7049d79
--- /dev/null
+++ b/src/jcgp/backend/population/Population.java
@@ -0,0 +1,45 @@
+package jcgp.backend.population;
+
+import jcgp.JCGP.Resources;
+
+
+public class Population {
+
+ private Chromosome[] chromosomes;
+ private int fittest;
+
+ public Population(Resources resources) {
+ chromosomes = new Chromosome[(resources.getInt("popSize"))];
+ for (int c = 0; c < chromosomes.length; c++) {
+ chromosomes[c] = new Chromosome(resources);
+ }
+ }
+
+ public Population(Chromosome parent, Resources resources) {
+ chromosomes = new Chromosome[(resources.getInt("popSize"))];
+ // make a clone for safety
+ this.chromosomes[0] = new Chromosome(parent);
+ // generate the rest of the individuals
+ for (int c = 1; c < chromosomes.length; c++) {
+ chromosomes[c] = new Chromosome(chromosomes[0]);
+ }
+ }
+
+ /**
+ * Returns all chromosomes, parents first, then offspring.
+ *
+ * @param index
+ * @return
+ */
+ public Chromosome getChromosome(int index) {
+ return chromosomes[index];
+ }
+
+ public void setBestIndividual(int index) {
+ fittest = index;
+ }
+
+ public Chromosome getBestIndividual() {
+ return chromosomes[fittest];
+ }
+}
diff --git a/src/jcgp/backend/tests/ChromosomeTests.java b/src/jcgp/backend/tests/ChromosomeTests.java
new file mode 100644
index 0000000..1cd067e
--- /dev/null
+++ b/src/jcgp/backend/tests/ChromosomeTests.java
@@ -0,0 +1,334 @@
+package jcgp.backend.tests;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import jcgp.JCGP.Resources;
+import jcgp.backend.population.Chromosome;
+import jcgp.backend.population.Connection;
+import jcgp.backend.population.Input;
+import jcgp.backend.population.MutableElement;
+import jcgp.backend.population.Node;
+import jcgp.backend.population.Output;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ *
+ * Tests which cover the behaviour specified for a chromosome.
+ *
+ * - The chromosome should be able to return a specified node, input or output.
+ * - It should be able to return a random MutableElement.
+ * - It should be able to return a random allowed connection given a column.
+ * - It should be able to return a random connection.
+ * - It should contain a freely modifiable fitness value.
+ * - For truth table evaluations, it should be able to have its inputs set.
+ * - For truth table evaluations, the output should return a value according to the inputs.
+ * - It should feature a copy method, which creates a deep copy of a specified Chromosome object.
+ * - It should be able to return a list of active nodes.
+ * - It should contain a method to evaluate whether a given chromosome is identical
+ * to it.
+ * - Same as above, but only looking at the active portion of a chromosome.
+ *
+ *
+ * WARNING: changing parameters may cause the tests to incorrectly fail!
+ *
+ * @author Eduardo Pedroni
+ *
+ */
+public class ChromosomeTests {
+
+ private Chromosome chromosome;
+ private static Resources resources;
+
+ @BeforeClass
+ public static void setUpBeforeClass() {
+ resources = new Resources();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ chromosome = new Chromosome(resources);
+ }
+
+ /**
+ *
+ */
+ @Test
+ public void cloneTest() {
+ // create a clone, check to see if it really is a clone
+ Chromosome clone = new Chromosome(chromosome);
+
+ // compare all elements, one by one
+ // check outputs
+ for (int o = 0; o < resources.getInt("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) &&
+ clone.getOutput(o).getSource() != chromosome.getOutput(o).getSource());
+ // check that the connections are equivalent
+ if (clone.getOutput(o).getSource() instanceof Input && chromosome.getOutput(o).getSource() instanceof Input) {
+ assertTrue("Outputs did not connect to equivalent inputs.",
+ ((Input) clone.getOutput(o).getSource()).getIndex() == ((Input) chromosome.getOutput(o).getSource()).getIndex());
+ } else if (clone.getOutput(o).getSource() instanceof Node && chromosome.getOutput(o).getSource() instanceof Node) {
+ assertTrue("Outputs did not connect to equivalent nodes.",
+ ((Node) clone.getOutput(o).getSource()).getRow() == ((Node) chromosome.getOutput(o).getSource()).getRow() &&
+ ((Node) clone.getOutput(o).getSource()).getColumn() == ((Node) chromosome.getOutput(o).getSource()).getColumn());
+ } else {
+ fail("Output source types did not match.");
+ }
+ }
+ // check nodes, rows first
+ for (int row = 0; row < resources.getInt("rows"); row++) {
+ for (int column = 0; column < resources.getInt("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
+ assertTrue("Equivalent nodes self-reference differently.", clone.getNode(row, column).getRow() == chromosome.getNode(row, column).getRow() &&
+ clone.getNode(row, column).getColumn() == chromosome.getNode(row, column).getColumn());
+ // check that the two nodes have the same function
+ assertTrue("Equivalent nodes have different functions.", clone.getNode(row, column).getFunction() == chromosome.getNode(row, column).getFunction());
+
+ // compare each connection
+ for (int connection = 0; connection < resources.getInt("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));
+
+ // if the connections aren't the same instance, check that their addresses are the same
+ if (clone.getNode(row, column).getConnection(connection) instanceof Input &&
+ chromosome.getNode(row, column).getConnection(connection) instanceof Input) {
+
+ assertTrue("Nodes did not connect to equivalent inputs.",
+ ((Input) clone.getNode(row, column).getConnection(connection)).getIndex() ==
+ ((Input) chromosome.getNode(row, column).getConnection(connection)).getIndex());
+
+ } else if (clone.getNode(row, column).getConnection(connection) instanceof Node &&
+ chromosome.getNode(row, column).getConnection(connection) instanceof Node) {
+
+ assertTrue("Nodes did not connect to equivalent nodes.",
+ ((Node) clone.getNode(row, column).getConnection(connection)).getRow() ==
+ ((Node) chromosome.getNode(row, column).getConnection(connection)).getRow() &&
+
+ ((Node) clone.getNode(row, column).getConnection(connection)).getColumn() ==
+ ((Node) chromosome.getNode(row, column).getConnection(connection)).getColumn());
+
+ } else {
+ fail("Connection types did not match.");
+ }
+ }
+ }
+ }
+
+ // check cloning given a known topology
+ chromosome = createKnownConfiguration();
+ clone = new Chromosome(chromosome);
+
+ Integer[] testInputs = new Integer[] {5, 8, 4};
+ chromosome.setInputs((Object[]) testInputs);
+ clone.setInputs((Object[]) testInputs);
+
+ // check that both chromosomes have the same outputs
+ for (int i = 0; i < resources.getInt("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(resources.getRandomInt(resources.getInt("arity")), clone.getInput(2));
+
+ assertTrue("Mutation affected nodes in both chromosomes.",
+ clone.getOutput(1).calculate() != chromosome.getOutput(1).calculate());
+
+ }
+ /**
+ *
+ */
+ @Test
+ public void fitnessTest() {
+ // set a fitness value, check if returned value is the same
+ chromosome.setFitness(10);
+ assertTrue("Incorrect fitness returned.", chromosome.getFitness() == 10);
+ }
+
+ /**
+ *
+ */
+ @Test
+ public void randomConnectionTest() {
+ // get random connections with column 0, check that they are all inputs
+ for (int i = 0; i < 10000; i++) {
+ boolean connectionReturn = chromosome.getRandomConnection(0) instanceof Input;
+ assertTrue("Connection is not an input.", connectionReturn);
+ }
+
+ // 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 = resources.getInt("columns") - 1;
+ for (int i = 0; i < connectionPicks; i++) {
+ Connection c = chromosome.getRandomConnection(chosenColumn);
+ if (c instanceof Node) {
+ connectionNodes++;
+ if (((Node) c).getColumn() >= chosenColumn) {
+ connectionOutOfRange++;
+ }
+ assertTrue("Connection is not allowed : " + ((Node) c).getColumn(), ((Node) c).getColumn() < chosenColumn && ((Node) c).getColumn() < chosenColumn);
+ } else if (c instanceof Input) {
+ connectionInputs++;
+ } else {
+ fail("Return is neither Node nor Input.");
+
+ }
+ }
+ System.out.println("Out of " + connectionPicks + " connections picked from " + ((chosenColumn >= resources.getInt("levelsBack")) ? resources.getInt("levelsBack") : chosenColumn) * resources.getInt("rows") +
+ " allowed nodes and " + resources.getInt("inputs") + " inputs, " + connectionNodes + " were nodes and " + connectionInputs + " were inputs.");
+
+ System.out.println("Node/input ratio: " + (chosenColumn >= resources.getInt("levelsBack") ? resources.getInt("levelsBack") : chosenColumn) * resources.getDouble("rows") / resources.getDouble("inputs") +
+ ", picked ratio: " + (double) connectionNodes / (double) connectionInputs);
+
+ System.out.println(connectionOutOfRange + " nodes that disrespected levels back were picked.");
+ }
+ /**
+ *
+ */
+ @Test
+ public void randomMutableTest() {
+ // get mutable elements, check Node to Output ratio
+ int mutablePicks = 100000;
+ int mutableNodes = 0, mutableOutputs = 0;
+ for (int i = 0; i < mutablePicks; i++) {
+ MutableElement m = chromosome.getRandomMutableElement();
+
+ if (m instanceof Node) {
+ mutableNodes++;
+ } else if (m instanceof Output) {
+ mutableOutputs++;
+ } else {
+ fail("Return is neither Node nor Output.");
+ }
+ }
+ System.out.println("Out of " + mutablePicks + " mutable elements picked from " + resources.getInt("nodes") +
+ " nodes and " + resources.getInt("outputs") + " outputs, " + mutableNodes + " were nodes and " +
+ mutableOutputs + " were outputs.");
+ System.out.println("Node/output ratio: " + resources.getDouble("nodes") / resources.getDouble("outputs") +
+ ", picked ratio: " + (double) mutableNodes / (double) mutableOutputs + "\n");
+ }
+
+ /**
+ *
+ */
+ @Test
+ public void getOutputsTest() {
+ chromosome = createKnownConfiguration();
+
+ chromosome.setInputs(5, 8, 4);
+
+ // with this configuration, the outputs should be 13 and 25.
+ assertTrue("Incorrect output returned.", (Integer) chromosome.getOutput(0).calculate() == 13);
+ assertTrue("Incorrect output returned.", (Integer) chromosome.getOutput(1).calculate() == 25);
+ }
+
+ /**
+ *
+ */
+ @Test
+ public void setInputTest() {
+ // set input values, check that acquired values are correct
+ Integer[] testInputs = new Integer[resources.getInt("inputs")];
+ for (int i = 0; i < resources.getInt("inputs"); i++) {
+ testInputs[i] = i * 2 - 3;
+ }
+ chromosome.setInputs((Object[]) testInputs);
+ for (int i = 0; i < resources.getInt("inputs"); i++) {
+ assertTrue("Incorrect input returned.", ((Integer) chromosome.getInput(i).getValue()) == i * 2 - 3);
+ }
+ }
+
+ /**
+ *
+ */
+ @Test
+ public void getNodeTest() {
+ // get all nodes one by one, check that they are all correct
+ for (int r = 0; r < resources.getInt("rows"); r++) {
+ for (int c = 0; c < resources.getInt("columns"); c++) {
+ assertTrue("Incorrect node returned.", chromosome.getNode(r, c).getColumn() == c &&
+ chromosome.getNode(r, c).getRow() == r);
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ @Test
+ public void activeNodeTest() {
+ // active node detection happens recursively, the user only calls a single method
+ // set connections to a known configuration
+ chromosome = createKnownConfiguration();
+
+ assertTrue("Active node missing from list.", chromosome.getActiveNodes().contains(chromosome.getNode(0, 0)));
+ assertTrue("Active node missing from list.", chromosome.getActiveNodes().contains(chromosome.getNode(1, 1)));
+ assertTrue("Active node missing from list.", chromosome.getActiveNodes().contains(chromosome.getNode(1, 2)));
+
+ assertTrue("List has the wrong number of nodes.", chromosome.getActiveNodes().size() == 3);
+ }
+
+ /**
+ *
+ */
+ @Test
+ public void compareActiveTest() {
+ // create a clone of the chromosome, compare active nodes - should return true
+ Chromosome c = new Chromosome(chromosome);
+ assertTrue("Active nodes did not match.", chromosome.compareActiveTo(c));
+ assertTrue("Symmetry not obeyed.", c.compareActiveTo(chromosome));
+
+ // create a new random chromosome, this time they should not match
+ c = new Chromosome(resources);
+ assertTrue("Active nodes did match.", !chromosome.compareActiveTo(c));
+ }
+
+ /**
+ *
+ */
+ @Test
+ public void compareTest() {
+ // create a clone of the chromosome, compare - should return true
+ Chromosome c = new Chromosome(chromosome);
+ assertTrue("Chromosomes did not match.", chromosome.compareTo(c));
+ assertTrue("Symmetry not obeyed.", c.compareTo(chromosome));
+
+ // create a new random chromosome, this time they should not match
+ c = new Chromosome(resources);
+ assertTrue("Chromosomes did match.", !chromosome.compareTo(c));
+ }
+ /**
+ * Utility for creating a chromosome of known configuration.
+ * Topology is 3x3, with 3 inputs and 2 outputs.
+ * Given inputs 5, 8 and 4 outputs should be 13 and 25.
+ *
+ * Active nodes (r, c): [0, 0], [1, 1], [1, 2]
+ *
+ * @return the configured chromosome
+ */
+ private Chromosome createKnownConfiguration() {
+ // with a small topology, build a chromosome of known connections and check outputs
+ resources.set("columns", 3);
+ resources.set("rows", 3);
+ resources.set("inputs", 3);
+ resources.set("outputs", 2);
+ resources.set("levelsBack", 3);
+
+ Chromosome c = new Chromosome(resources);
+
+ 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(0, c.getNode(0, 0));
+ c.getOutput(1).setConnection(0, c.getNode(1, 2));
+
+ return c;
+ }
+}
diff --git a/src/jcgp/backend/tests/InputTests.java b/src/jcgp/backend/tests/InputTests.java
new file mode 100644
index 0000000..4d15667
--- /dev/null
+++ b/src/jcgp/backend/tests/InputTests.java
@@ -0,0 +1,47 @@
+package jcgp.backend.tests;
+
+import static org.junit.Assert.assertTrue;
+import jcgp.backend.population.Input;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ * Tests which cover the behaviour specified for an input.
+ *
+ * - An input contains a single set value. This value can be freely set for
+ * fitness evaluation purposes.
+ * - It must be addressable by an index set upon construction only.
+ *
+ *
+ * @author Eduardo Pedroni
+ *
+ */
+public class InputTests {
+
+ private Input input;
+ // these are the test values
+ private final int inputValue = 19;
+ private final int inputIndex = 12;
+
+ @Before
+ public void setUp() throws Exception {
+ input = new Input(inputIndex);
+ }
+
+ @Test
+ public void valueTest() {
+ // assign a value to input, check that the returned value is correct
+ input.setValue(inputValue);
+
+ assertTrue("Incorrect value returned.", ((Integer) input.getValue()) == inputValue);
+ }
+
+ @Test
+ public void indexTest() {
+ // check that the index returned is the one passed to the constructor
+ assertTrue("Incorrect index returned.", input.getIndex() == inputIndex);
+ }
+
+}
diff --git a/src/jcgp/backend/tests/NodeTests.java b/src/jcgp/backend/tests/NodeTests.java
new file mode 100644
index 0000000..7121e81
--- /dev/null
+++ b/src/jcgp/backend/tests/NodeTests.java
@@ -0,0 +1,196 @@
+package jcgp.backend.tests;
+
+import static org.junit.Assert.assertTrue;
+import jcgp.JCGP.Resources;
+import jcgp.backend.function.Arithmetic;
+import jcgp.backend.function.Function;
+import jcgp.backend.population.Chromosome;
+import jcgp.backend.population.Connection;
+import jcgp.backend.population.Node;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ *
+ * Tests which cover the behaviour specified for a node.
+ *
+ * - A node should contain read-only row and column values which are set upon construction.
+ * - It should contain a fully accessible Function object.
+ * - It should contain a set of connections which can be initialised and randomly
+ * modified. It should be able to return an addressed connection.
+ * - It should be able to compute a value using its function with its connections as arguments.
+ *
+ * WARNING: changing parameters may cause the tests to incorrectly fail!
+ *
+ * @author Eduardo Pedroni
+ *
+ */
+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() {
+
+ resources = new Resources();
+
+ chromosome = new Chromosome(resources);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ node = new Node(chromosome, 0, 0, resources.getInt("arity"));
+ // make node with anonymous addition function and hard-coded value connections
+ node.initialise(new Arithmetic.Addition(),
+ new Connection[]{new Connection() {
+
+ @Override
+ public Object getValue() {
+ // hardcode a value
+ return arg1;
+ }
+
+ @Override
+ public String getDescription() {
+ // blank
+ return null;
+ }
+
+ },
+ new Connection() {
+
+ @Override
+ public Object getValue() {
+ // hardcode a value
+ return arg2;
+ }
+
+ @Override
+ public String getDescription() {
+ // blank
+ return null;
+ }
+
+ }});
+ }
+
+ @Test
+ public void rowAndColumnTest() {
+ assertTrue("Incorrect row.", node.getRow() == 0);
+ assertTrue("Incorrect column.", node.getColumn() == 0);
+ }
+
+ @Test
+ public void functionTest() {
+ // make a new function and assign to node
+ Function f = new Function() {
+
+ @Override
+ public Object run(Connection... connections) {
+ // blank
+ return 0;
+ }
+
+ @Override
+ public int getArity() {
+ // blank
+ return 0;
+ }
+
+ @Override
+ public String getName() {
+ // blank
+ return null;
+ }
+ };
+
+ node.setFunction(f);
+
+ // check that the function returned by the node is f
+ assertTrue("Incorrect function returned.", node.getFunction() == f);
+ // check that it outputs 0 as it should
+ assertTrue("Incorrect function output.", ((Integer) node.getValue()) == 0);
+ }
+
+ @Test
+ public void evaluationTest() {
+ // check that addition is working
+ assertTrue("Node did not return expected value (sum of arguments). Output was: " + ((int) node.getValue()),
+ ((int) node.getValue()) == arg1 + arg2);
+
+ // put in a different function, check the output has changed appropriately
+ node.setFunction(new Arithmetic.Subtraction());
+
+ assertTrue("Node did not return expected value (difference of arguments).", ((Integer) node.getValue()) == arg1 - arg2);
+
+ }
+
+ @Test
+ public void connectionsTest() {
+ // make new blank connections, check that they are returned correctly when addressed
+ Connection conn0, conn1, conn2;
+ conn0 = new Connection() {
+
+ @Override
+ public Object getValue() {
+ // blank
+ return 0;
+ }
+
+ @Override
+ public String getDescription() {
+ // blank
+ return null;
+ }
+
+ };
+ conn1 = new Connection() {
+
+ @Override
+ public Object getValue() {
+ // blank
+ return 0;
+ }
+
+ @Override
+ public String getDescription() {
+ // blank
+ return null;
+ }
+
+ };
+ node.initialise(null, conn0, conn1);
+
+ assertTrue("Connection 0 is incorrect.", node.getConnection(0) == conn0);
+ assertTrue("Connection 1 is incorrect.", node.getConnection(1) == conn1);
+
+ // make yet another connection, set it randomly, check that it is one of the node's connections
+ conn2 = new Connection() {
+
+ @Override
+ public Object getValue() {
+ // blank
+ return 0;
+ }
+
+ @Override
+ public String getDescription() {
+ // blank
+ return null;
+ }
+ };
+ node.setConnection(resources.getRandomInt(resources.getInt("arity")), conn2);
+
+ assertTrue("Connection was not found in node.", node.getConnection(0) == conn2 || node.getConnection(1) == conn2);
+
+ }
+
+
+}
diff --git a/src/jcgp/backend/tests/OutputTests.java b/src/jcgp/backend/tests/OutputTests.java
new file mode 100644
index 0000000..7ff8a4a
--- /dev/null
+++ b/src/jcgp/backend/tests/OutputTests.java
@@ -0,0 +1,93 @@
+package jcgp.backend.tests;
+
+import static org.junit.Assert.assertTrue;
+import jcgp.JCGP.Resources;
+import jcgp.backend.population.Chromosome;
+import jcgp.backend.population.Connection;
+import jcgp.backend.population.Output;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ *
+ * Tests which cover the behaviour specified for an output.
+ *
+ * - An output contains a single source Connection, which can be set and got.
+ * - It should return the value of its source connection.
+ * - It must be addressable by an index set upon construction only.
+ *
+ *
+ * @author Eduardo Pedroni
+ *
+ */
+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() {
+ resources = new Resources();
+ chromosome = new Chromosome(resources);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ output = new Output(chromosome, outputIndex);
+ }
+
+ @Test
+ public void evaluationsTest() {
+ // set source connection, check that the appropriate value is returned
+ output.setConnection(0, new Connection() {
+
+ @Override
+ public Object getValue() {
+ // test value
+ return outputValue;
+ }
+
+ @Override
+ public String getDescription() {
+ // blank
+ return null;
+ }
+ });
+
+ assertTrue("Incorrect evaluation.", ((Integer) output.calculate()) == outputValue);
+ }
+
+ @Test
+ public void connectionTest() {
+ // set a new connection, check that it is correctly returned
+ Connection newConn = new Connection() {
+
+ @Override
+ public Object getValue() {
+ // blank
+ return 0;
+ }
+
+ @Override
+ public String getDescription() {
+ // blank
+ return null;
+ }
+ };
+ output.setConnection(0, newConn);
+
+ assertTrue("Incorrect connection returned.", output.getSource() == newConn);
+ }
+
+ @Test
+ public void indexTest() {
+ // check that the index returned is the one passed to the constructor
+ assertTrue("Incorrect index returned.", output.getIndex() == outputIndex);
+ }
+}
diff --git a/src/jcgp/backend/tests/PopulationTests.java b/src/jcgp/backend/tests/PopulationTests.java
new file mode 100644
index 0000000..51b5168
--- /dev/null
+++ b/src/jcgp/backend/tests/PopulationTests.java
@@ -0,0 +1,86 @@
+package jcgp.backend.tests;
+
+import static org.junit.Assert.assertTrue;
+import jcgp.JCGP.Resources;
+import jcgp.backend.population.Chromosome;
+import jcgp.backend.population.Population;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ *
+ * Tests which cover the behaviour specified for a population.
+ *
+ * - 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
+ * as parents to create the rest of the population.
+ *
+ *
+ * @author Eduardo Pedroni
+ *
+ */
+public class PopulationTests {
+
+ private Population population;
+ private static Resources resources;
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ 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(resources);
+ }
+
+ @Test
+ public void defaultPopulationTest() {
+ // check that the constructor really generates populationSize chromosomes when none is given
+ int chromosomes = 0;
+ while (true) {
+ try {
+ population.getChromosome(chromosomes);
+ } catch (IndexOutOfBoundsException e) {
+ break;
+ }
+ chromosomes++;
+ }
+
+ assertTrue("Incorrect number of chromosomes generated.", chromosomes == resources.getInt("popSize"));
+ }
+
+ @Test
+ public void preinitialisedChromosomeTest() {
+ // the original chromosome that will be cloned
+ Chromosome oc = new Chromosome(resources);
+
+ // initialise a population with a copy of it
+ population = new Population(oc, resources);
+ // check that the first parent chromosome is identical to, but not the same instance as, the one given
+ assertTrue("Incorrect chromosome in population.", population.getChromosome(0).compareTo(oc) && population.getChromosome(0) != oc);
+ }
+}