aboutsummaryrefslogtreecommitdiffstats
path: root/src/jcgp/backend/population/Chromosome.java
diff options
context:
space:
mode:
authorEduardo Pedroni <ep625@york.ac.uk>2014-04-01 23:00:53 +0100
committerEduardo Pedroni <ep625@york.ac.uk>2014-04-01 23:00:53 +0100
commit02fd2bc7059da416937beb1abe67e5ca60379030 (patch)
tree609341fe10aaa0f2dc45a1e72eba20bd24fb1281 /src/jcgp/backend/population/Chromosome.java
parenta757deacded0d7357a9f68462d3f2051e16004ee (diff)
Settings pane now actually controls the parameters, not much left to do.
Diffstat (limited to 'src/jcgp/backend/population/Chromosome.java')
-rw-r--r--src/jcgp/backend/population/Chromosome.java335
1 files changed, 335 insertions, 0 deletions
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;
+ }
+}