aboutsummaryrefslogtreecommitdiffstats
path: root/src/jcgp
diff options
context:
space:
mode:
Diffstat (limited to 'src/jcgp')
-rw-r--r--src/jcgp/population/Chromosome.java63
-rw-r--r--src/jcgp/population/Connection.java4
-rw-r--r--src/jcgp/population/Input.java9
-rw-r--r--src/jcgp/population/MutableElement.java18
-rw-r--r--src/jcgp/population/Node.java60
-rw-r--r--src/jcgp/population/Output.java30
-rw-r--r--src/jcgp/tests/ChromosomeTests.java113
-rw-r--r--src/jcgp/tests/NodeTests.java28
-rw-r--r--src/jcgp/tests/OutputTests.java11
9 files changed, 227 insertions, 109 deletions
diff --git a/src/jcgp/population/Chromosome.java b/src/jcgp/population/Chromosome.java
index 08ff9b9..f61b780 100644
--- a/src/jcgp/population/Chromosome.java
+++ b/src/jcgp/population/Chromosome.java
@@ -11,8 +11,8 @@ public class Chromosome {
private Input[] inputs;
private Node[][] nodes;
private Output[] outputs;
-
- private ArrayList<Connection> activeNodes;
+
+ private ArrayList<Node> activeNodes;
private int fitness = 0;
private boolean recomputeActiveNodes = true;
@@ -117,7 +117,7 @@ public class Chromosome {
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++) {
@@ -153,6 +153,11 @@ public class Chromosome {
fitness = newFitness;
}
+ /**
+ *
+ * @param values
+ * @throws ParameterMismatchException
+ */
public void setInputs(int ... 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) {
@@ -232,31 +237,67 @@ public class Chromosome {
return nodes[index / Parameters.getColumns()][index % Parameters.getColumns()];
}
}
-
+
/**
* 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 nodes (if necessary) and returns it.
+ * This method computes a list of active connections (if necessary) and returns it.
*
* @return
*/
- public ArrayList<Connection> getActiveNodes() {
+ public ArrayList<Node> getActiveNodes() {
+ computeActiveNodes();
+ return activeNodes;
+ }
+
+ private void computeActiveNodes() {
// lazy recomputation has been triggered, do it
if (recomputeActiveNodes) {
recomputeActiveNodes = false;
- activeNodes = new ArrayList<Connection>();
-
+ activeNodes = new ArrayList<Node>();
+
for (Output output : outputs) {
output.getActiveNodes(activeNodes);
}
-
}
- return activeNodes;
+
}
+ public boolean compareTo(Chromosome chromosome) {
+ for (int r = 0; r < Parameters.getRows(); r++) {
+ for (int c = 0; c < Parameters.getColumns(); c++) {
+ if (!(nodes[r][c].copyOf(chromosome.getNode(r, c)))) {
+ return false;
+ }
+ }
+ }
+
+ for (int o = 0; o < Parameters.getOutputs(); 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;
+ }
}
diff --git a/src/jcgp/population/Connection.java b/src/jcgp/population/Connection.java
index 12e92d6..3312de3 100644
--- a/src/jcgp/population/Connection.java
+++ b/src/jcgp/population/Connection.java
@@ -1,11 +1,7 @@
package jcgp.population;
-import java.util.ArrayList;
-
public interface Connection {
public int getValue();
- public void getActive(ArrayList<Connection> activeNodes);
-
}
diff --git a/src/jcgp/population/Input.java b/src/jcgp/population/Input.java
index f3199b8..ee008ce 100644
--- a/src/jcgp/population/Input.java
+++ b/src/jcgp/population/Input.java
@@ -1,7 +1,5 @@
package jcgp.population;
-import java.util.ArrayList;
-
public class Input implements Connection {
private int value = 0, index;
@@ -23,11 +21,4 @@ public class Input implements Connection {
return index;
}
- @Override
- public void getActive(ArrayList<Connection> activeNodes) {
- if (!activeNodes.contains(this)) {
- activeNodes.add(this);
- }
- }
-
}
diff --git a/src/jcgp/population/MutableElement.java b/src/jcgp/population/MutableElement.java
index 5eae4ef..114a4ab 100644
--- a/src/jcgp/population/MutableElement.java
+++ b/src/jcgp/population/MutableElement.java
@@ -3,5 +3,23 @@ package jcgp.population;
public interface MutableElement {
public void setConnection(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;</br>
+ *
+ * @param m
+ * @return
+ */
+ boolean copyOf(MutableElement m);
}
diff --git a/src/jcgp/population/Node.java b/src/jcgp/population/Node.java
index c09532c..b2e29df 100644
--- a/src/jcgp/population/Node.java
+++ b/src/jcgp/population/Node.java
@@ -1,6 +1,7 @@
package jcgp.population;
import java.util.ArrayList;
+import java.util.Arrays;
import jcgp.Parameters;
import jcgp.Utilities;
@@ -8,7 +9,7 @@ import jcgp.function.Function;
public class Node implements MutableElement, Connection {
-
+
private Function function;
private Connection[] connections;
private int column, row;
@@ -18,28 +19,29 @@ public class Node implements MutableElement, Connection {
this.chromosome = chromosome;
this.column = column;
this.row = row;
+ this.connections = new Connection[Parameters.getMaxArity()];
}
-
+
@Override
public int getValue() {
- return function.run(connections);
+ return function.run(Arrays.copyOfRange(connections, 0, function.getArity()));
}
-
+
public void setFunction(Function newFunction) {
function = newFunction;
chromosome.recomputeActiveNodes();
}
-
+
@Override
public void setConnection(Connection newConnection) {
connections[Utilities.getRandomInt(connections.length)] = newConnection;
chromosome.recomputeActiveNodes();
}
-
+
public void initialise(Function newFunction, Connection ... newConnections) throws InsufficientConnectionsException {
-
+
function = newFunction;
-
+
if (newConnections.length >= Parameters.getMaxArity()) {
connections = newConnections;
} else {
@@ -48,7 +50,6 @@ public class Node implements MutableElement, Connection {
}
public int getColumn() {
-
return column;
}
@@ -64,13 +65,48 @@ public class Node implements MutableElement, Connection {
return connections[index];
}
- @Override
- public void getActive(ArrayList<Connection> activeNodes) {
+ public void getActive(ArrayList<Node> activeNodes) {
if (!activeNodes.contains(this)) {
activeNodes.add(this);
}
for (int i = 0; i < function.getArity(); i++) {
- connections[i].getActive(activeNodes);
+ 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;
}
}
diff --git a/src/jcgp/population/Output.java b/src/jcgp/population/Output.java
index 0171d7b..f0bcbbf 100644
--- a/src/jcgp/population/Output.java
+++ b/src/jcgp/population/Output.java
@@ -31,7 +31,33 @@ public class Output implements MutableElement {
return source;
}
- public void getActiveNodes(ArrayList<Connection> activeNodes) {
- source.getActive(activeNodes);
+ 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/tests/ChromosomeTests.java b/src/jcgp/tests/ChromosomeTests.java
index ff1de18..d2407a6 100644
--- a/src/jcgp/tests/ChromosomeTests.java
+++ b/src/jcgp/tests/ChromosomeTests.java
@@ -36,6 +36,9 @@ import org.junit.Test;
* - It should feature a clone constructor, 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.
*
* TODO: bashing (strange value ranges, etc)
*
@@ -82,29 +85,21 @@ public class ChromosomeTests {
// create a clone, check to see if it really is a clone
Chromosome clone = new Chromosome(chromosome);
- // set input values
- testInputs = new int[Parameters.getInputs()];
- for (int i = 0; i < Parameters.getInputs(); i++) {
- testInputs[i] = i * 2 - 3;
- }
- chromosome.setInputs(testInputs);
- clone.setInputs(testInputs);
-
// compare all elements, one by one
// check outputs
for (int o = 0; o < Parameters.getOutputs(); o++) {
// check that no cross-references exist between chromosomes
- assertTrue("Cloned chromosome contained a reference to a member of the original chromosome.",
- c.getOutput(o) != oc.getOutput(o) &&
- c.getOutput(o).getSource() != oc.getOutput(o).getSource());
+ 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 (c.getOutput(o).getSource() instanceof Input && oc.getOutput(o).getSource() instanceof Input) {
+ if (clone.getOutput(o).getSource() instanceof Input && chromosome.getOutput(o).getSource() instanceof Input) {
assertTrue("Outputs did not connect to equivalent inputs.",
- ((Input) c.getOutput(o).getSource()).getIndex() == ((Input) oc.getOutput(o).getSource()).getIndex());
- } else if (c.getOutput(o).getSource() instanceof Node && oc.getOutput(o).getSource() instanceof Node) {
+ ((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) c.getOutput(o).getSource()).getRow() == ((Node) oc.getOutput(o).getSource()).getRow() &&
- ((Node) c.getOutput(o).getSource()).getColumn() == ((Node) oc.getOutput(o).getSource()).getColumn());
+ ((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.");
}
@@ -112,24 +107,37 @@ public class ChromosomeTests {
// check nodes, rows first
for (int row = 0; row < Parameters.getRows(); row++) {
for (int column = 0; column < Parameters.getColumns(); column++) {
- // look at each connection
+ // 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 < Parameters.getMaxArity(); connection++) {
- if (c.getNode(row, column).getConnection(connection) instanceof Input &&
- oc.getNode(row, column).getConnection(connection) instanceof Input) {
+ // 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) c.getNode(row, column).getConnection(connection)).getIndex() ==
- ((Input) oc.getNode(row, column).getConnection(connection)).getIndex());
+ ((Input) clone.getNode(row, column).getConnection(connection)).getIndex() ==
+ ((Input) chromosome.getNode(row, column).getConnection(connection)).getIndex());
- } else if (c.getNode(row, column).getConnection(connection) instanceof Node &&
- oc.getNode(row, column).getConnection(connection) instanceof Node) {
+ } 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) c.getNode(row, column).getConnection(connection)).getRow() ==
- ((Node) oc.getNode(row, column).getConnection(connection)).getRow() &&
+ ((Node) clone.getNode(row, column).getConnection(connection)).getRow() ==
+ ((Node) chromosome.getNode(row, column).getConnection(connection)).getRow() &&
- ((Node) c.getNode(row, column).getConnection(connection)).getColumn() ==
- ((Node) oc.getNode(row, column).getConnection(connection)).getColumn());
+ ((Node) clone.getNode(row, column).getConnection(connection)).getColumn() ==
+ ((Node) chromosome.getNode(row, column).getConnection(connection)).getColumn());
} else {
fail("Connection types did not match.");
@@ -138,17 +146,27 @@ public class ChromosomeTests {
}
}
-
- // change clone inputs, outputs should no longer match
+ // set input values
testInputs = new int[Parameters.getInputs()];
for (int i = 0; i < Parameters.getInputs(); i++) {
- testInputs[i] = i * 2;
+ testInputs[i] = (i + 1) * 2 - 3;
}
+ chromosome.setInputs(testInputs);
clone.setInputs(testInputs);
+
+ // check that both chromosomes have the same outputs
for (int i = 0; i < Parameters.getOutputs(); i++) {
- assertTrue("Incorrect output returned.", chromosome.getOutput(i).calculate() !=
+ assertTrue("Incorrect output returned.", chromosome.getOutput(i).calculate() ==
clone.getOutput(i).calculate());
}
+
+ // mutate an active node in clone, check that the same node in chromosome produces a different output
+ // NOTE: given a small grid this mutation may actually pick the same connection (causing no effective change) and therefore the test would fail.
+ Node node = clone.getActiveNodes().get(Utilities.getRandomInt(clone.getActiveNodes().size()));
+ node.setConnection(clone.getRandomConnection(node.getColumn()));
+
+ assertTrue("Mutation affected both nodes in both chromosomes.", node.getValue() != chromosome.getNode(node.getRow(), node.getColumn()).getValue());
+
}
/**
@@ -282,12 +300,41 @@ public class ChromosomeTests {
chromosome.getNode(0, 0).setConnection(chromosome.getInput(0));
- assertTrue("Active connection not in list.", chromosome.getActiveNodes().contains(chromosome.getInput(0)));
- assertTrue("Active connection not in list.", chromosome.getActiveNodes().contains(chromosome.getNode(0, 0)));
+ assertTrue("Active node not in list.", chromosome.getActiveNodes().contains(chromosome.getNode(0, 0)));
// change outputs, print list
chromosome.getOutput(0).setConnection(chromosome.getNode(0, Parameters.getColumns() - 1));
System.out.println("Active connections: " + chromosome.getActiveNodes().toString() + "\n");
}
+
+ /**
+ *
+ */
+ @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();
+ 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();
+ assertTrue("Chromosomes did match.", !chromosome.compareTo(c));
+ }
}
diff --git a/src/jcgp/tests/NodeTests.java b/src/jcgp/tests/NodeTests.java
index ecc87ff..5b378d2 100644
--- a/src/jcgp/tests/NodeTests.java
+++ b/src/jcgp/tests/NodeTests.java
@@ -2,7 +2,6 @@ package jcgp.tests;
import static org.junit.Assert.assertTrue;
-import java.util.ArrayList;
import java.util.Random;
import jcgp.Parameters;
@@ -92,11 +91,6 @@ public class NodeTests {
return arg1;
}
- @Override
- public void getActive(ArrayList<Connection> activeNodes) {
- // irrelevant for this test
- }
-
},
new Connection() {
@@ -106,11 +100,6 @@ public class NodeTests {
return arg2;
}
- @Override
- public void getActive(ArrayList<Connection> activeNodes) {
- // irrelevant for this test
- }
-
}});
}
@@ -189,11 +178,6 @@ public class NodeTests {
return 0;
}
- @Override
- public void getActive(ArrayList<Connection> activeNodes) {
- // blank
-
- }
};
conn1 = new Connection() {
@@ -203,11 +187,6 @@ public class NodeTests {
return 0;
}
- @Override
- public void getActive(ArrayList<Connection> activeNodes) {
- // blank
-
- }
};
node.initialise(null, conn0, conn1);
@@ -222,17 +201,12 @@ public class NodeTests {
// blank
return 0;
}
-
- @Override
- public void getActive(ArrayList<Connection> activeNodes) {
- // blank
-
- }
};
node.setConnection(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 06295ae..04eac7f 100644
--- a/src/jcgp/tests/OutputTests.java
+++ b/src/jcgp/tests/OutputTests.java
@@ -2,7 +2,6 @@ package jcgp.tests;
import static org.junit.Assert.assertTrue;
-import java.util.ArrayList;
import java.util.Random;
import jcgp.Parameters;
@@ -69,11 +68,6 @@ public class OutputTests {
// test value
return outputValue;
}
-
- @Override
- public void getActive(ArrayList<Connection> activeNodes) {
- // blank
- }
});
assertTrue("Incorrect evaluation.", output.calculate() == outputValue);
@@ -89,11 +83,6 @@ public class OutputTests {
// blank
return 0;
}
-
- @Override
- public void getActive(ArrayList<Connection> activeNodes) {
- // blank
- }
};
output.setConnection(newConn);