aboutsummaryrefslogtreecommitdiffstats
path: root/src/jcgp/backend/tests/ChromosomeTests.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jcgp/backend/tests/ChromosomeTests.java')
-rw-r--r--src/jcgp/backend/tests/ChromosomeTests.java334
1 files changed, 334 insertions, 0 deletions
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;
+ }
+}