aboutsummaryrefslogtreecommitdiffstats
path: root/src/jcgp/backend
diff options
context:
space:
mode:
authorEduardo Pedroni <ep625@york.ac.uk>2014-05-04 19:23:52 +0100
committerEduardo Pedroni <ep625@york.ac.uk>2014-05-04 19:23:52 +0100
commitaa9e74e7f67789f6353fc26e02ee8e68e40609a2 (patch)
tree3ad4ed8a0717a9983775a38b0cc8d9a10c01302a /src/jcgp/backend
parentc4fc7e307caf03c93c4203aff8960ffcb3ca8737 (diff)
Added more comments, minor refactorings
Diffstat (limited to 'src/jcgp/backend')
-rw-r--r--src/jcgp/backend/population/Chromosome.java192
-rw-r--r--src/jcgp/backend/population/Connection.java23
-rw-r--r--src/jcgp/backend/population/Gene.java5
-rw-r--r--src/jcgp/backend/population/Input.java36
-rw-r--r--src/jcgp/backend/population/MutableElement.java53
-rw-r--r--src/jcgp/backend/population/Node.java125
-rw-r--r--src/jcgp/backend/population/Output.java49
-rw-r--r--src/jcgp/backend/population/Population.java67
-rw-r--r--src/jcgp/backend/resources/Resources.java17
-rw-r--r--src/jcgp/backend/statistics/StatisticsLogger.java5
10 files changed, 467 insertions, 105 deletions
diff --git a/src/jcgp/backend/population/Chromosome.java b/src/jcgp/backend/population/Chromosome.java
index d23f43c..b99b817 100644
--- a/src/jcgp/backend/population/Chromosome.java
+++ b/src/jcgp/backend/population/Chromosome.java
@@ -4,6 +4,54 @@ import java.util.ArrayList;
import jcgp.backend.resources.Resources;
+/**
+ * This class encapsulates a CGP chromosome.
+ * <br><br>
+ * A chromosome contains a matrix of nodes and arrays of inputs and outputs.
+ * These elements are all interconnected, and actually form the chromosome
+ * network itself. Individual nodes can be retrieved using {@code getNode()}
+ * which requires the row and column to be specified. The same works for
+ * inputs and outputs using the associated getters, in which case only the
+ * index is necessary.
+ * <br><br>
+ * In evolutionary computation it is often necessary to make copies of
+ * chromosomes; this can be accomplished in JCGP in two ways. The recommended
+ * way to do this is using {@code copyChromosome()} in {@code Population}, but alternatively
+ * it can be done by using the {@code Chromosome} copy constructor and specifying the
+ * object to copy from, or by using the {@code copyGenes()} method.
+ * <br><br>
+ * To illustrate this, given two chromosomes, chr1 and chr2, the following code:
+ * <br><br>
+ * <code>
+ * chr1.copyGenes(chr2);
+ * </code><br><br>
+ * will modify all of chr1's connections and functions to match those of chr2, without
+ * creating a new instance. In contrast,
+ * <br><br>
+ * <code>
+ * chr1 = new Chromosome(chr2);
+ * </code><br><br>
+ * creates a new instance of chromosome which is identical to chr2 and assigns it to chr1,
+ * meaning any old references to chr1 that are not updated will still refer to a chromosome
+ * that is not identical to chr2. In practice, the most reliable way is to use the copy method
+ * in {@code Population}. Assuming chr1 and chr2 are indexed 1 and 2 in {@code population} respectively,
+ * <br><br>
+ * population.copyChromosome(2, 1);
+ * <br><br>
+ * will copy chr2 into chr1 without creating new instances or requiring access to the underlying
+ * chromosome array. {@code Chromosome} offers a variety of methods to compare chromosomes as well,
+ * such as {@code compareGenesTo()} and {@code compareActiveGenesTo()}. {@code Comparable} is implemented
+ * to compare fitness value, meaning {@code compareTo()} returns a value depending the relative fitness
+ * of the compared chromosomes.
+ * <br><br>
+ * In order to set the chromosome's input values for decoding, {@code setInputs()} should be used. A few
+ * utility methods are provided in order to retrieve random elements from the chromosome, which are used
+ * internally to initialise with random connections but also externally by mutators when performing
+ * mutations.
+ *
+ * @author Eduardo Pedroni
+ *
+ */
public class Chromosome implements Comparable<Chromosome> {
private Resources resources;
@@ -19,14 +67,13 @@ public class Chromosome implements Comparable<Chromosome> {
/**
* Initialise a chromosome with the specified parameters. Random valid connections
- * are created.
- *
- *
+ * are created upon initialisation.
+ *
+ * @param resources the experiment's resources.
*/
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
@@ -38,12 +85,11 @@ public class Chromosome implements Comparable<Chromosome> {
*
* Initialise a new chromosome with the exact same connections as a given instance of Chromosome.
*
- * @param clone the chromosome to be copied
+ * @param clone the chromosome to be copied.
*/
public Chromosome(Chromosome clone) {
// store a reference to the parameters
this.resources = clone.getResources();
-
// allocate memory for all elements of the chromosome
instantiateElements();
// initialise all connections based on argument
@@ -51,7 +97,14 @@ public class Chromosome implements Comparable<Chromosome> {
}
/**
- *
+ * Allocates the necessary memory for all of the nodes, inputs
+ * and outputs in the chromosome according to the experiment
+ * resources.
+ * <br>
+ * Note that this does not actually initialise the genes, as it
+ * is not possible to do so until they have all been initialised;
+ * to initialise the genes, {@code reinitialiseConnections()} should
+ * be used.
*/
private void instantiateElements() {
inputs = new Input[(resources.inputs())];
@@ -75,7 +128,9 @@ public class Chromosome implements Comparable<Chromosome> {
}
/**
- *
+ * Sets random connections and functions across the entire
+ * chromosome. This method can be used more than once for
+ * each instance, if entirely random chromosomes are desired.
*/
public void reinitialiseConnections() {
@@ -92,6 +147,7 @@ public class Chromosome implements Comparable<Chromosome> {
}
}
+ // set random outputs
for (Output output : outputs) {
output.setConnection(0, getRandomConnection());
}
@@ -99,11 +155,21 @@ public class Chromosome implements Comparable<Chromosome> {
}
/**
- * @param clone
+ * Creates a deep copy of the specified chromosome in the
+ * this instance. In practice, this iterates through the
+ * entire chromosome making equivalent connections and
+ * setting functions to the same values as those in the
+ * specified chromosome.
+ * <br>
+ * It is assumed that both chromosomes have the same
+ * topology; while this method will still run if that is not
+ * the case, the effects might be undesirable and null pointer
+ * access might occur.
+ *
+ * @param clone the chromosome to clone.
*/
public void copyGenes(Chromosome clone) {
int arity = resources.arity();
-
// copy nodes - [rows][columns]
for (int r = 0; r < nodes.length; r++) {
for (int c = 0; c < nodes[r].length; c++) {
@@ -141,31 +207,64 @@ public class Chromosome implements Comparable<Chromosome> {
}
}
+ /**
+ * Returns a reference to any node, addressed by row and column.
+ *
+ * @param row the row of the node.
+ * @param column the column of the node.
+ * @return the addressed node.
+ */
public Node getNode(int row, int column) {
return nodes[row][column];
}
+ /**
+ * Returns a reference to the indexed output.
+ *
+ * @param index the output index.
+ * @return the output reference.
+ */
public Output getOutput(int index) {
return outputs[index];
}
+ /**
+ * Returns a reference to the indexed input.
+ *
+ * @param index the input index.
+ * @return the input reference.
+ */
public Input getInput(int index) {
return inputs[index];
}
+ /**
+ * @return the fitness of the chromosome.
+ */
public double getFitness() {
return fitness;
}
+ /**
+ * Sets the fitness of the chromosome. This method
+ * should be used by the experiment problem when the
+ * population is evaluated in order to assign a fitness
+ * to each individual.
+ *
+ * @param newFitness the fitness to assign.
+ */
public void setFitness(double newFitness) {
fitness = newFitness;
}
/**
+ * Loops through the inputs and sets the specified values,
+ * so that evaluations can be performed. If the number of
+ * elements in the array of values does not match the
+ * number of inputs exactly, an exception is thrown.
*
- *
- * @param values
- * @throws ParameterMismatchException
+ * @param values the values the input should take.
+ * @throws ParameterMismatchException if the wrong number of values is received.
*/
public void setInputs(Object ... values) {
// if the values provided don't match the specified number of inputs, the user should be warned
@@ -180,9 +279,11 @@ public class Chromosome implements Comparable<Chromosome> {
}
/**
- * This method is useful for mutating chromosomes.
+ * This method is useful for mutating chromosomes. It returns any
+ * random {@code MutableElement} out of the chromosome with equal
+ * probability.
*
- * @return a random element that can be mutated - Node or Output
+ * @return a random element that can be mutated - node or output.
*/
public MutableElement getRandomMutableElement() {
// choose output or node
@@ -199,12 +300,12 @@ public class Chromosome implements Comparable<Chromosome> {
}
/**
- * Returns a random allowed connection respecting levels back.</br>
+ * 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
+ * @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
@@ -226,12 +327,9 @@ public class Chromosome implements Comparable<Chromosome> {
/**
* This method will pick a completely random connection, independently
- * of levels back, including inputs.
- *
- * Useful for setting outputs.
+ * of levels back, including inputs. It is useful for setting outputs.
*
- *
- * @return a random connection
+ * @return a random connection regardless of levels back.
*/
public Connection getRandomConnection() {
// choose output or node
@@ -254,27 +352,54 @@ public class Chromosome implements Comparable<Chromosome> {
}
/**
- * This method computes a list of active connections (if necessary) and returns it.
+ * This method computes a list of active nodes (if necessary) and returns it.
*
- * @return
+ * @return the list of active nodes.
*/
public ArrayList<Node> getActiveNodes() {
computeActiveNodes();
return activeNodes;
}
+ /**
+ * For internal use only, this method actually computes the list of active nodes
+ * from the chromosome. This is done recursively by calling {@code getActive()}
+ * on the nodes until the first node returns.
+ */
private void computeActiveNodes() {
// lazy recomputation has been triggered, do it
if (recomputeActiveNodes) {
recomputeActiveNodes = false;
activeNodes = new ArrayList<Node>();
-
+ // recursive operation
for (Output output : outputs) {
output.getActiveNodes(activeNodes);
}
}
}
+ /**
+ * Performs a deep comparison between this chromosome and the provided one.
+ * This is done on a gene-by-gene basis.
+ *
+ * This method returns true if and only if:
+ * <ul>
+ * <li>the chromosomes being compared are not the same instance;</li>
+ * <li>the connections of the compared chromosomes are not the same instance;</li>
+ * <li>the grid position of the chromosome's elements are the same;</li>
+ * </ul>
+ * <br><br>
+ * The relationship computed by this method is:
+ * <ul>
+ * <li>symmetric: a.copyOf(b) == b.copyOf(a);</li>
+ * <li>not reflexive: a.copyOf(a) returns false;</li>
+ * <li>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.</li>
+ * </ul>
+ * @param chromosome the chromosome to compare to.
+ * @return true if it is a copy of this chromosome, but not the same chromosome.
+ *
+ */
public boolean compareGenesTo(Chromosome chromosome) {
for (int r = 0; r < resources.rows(); r++) {
for (int c = 0; c < resources.columns(); c++) {
@@ -293,6 +418,13 @@ public class Chromosome implements Comparable<Chromosome> {
return true;
}
+ /**
+ * Does the same as {@code compareGenesto()} but only looks
+ * at the active portion of the chromosome.
+ *
+ * @param chromosome the chromosome to compare to.
+ * @return true if the two active portions are identical.
+ */
public boolean compareActiveGenesTo(Chromosome chromosome) {
// update list if it is out of date
computeActiveNodes();
@@ -308,6 +440,11 @@ public class Chromosome implements Comparable<Chromosome> {
return false;
}
+ /**
+ * Iterates through the nodes and prints all connections and functions.
+ * This is intended for debugging purposes only and does not print to the
+ * GUI console.
+ */
public void printNodes() {
int arity = resources.arity();
@@ -330,6 +467,9 @@ public class Chromosome implements Comparable<Chromosome> {
System.out.println();
}
+ /**
+ * @return a reference to the resources based on which the chromosome was built.
+ */
public Resources getResources() {
return resources;
}
diff --git a/src/jcgp/backend/population/Connection.java b/src/jcgp/backend/population/Connection.java
index cd0f5ce..f0be590 100644
--- a/src/jcgp/backend/population/Connection.java
+++ b/src/jcgp/backend/population/Connection.java
@@ -1,7 +1,30 @@
package jcgp.backend.population;
+/**
+ * {@code Connection} declares the expected behaviour of any
+ * part of a chromosome that can be connected to, specifically
+ * nodes or inputs. Outputs are not connections since they
+ * mark the end of chromosome paths.
+ * <br><br>
+ * This interface provides a way to deal with connections
+ * generically without having to specify whether they are nodes
+ * or inputs. In this way a random connection can be picked and
+ * dealt with more easily, facilitating mutations.
+ *
+ * @author Eduardo Pedroni
+ *
+ */
public interface Connection {
+ /**
+ * Compute and return the value of this connection. In
+ * the case of inputs no computation is necessary, this
+ * simply returns the value the input is set to. In the
+ * case of nodes, the value is computed based on the
+ * node's function and the value of its own connections.
+ *
+ * @return the connection's value.
+ */
public Object getValue();
}
diff --git a/src/jcgp/backend/population/Gene.java b/src/jcgp/backend/population/Gene.java
deleted file mode 100644
index 6b90949..0000000
--- a/src/jcgp/backend/population/Gene.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package jcgp.backend.population;
-
-public abstract class Gene {
-
-}
diff --git a/src/jcgp/backend/population/Input.java b/src/jcgp/backend/population/Input.java
index 2661f8c..97fe82b 100644
--- a/src/jcgp/backend/population/Input.java
+++ b/src/jcgp/backend/population/Input.java
@@ -1,27 +1,51 @@
package jcgp.backend.population;
-public class Input extends Gene implements Connection {
+/**
+ * This is a chromosome input. Inputs are a special
+ * type of connection which simply return a set value.
+ * They do not have connections and instead provide a
+ * starting point for the chromosome's active paths.
+ *
+ * @author Eduardo Pedroni
+ *
+ */
+public class Input implements Connection {
- private Object value = 0;
+ private Object value;
private int index;
+ /**
+ * Initialises a new input with the current index.
+ *
+ * @param index the index of the new input.
+ */
public Input(int index) {
this.index = index;
}
+ /**
+ * Sets this input's value. The new value
+ * will now be returned by this input's
+ * {@code getValue()} method.
+ *
+ * @param newValue the value to set.
+ */
public void setValue(Object newValue) {
value = newValue;
}
+ /**
+ * @return the input's index.
+ */
+ public int getIndex() {
+ return index;
+ }
+
@Override
public Object getValue() {
return value;
}
- public int getIndex() {
- return index;
- }
-
@Override
public String toString() {
return "Input " + index;
diff --git a/src/jcgp/backend/population/MutableElement.java b/src/jcgp/backend/population/MutableElement.java
index 8548e63..33f3890 100644
--- a/src/jcgp/backend/population/MutableElement.java
+++ b/src/jcgp/backend/population/MutableElement.java
@@ -1,5 +1,19 @@
package jcgp.backend.population;
+/**
+ * {@code MutableElement} declares the expected behaviour of any
+ * part of a chromosome that is mutable, more specifically
+ * nodes or outputs. Inputs are not mutable since they don't have
+ * connections or functions.
+ * <br><br>
+ * This interface provides a way to deal with mutable elements
+ * generically without having to specify whether they are nodes
+ * or outputs. In this way a random mutable element can be picked and
+ * dealt with more easily, facilitating mutations.
+ *
+ * @author Eduardo Pedroni
+ *
+ */
public interface MutableElement {
/**
@@ -7,28 +21,33 @@ public interface MutableElement {
* 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
+ * @param index the connection index to set.
+ * @param newConnection the chromosome element to connect to.
*/
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>
+ * Asserts if the specified element is a copy of the elements
+ * this is called on.<br>
+ * This method returns true if and only if:
+ * <ul>
+ * <li>the elements being compared are not the same instance;</li>
+ * <li>the connections of the compared elements are not the same instance;</li>
+ * <li>the elements have the same function (in the case of Node);</li>
+ * <li>the grid position of the elements themselves are the same;</li>
+ * <li>the grid position of all equivalent connections are the same;</li>
+ * </ul>
+ * <br><br>
+ * The relationship computed by this method is:
+ * <ul>
+ * <li>symmetric: a.copyOf(b) == b.copyOf(a);</li>
+ * <li>not reflexive: a.copyOf(a) returns false;</li>
+ * <li>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.</li>
*
- * 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
+ * @param element the mutable element to compare to.
+ * @return true if {@code element} is a copy of this element.
*/
- boolean copyOf(MutableElement m);
+ boolean copyOf(MutableElement element);
}
diff --git a/src/jcgp/backend/population/Node.java b/src/jcgp/backend/population/Node.java
index 74f6b54..704b24e 100644
--- a/src/jcgp/backend/population/Node.java
+++ b/src/jcgp/backend/population/Node.java
@@ -4,41 +4,63 @@ import java.util.ArrayList;
import jcgp.backend.function.Function;
-
-public class Node extends Gene implements MutableElement, Connection {
+/**
+ * Nodes make up the main part of the chromosome,
+ * where the actual functions are evolved. Each node
+ * contains a function and a number of connections.
+ * The node outputs the result of performing its function
+ * on the values of its connections. Nodes therefore
+ * implement both {@code MutableElement} and {@code Connection}
+ * since they can be mutated but also connected to.
+ * Nodes are constructed with a fixed number of connections
+ * (determined by the maximum arity of the function set)
+ * and must be reinstantiated if the experiment arity
+ * changes.
+ *
+ * @author Eduardo Pedroni
+ *
+ */
+public class Node implements MutableElement, Connection {
private Function function;
private Connection[] connections;
private int column, row;
private Chromosome chromosome;
+ /**
+ * Constructs a new instance of {@code Node} with the
+ * specified parameters. Nodes must contain their
+ * own row and column for ease of copying.
+ *
+ * @param chromosome the chromosome this node belongs to.
+ * @param row the node's row.
+ * @param column the node's column.
+ * @param arity the maximum arity of the experiment.
+ */
public Node(Chromosome chromosome, int row, int column, int arity) {
this.chromosome = chromosome;
this.column = column;
this.row = row;
}
-
- @Override
- public Object getValue() {
- Object[] args = new Object[function.getArity()];
- for (int i = 0; i < function.getArity(); i++) {
- args[i] = connections[i].getValue();
- }
- return function.run(args);
- }
-
+
+ /**
+ * Sets the node function.
+ *
+ * @param newFunction the new function to set.
+ */
public void setFunction(Function newFunction) {
function = newFunction;
}
- @Override
- public void setConnection(int index, Connection newConnection) {
- if (newConnection != null) {
- connections[index] = newConnection;
- chromosome.recomputeActiveNodes();
- }
- }
-
+ /**
+ * Initialises the node with the specified values.
+ * The number of connections passed as argument must
+ * be exactly the same as the experiment arity, or
+ * an {@code IllegalArgumentException} will be thrown.
+ *
+ * @param newFunction the node function to set.
+ * @param newConnections the node connections to set.
+ */
public void initialise(Function newFunction, Connection ... newConnections) {
function = newFunction;
if (newConnections.length == chromosome.getResources().arity()) {
@@ -48,41 +70,71 @@ public class Node extends Gene implements MutableElement, Connection {
}
}
+ /**
+ * @return this node's column.
+ */
public int getColumn() {
return column;
}
+ /**
+ * @return this node's row.
+ */
public int getRow() {
return row;
}
+ /**
+ * @return this node's function.
+ */
public Function getFunction() {
return function;
}
+ /**
+ * @param index the connection to return.
+ * @return the indexed connection.
+ */
public Connection getConnection(int index) {
return connections[index];
}
- public void getActive(ArrayList<Node> activeNodes) {
+ /**
+ * For package use, this is a recursive method
+ * used to create a collection of active nodes
+ * in the chromosome. If this node is not already
+ * in the active node list, this method adds it.
+ * It then calls {@code getActive()} on each of its
+ * connections, therefore recursively adding every
+ * single active node to the given list.
+ *
+ * @param activeNodes the list of active nodes being built.
+ */
+ protected void getActive(ArrayList<Node> activeNodes) {
+ // don't add the same node twice
if (!activeNodes.contains(this)) {
activeNodes.add(this);
}
+ // call getActive on all connections - they are all active
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;
+ public boolean copyOf(MutableElement element) {
+ // both cannot be the same instance
+ if (this != element) {
+ // element must be instance of node
+ if (element instanceof Node) {
+ Node n = (Node) element;
+ // must have the same function
if (function == n.getFunction()) {
+ // row and column must be the same
if (column == n.getColumn() && row == n.getRow()) {
+ // connections must be the equivalent, but not the same instance
for (int i = 0; i < connections.length; i++) {
if (connections[i] != n.getConnection(i)) {
if (connections[i] instanceof Input && n.getConnection(i) instanceof Input) {
@@ -101,6 +153,7 @@ public class Node extends Gene implements MutableElement, Connection {
return false;
}
}
+ // all connections checked, this really is a copy
return true;
}
}
@@ -108,6 +161,26 @@ public class Node extends Gene implements MutableElement, Connection {
}
return false;
}
+
+ @Override
+ public Object getValue() {
+ // build list of arguments recursively
+ Object[] args = new Object[function.getArity()];
+ for (int i = 0; i < function.getArity(); i++) {
+ args[i] = connections[i].getValue();
+ }
+ // return function result
+ return function.run(args);
+ }
+
+ @Override
+ public void setConnection(int index, Connection newConnection) {
+ // connection must not be null
+ if (newConnection != null) {
+ connections[index] = newConnection;
+ chromosome.recomputeActiveNodes();
+ }
+ }
@Override
public String toString() {
diff --git a/src/jcgp/backend/population/Output.java b/src/jcgp/backend/population/Output.java
index d876951..ab693e2 100644
--- a/src/jcgp/backend/population/Output.java
+++ b/src/jcgp/backend/population/Output.java
@@ -2,26 +2,39 @@ package jcgp.backend.population;
import java.util.ArrayList;
-public class Output extends Gene implements MutableElement {
+/**
+ * This is a chromosome output. Outputs are a special
+ * type of mutable element with a single connection. It
+ * returns the value of its single connection, but it
+ * may not be connected to - it terminates a chromosome
+ * active connection path.
+ *
+ * @author Eduardo Pedroni
+ *
+ */
+public class Output implements MutableElement {
private Connection source;
private Chromosome chromosome;
private int index;
+ /**
+ * Makes a new instance of {@code Output} with the
+ * specified arguments.
+ *
+ * @param chromosome the chromosome this output belongs to.
+ * @param index the output index.
+ */
public Output(Chromosome chromosome, int index) {
this.chromosome = chromosome;
this.index = index;
}
+ /**
+ * @return the value of the output's source.
+ */
public Object calculate() {
- Object result = source.getValue();
- return result;
- }
-
- @Override
- public void setConnection(int index, Connection newConnection) {
- source = newConnection;
- chromosome.recomputeActiveNodes();
+ return source.getValue();
}
public int getIndex() {
@@ -38,12 +51,30 @@ public class Output extends Gene implements MutableElement {
}
}
+ /**
+ * When mutating an output, the index parameter
+ * is simply ignored and the output source is
+ * set.
+ *
+ * @see jcgp.backend.population.MutableElement#setConnection(int, jcgp.backend.population.Connection)
+ */
+ @Override
+ public void setConnection(int index, Connection newConnection) {
+ source = newConnection;
+ // trigger active path recomputation
+ chromosome.recomputeActiveNodes();
+ }
+
@Override
public boolean copyOf(MutableElement m) {
+ // both cannot be the same instance
if (this != m) {
+ // element must be instance of output
if (m instanceof Output) {
Output o = (Output) m;
+ // index must be the same
if (index == o.getIndex()) {
+ // source must be the same
if (source != o.getSource()) {
if (source instanceof Input && o.getSource() instanceof Input) {
if (((Input) source).getIndex() == ((Input) o.getSource()).getIndex()) {
diff --git a/src/jcgp/backend/population/Population.java b/src/jcgp/backend/population/Population.java
index d99c64e..5d549e9 100644
--- a/src/jcgp/backend/population/Population.java
+++ b/src/jcgp/backend/population/Population.java
@@ -5,6 +5,35 @@ import java.util.Collections;
import jcgp.backend.resources.Resources;
+/**
+ * This class primarily holds a collection of chromosomes. In addition,
+ * it provides a few utility methods for manipulating and copying
+ * chromosomes, useful for evolutionary strategies.
+ * <br><br>
+ * {@code copyChromosome()} is used to create copies of chromosomes,
+ * though it is also possible to create a new instance of population
+ * directly from a seed chromosome using the right constructor.
+ * <br><br>
+ * For convenience, a random chromosome can be retrieved using
+ * {@code getRandomChromosome()}, which is guaranteed to use the
+ * experiment's specified seed. If an entirely random population
+ * is needed, {@code reinitialise()} should be used to randomise
+ * all chromosomes without creating a new instance of {@code Population}.
+ * <br><br>
+ * By convention the population's chromosomes should always be sorted in
+ * order of fitness, from worst to best. This cannot be done automatically
+ * since a higher fitness value does not necessarily mean better fitness;
+ * some problem types might instead interpret fitness 0 as a perfect solution.
+ * Sorting the population is done easily using {@code sortAscending()} and
+ * {@code sortDescending()}, and should be done by the problem type as appropriate.
+ * It is <b>critical</b> to sort the population after it is evaluated as
+ * evolutionary strategies will obey the convention and assume the population
+ * is sorted in order of fitness.
+ *
+ *
+ * @author Eduardo Pedroni
+ *
+ */
public class Population {
private final Chromosome[] chromosomes;
@@ -14,7 +43,7 @@ public class Population {
* Initialise a random population according to the parameters specified
* in the resources.
*
- * @param resources the CGP resources
+ * @param resources the experiment's resources.
*/
public Population(Resources resources) {
this.resources = resources;
@@ -28,8 +57,8 @@ public class Population {
/**
* Initialise a population of copies of the given chromosome.
*
- * @param parent
- * @param resources
+ * @param parent the chromosome to use as a model.
+ * @param resources a reference to the experiment's resources.
*/
public Population(Chromosome parent, Resources resources) {
this.resources = resources;
@@ -43,8 +72,8 @@ public class Population {
/**
* Returns the indexed chromosome.
*
- * @param index
- * @return
+ * @param index the chromosome to return.
+ * @return the indexed chromosome.
*/
public Chromosome getChromosome(int index) {
return chromosomes[index];
@@ -65,8 +94,8 @@ public class Population {
*
* This method does nothing if source == target.
*
- * @param source
- * @param target
+ * @param source the chromosome to copy from.
+ * @param target the chromosome to copy to.
*/
public void copyChromosome(int source, int target) {
if (source != target) {
@@ -74,16 +103,40 @@ public class Population {
}
}
+ /**
+ * Loop through all chromosomes and randomise all connections
+ * and functions.
+ */
public void reinitialise() {
for (int c = 0; c < chromosomes.length; c++) {
chromosomes[c].reinitialiseConnections();
}
}
+ /**
+ * The fittest chromosome, by convention, is the last one
+ * in the array. Problem evaluation methods are expected to
+ * sort the population into ascending order of fitness, such
+ * that the best chromosome is in the last position (size - 1).
+ * This method assumes that the population is sorted as such
+ * and returns the last chromosome in the array.
+ *
+ * @return the fittest chromosome in the population.
+ */
public Chromosome getFittest() {
return chromosomes[chromosomes.length - 1];
}
+ /**
+ * The fittest chromosome, by convention, is the last one
+ * in the array. Problem evaluation methods are expected to
+ * sort the population into ascending order of fitness, such
+ * that the best chromosome is in the last position (size - 1).
+ * This method assumes that the population is sorted as such
+ * and returns the last index in the array.
+ *
+ * @return the index of the fittest chromosome.
+ */
public int getFittestIndex() {
return chromosomes.length - 1;
}
diff --git a/src/jcgp/backend/resources/Resources.java b/src/jcgp/backend/resources/Resources.java
index 1f79627..ebd6fb2 100644
--- a/src/jcgp/backend/resources/Resources.java
+++ b/src/jcgp/backend/resources/Resources.java
@@ -8,21 +8,22 @@ import jcgp.backend.parameters.IntegerParameter;
/**
*
- * The resources class encapsulates all of the resources based on which the program operates.
- * Each instance of JCGP contains a single instance of Resources, which gets passed to the selected
- * modules as the program executes.
+ * Encapsulates all of the resources based on which the program operates.
+ * Each instance of JCGP contains a single instance of {@code Resources}.
* <br><br>
* The experiment's {@code Resources} object is passed to modules as the program operates, and
* the actual parameter values can be obtained using getter methods. Note that, for code brevity,
* this class's getters do not start with the word "get". For instance, to get the number of rows,
- * one would use {@code rows()} instead of {@code getRows()}.
+ * one would use {@code rows()} instead of {@code getRows()} which doesn't exist.
* <br><br>
* In addition to parameters, this class also offers utility methods. Any necessary random numbers
- * should be obtained using {@code getRandomInt()} and {@code getRandomDouble()}. Functions from the
- * selected function set can be obtained through this class as well. Finally, printing to the console
- * should be done via the resources using the report and print methods, so that these prints also
- * get sent to the GUI console (if one is present).
+ * should be obtained using {@code getRandomInt()} and {@code getRandomDouble()} as these methods
+ * use a particular {@code Random} object guaranteed to generate random numbers based on the seed
+ * parameter. Functions from the selected function set can be obtained through this class as well.
+ * Finally, printing to the console should be done via the resources using the report and print
+ * methods, so that these prints also get sent to the GUI console (if one is present).
*
+ * @see Parameter, Console, FunctionSet
* @author Eduardo Pedroni
*
*/
diff --git a/src/jcgp/backend/statistics/StatisticsLogger.java b/src/jcgp/backend/statistics/StatisticsLogger.java
index dfbcdbe..7165a11 100644
--- a/src/jcgp/backend/statistics/StatisticsLogger.java
+++ b/src/jcgp/backend/statistics/StatisticsLogger.java
@@ -173,6 +173,7 @@ public class StatisticsLogger {
public int getSuccessfulRuns() {
int count = 0;
for (RunEntry runEntry : runEntries) {
+ // only increment if solution was perfect
if (runEntry.isSuccessful()) {
count++;
}
@@ -189,7 +190,7 @@ public class StatisticsLogger {
* @return the success rate across all runs.
*/
public double getSuccessRate() {
- return getSuccessfulRuns() / runEntries.size();
+ return getSuccessfulRuns() / (double) runEntries.size();
}
/**
@@ -203,6 +204,7 @@ public class StatisticsLogger {
double average = 0;
int successfulRuns = getSuccessfulRuns();
for (RunEntry runEntry : runEntries) {
+ // only if solution was perfect
if (runEntry.isSuccessful()) {
average += runEntry.getGeneration() / successfulRuns;
}
@@ -221,6 +223,7 @@ public class StatisticsLogger {
double average = getAverageSuccessfulGenerations();
double temp, stdDev = 0;
for (RunEntry runEntry : runEntries) {
+ // only if solution was perfect
if (runEntry.isSuccessful()) {
temp = runEntry.getGeneration() - average;
temp = temp * temp;