From 02fd2bc7059da416937beb1abe67e5ca60379030 Mon Sep 17 00:00:00 2001
From: Eduardo Pedroni <ep625@york.ac.uk>
Date: Tue, 1 Apr 2014 23:00:53 +0100
Subject: Settings pane now actually controls the parameters, not much left to
 do.

---
 src/jcgp/backend/population/Chromosome.java     | 335 ++++++++++++++++++++++++
 src/jcgp/backend/population/Connection.java     |   9 +
 src/jcgp/backend/population/Gene.java           |   5 +
 src/jcgp/backend/population/Input.java          |  29 ++
 src/jcgp/backend/population/MutableElement.java |  34 +++
 src/jcgp/backend/population/Node.java           | 112 ++++++++
 src/jcgp/backend/population/Output.java         |  69 +++++
 src/jcgp/backend/population/Population.java     |  45 ++++
 8 files changed, 638 insertions(+)
 create mode 100644 src/jcgp/backend/population/Chromosome.java
 create mode 100644 src/jcgp/backend/population/Connection.java
 create mode 100644 src/jcgp/backend/population/Gene.java
 create mode 100644 src/jcgp/backend/population/Input.java
 create mode 100644 src/jcgp/backend/population/MutableElement.java
 create mode 100644 src/jcgp/backend/population/Node.java
 create mode 100644 src/jcgp/backend/population/Output.java
 create mode 100644 src/jcgp/backend/population/Population.java

(limited to 'src/jcgp/backend/population')

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];
+	}
+}
-- 
cgit v1.2.3