From aa9e74e7f67789f6353fc26e02ee8e68e40609a2 Mon Sep 17 00:00:00 2001 From: Eduardo Pedroni Date: Sun, 4 May 2014 19:23:52 +0100 Subject: Added more comments, minor refactorings --- src/jcgp/JCGP.java | 2 +- src/jcgp/backend/population/Chromosome.java | 192 ++++++++++++++++++--- src/jcgp/backend/population/Connection.java | 23 +++ src/jcgp/backend/population/Gene.java | 5 - src/jcgp/backend/population/Input.java | 36 +++- src/jcgp/backend/population/MutableElement.java | 53 ++++-- src/jcgp/backend/population/Node.java | 125 +++++++++++--- src/jcgp/backend/population/Output.java | 49 +++++- src/jcgp/backend/population/Population.java | 67 ++++++- src/jcgp/backend/resources/Resources.java | 17 +- src/jcgp/backend/statistics/StatisticsLogger.java | 5 +- src/jcgp/gui/GUI.java | 9 +- src/jcgp/gui/dragresize/HorizontalDragResize.java | 3 +- src/jcgp/gui/dragresize/VerticalDragResize.java | 1 - src/jcgp/gui/population/GUIGene.java | 3 - src/jcgp/gui/population/GUIInput.java | 5 - src/jcgp/gui/population/GUINode.java | 12 +- src/jcgp/gui/population/GUIOutput.java | 5 - src/jcgp/gui/settings/SettingsPane.java | 4 +- .../settings/parameters/GUIBooleanParameter.java | 6 +- .../settings/parameters/GUIDoubleParameter.java | 11 +- .../settings/parameters/GUIIntegerParameter.java | 10 +- src/jcgp/gui/settings/parameters/GUIParameter.java | 49 +++--- 23 files changed, 522 insertions(+), 170 deletions(-) delete mode 100644 src/jcgp/backend/population/Gene.java diff --git a/src/jcgp/JCGP.java b/src/jcgp/JCGP.java index aa3b647..213dd06 100644 --- a/src/jcgp/JCGP.java +++ b/src/jcgp/JCGP.java @@ -477,13 +477,13 @@ public class JCGP { * */ public void reset() { - finished = false; statistics = new StatisticsLogger(); resources.setArity(problem.getFunctionSet().getMaxArity()); if (resources.arity() < 1) { resources.println("[CGP] Error: arity is smaller than 1. Check that at least one function is enabled"); return; } + finished = false; population = new Population(resources); resetStatisticsValues(); resources.setCurrentGeneration(1); 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. + *

+ * 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. + *

+ * 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. + *

+ * To illustrate this, given two chromosomes, chr1 and chr2, the following code: + *

+ * + * chr1.copyGenes(chr2); + *

+ * will modify all of chr1's connections and functions to match those of chr2, without + * creating a new instance. In contrast, + *

+ * + * chr1 = new Chromosome(chr2); + *

+ * 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, + *

+ * population.copyChromosome(2, 1); + *

+ * 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. + *

+ * 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 { private Resources resources; @@ -19,14 +67,13 @@ public class Chromosome implements Comparable { /** * 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 { * * 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 { } /** - * + * Allocates the necessary memory for all of the nodes, inputs + * and outputs in the chromosome according to the experiment + * resources. + *
+ * 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 { } /** - * + * 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 { } } + // set random outputs for (Output output : outputs) { output.setConnection(0, getRandomConnection()); } @@ -99,11 +155,21 @@ public class Chromosome implements Comparable { } /** - * @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. + *
+ * 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 { } } + /** + * 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 { } /** - * 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 { } /** - * Returns a random allowed connection respecting levels back.
+ * Returns a random allowed connection respecting levels back.
* 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 { /** * 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 { } /** - * 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 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(); - + // 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: + *
    + *
  • the chromosomes being compared are not the same instance;
  • + *
  • the connections of the compared chromosomes are not the same instance;
  • + *
  • the grid position of the chromosome's elements are the same;
  • + *
+ *

+ * The relationship computed by this method is: + *
    + *
  • symmetric: a.copyOf(b) == b.copyOf(a);
  • + *
  • not reflexive: a.copyOf(a) returns false;
  • + *
  • 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.
  • + *
+ * @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 { 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 { 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 { 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. + *

+ * 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. + *

+ * 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:
- * - the elements being compared are not the same instance;
- * - the connections of the compared elements are not the same instance;
- * - the elements have the same function (in the case of Node);
- * - the grid position of the elements themselves are the same;
- * - the grid position of all equivalent connections are the same;

+ * Asserts if the specified element is a copy of the elements + * this is called on.
+ * This method returns true if and only if: + *
    + *
  • the elements being compared are not the same instance;
  • + *
  • the connections of the compared elements are not the same instance;
  • + *
  • the elements have the same function (in the case of Node);
  • + *
  • the grid position of the elements themselves are the same;
  • + *
  • the grid position of all equivalent connections are the same;
  • + *
+ *

+ * The relationship computed by this method is: + *
    + *
  • symmetric: a.copyOf(b) == b.copyOf(a);
  • + *
  • not reflexive: a.copyOf(a) returns false;
  • + *
  • 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.
  • * - * The relationship computed by this method is:
    - * - symmetric: a.copyOf(b) == b.copyOf(a);
    - * - not reflexive: a.copyOf(a) returns false;
    - * - 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.
    - * - * @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 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 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. + *

    + * {@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. + *

    + * 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}. + *

    + * 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 critical 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}. *

    * 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. *

    * 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; diff --git a/src/jcgp/gui/GUI.java b/src/jcgp/gui/GUI.java index 62a159c..cd4778f 100644 --- a/src/jcgp/gui/GUI.java +++ b/src/jcgp/gui/GUI.java @@ -21,6 +21,13 @@ import jcgp.gui.population.GUINode; import jcgp.gui.population.PopulationPane; import jcgp.gui.settings.SettingsPane; +/** + * Main class for the graphical user interface (GUI) + * + * + * @author Eduardo Pedroni + * + */ public class GUI extends Application { /* Colours */ @@ -185,8 +192,8 @@ public class GUI extends Application { public void reset() { if (!running && settingsPane.areParametersValid()) { setEvaluating(false); - settingsPane.applyParameters(); jcgp.reset(); + settingsPane.applyParameters(); reDraw(); } } diff --git a/src/jcgp/gui/dragresize/HorizontalDragResize.java b/src/jcgp/gui/dragresize/HorizontalDragResize.java index 3acfd4a..84c322f 100644 --- a/src/jcgp/gui/dragresize/HorizontalDragResize.java +++ b/src/jcgp/gui/dragresize/HorizontalDragResize.java @@ -7,10 +7,11 @@ import javafx.scene.layout.Region; import jcgp.gui.GUI; /** + * + * Decorator pattern. * * http://andrewtill.blogspot.co.uk/2012/12/dragging-to-resize-javafx-region.html * - * */ public class HorizontalDragResize { diff --git a/src/jcgp/gui/dragresize/VerticalDragResize.java b/src/jcgp/gui/dragresize/VerticalDragResize.java index 8d79a2e..9397e5d 100644 --- a/src/jcgp/gui/dragresize/VerticalDragResize.java +++ b/src/jcgp/gui/dragresize/VerticalDragResize.java @@ -10,7 +10,6 @@ import jcgp.gui.GUI; * * http://andrewtill.blogspot.co.uk/2012/12/dragging-to-resize-javafx-region.html * - * */ public class VerticalDragResize { diff --git a/src/jcgp/gui/population/GUIGene.java b/src/jcgp/gui/population/GUIGene.java index c41261d..3ace150 100644 --- a/src/jcgp/gui/population/GUIGene.java +++ b/src/jcgp/gui/population/GUIGene.java @@ -8,7 +8,6 @@ import javafx.scene.text.Font; import javafx.scene.text.Text; import javafx.scene.text.TextAlignment; import jcgp.backend.population.Connection; -import jcgp.backend.population.Gene; public abstract class GUIGene extends Group { @@ -83,8 +82,6 @@ public abstract class GUIGene extends Group { public abstract void updateLines(); - public abstract Gene getGene(); - public abstract void setChangingConnection(Connection newConnection); public abstract Connection getChangingConnection(); diff --git a/src/jcgp/gui/population/GUIInput.java b/src/jcgp/gui/population/GUIInput.java index 065c125..05372c4 100644 --- a/src/jcgp/gui/population/GUIInput.java +++ b/src/jcgp/gui/population/GUIInput.java @@ -165,11 +165,6 @@ public class GUIInput extends GUIGene { } - @Override - public Input getGene() { - return input; - } - /** * Set all connections to a given state. * diff --git a/src/jcgp/gui/population/GUINode.java b/src/jcgp/gui/population/GUINode.java index 731db8f..6dfeaa4 100644 --- a/src/jcgp/gui/population/GUINode.java +++ b/src/jcgp/gui/population/GUINode.java @@ -319,7 +319,7 @@ public class GUINode extends GUIGene { return true; } else if (target instanceof GUINode) { // target and source are nodes, let's look at levels back - Node t = ((GUINode) target).getGene(), s = ((GUINode) source).getGene(); + Node t = ((GUINode) target).getNode(), s = ((GUINode) source).getNode(); if (s.getColumn() - t.getColumn() > 0 && s.getColumn() - t.getColumn() <= resources.levelsBack()) { return true; } @@ -344,6 +344,10 @@ public class GUINode extends GUIGene { } + public Node getNode() { + return node; + } + /** * Place the end of the specified line on the output of the associated connection. * @@ -383,12 +387,6 @@ public class GUINode extends GUIGene { } } - @Override - public Node getGene() { - return node; - } - - @Override public void setConnections(GUIGeneState newState) { for (int i = 0; i < lines.length; i++) { diff --git a/src/jcgp/gui/population/GUIOutput.java b/src/jcgp/gui/population/GUIOutput.java index 79159b9..29752cd 100644 --- a/src/jcgp/gui/population/GUIOutput.java +++ b/src/jcgp/gui/population/GUIOutput.java @@ -239,11 +239,6 @@ public class GUIOutput extends GUIGene { } } - @Override - public Output getGene() { - return output; - } - @Override public void setConnections(GUIGeneState newState) { parent.getGuiGene(output.getSource()).setState(newState); diff --git a/src/jcgp/gui/settings/SettingsPane.java b/src/jcgp/gui/settings/SettingsPane.java index e3d2096..802c1f1 100644 --- a/src/jcgp/gui/settings/SettingsPane.java +++ b/src/jcgp/gui/settings/SettingsPane.java @@ -235,7 +235,7 @@ public class SettingsPane extends AnchorPane { } private Button makeLoadTestCaseButton() { - Button b = new Button("Load problem data"); + Button b = new Button("Load data"); b.setOnAction(new EventHandler() { @Override public void handle(ActionEvent event) { @@ -255,7 +255,7 @@ public class SettingsPane extends AnchorPane { } private Button makeTestCaseButton() { - Button b = new Button("Show test cases"); + Button b = new Button("Show data1"); b.setOnAction(new EventHandler() { @Override public void handle(ActionEvent event) { diff --git a/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java b/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java index eb669bb..29f8e1f 100644 --- a/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java @@ -9,9 +9,9 @@ import jcgp.backend.parameters.ParameterStatus; import jcgp.gui.settings.SettingsPane; /** - * This extension of GUIParameter uses a CheckBox to display - * the value of a BooleanParameter. It cannot be constructed - * directly - instead, use GUIParameter.create(). + * This extension of @code{GUIParameter} uses a @code{CheckBox} to display + * the value of a @code{BooleanParameter}. It cannot be constructed + * directly - instead, use @code{GUIParameter.create()}. *

    * See {@link GUIParameter} for more information. * diff --git a/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java b/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java index 16a4cd4..c3c1f59 100644 --- a/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java @@ -12,9 +12,9 @@ import jcgp.backend.parameters.ParameterStatus; import jcgp.gui.settings.SettingsPane; /** - * This extension of GUIParameter uses a TextField to display - * the value of a DoubleParameter. It cannot be constructed - * directly - instead, use GUIParameter.create(). + * This extension of @code{GUIParameter} uses a @code{TextField} to display + * the value of a @code{DoubleParameter}. It cannot be constructed + * directly - instead, use @code{GUIParameter.create()}. *

    * See {@link GUIParameter} for more information. * @@ -26,11 +26,11 @@ public class GUIDoubleParameter extends GUIParameter { private DecimalFormat decimalFormat; /** - * This default-visibility constructor is intended for use + * This protected constructor is intended for use * by the factory method only. * */ - GUIDoubleParameter(Parameter parameter, SettingsPane sp) { + protected GUIDoubleParameter(Parameter parameter, SettingsPane sp) { super(parameter, sp); } @@ -57,7 +57,6 @@ public class GUIDoubleParameter extends GUIParameter { ObservableValue observable, String oldValue, String newValue) { if (!settingsPane.isExperimentRunning()) { - //if (newValue.matches("([0-9]*[.]?[0-9]*)")) { if (newValue.matches("^[-+]?[0-9]*\\.?[0-9]+$")) { if (!newValue.isEmpty()) { double value = Double.parseDouble(newValue); diff --git a/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java b/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java index e8a9183..5bf6d9c 100644 --- a/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java @@ -10,9 +10,9 @@ import jcgp.backend.parameters.ParameterStatus; import jcgp.gui.settings.SettingsPane; /** - * This extension of GUIParameter uses a TextField to display - * the value of a IntegerParameter. It cannot be constructed - * directly - instead, use GUIParameter.create(). + * This extension of @code{GUIParameter} uses a @code{TextField} to display + * the value of a @code{IntegerParameter}. It cannot be constructed + * directly - instead, use @code{GUIParameter.create()}. *

    * See {@link GUIParameter} for more information. * @@ -23,11 +23,11 @@ public class GUIIntegerParameter extends GUIParameter { private TextField textField; /** - * This default-visibility constructor is intended for use + * This protected constructor is intended for use * by the factory method only. * */ - GUIIntegerParameter(Parameter parameter, SettingsPane sp) { + protected GUIIntegerParameter(Parameter parameter, SettingsPane sp) { super(parameter, sp); } diff --git a/src/jcgp/gui/settings/parameters/GUIParameter.java b/src/jcgp/gui/settings/parameters/GUIParameter.java index 0a9149f..3009340 100644 --- a/src/jcgp/gui/settings/parameters/GUIParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIParameter.java @@ -20,14 +20,16 @@ import jcgp.gui.settings.SettingsPane; /** * - * This is the base class for all GUIParameters. Using the factory method GUIParameter.create() + * This is the base class for all @code{GUIParameter}s. Using the factory method @code{GUIParameter.create()} * generates an appropriate instance of this class for the specified parameter. *

    - * GUIParameter is an HBox containing a Text for the parameter name and a Control for interaction. - * It stores an instance of its associated Parameter object and also contains a Tooltip for + * @code{GUIParameter} is an @code{HBox} containing a @code{Text} for the parameter name + * and a @code{Control} for interaction. + * It stores an instance of its associated @code{Parameter} object and also contains a @code{Tooltip} for * displaying status information. *

    - * Monitor parameters have their Control disabled so that no changed can be made via the GUI. + * Monitor parameters are updated automatically and have their @code{Control} disabled so + * that no changes can be made via the GUI. * Non-monitor parameters are updated automatically as well, but may be changed by the user * if the program is not evolving. * @@ -64,12 +66,12 @@ public abstract class GUIParameter extends HBox { /** * This protected template constructor contains the common elements to all - * GUIParameters and should be invoked by any subclasses using super(). It + * @code{GUIParameter}s and should be invoked by any subclasses using @code{super()}. It * defers the creation of the parameter {@code Control} object to the subclass * currently being built (which in turn is defined by the factory method). * - * @param parameter a Parameter for which to generate a GUIParameter. - * @param sp a reference to the SettingsPane. + * @param parameter a @code{Parameter} for which to generate a @code{GUIParameter}. + * @param sp a reference to the @code{SettingsPane}. */ protected GUIParameter(Parameter parameter, final SettingsPane settingsPane) { this.parameter = parameter; @@ -105,13 +107,13 @@ public abstract class GUIParameter extends HBox { } /** - * Factory method to create GUIParameters from Parameters. - * Use this to create an appropriate GUIParameter from any instance of Parameter, - * rather than manually downcasting the Parameter object every time. + * Factory method to create @code{GUIParameter}s from @code{Parameter}s. + * Use this to create an appropriate @code{GUIParameter} from any instance of @code{Parameter}, + * rather than manually downcasting the @code{Parameter} object every time. * - * @param parameter a Parameter for which to generate a GUIParameter. - * @param sp a reference to the SettingsPane. - * @return an appropriate instance of GUIParameter. + * @param parameter a parameter for which to generate a @code{GUIParameter}. + * @param sp a reference to the @code{SettingsPane}. + * @return an appropriate instance of @code{GUIParameter}. */ public static GUIParameter create(Parameter parameter, SettingsPane sp) { if (parameter instanceof IntegerParameter) { @@ -171,7 +173,7 @@ public abstract class GUIParameter extends HBox { /** * Force the parameter to validate its current value, and apply the associated - * style to the GUIParameter. + * style to the @code{GUIParameter}. */ public void validate() { parameter.validate(parameter.get()); @@ -190,7 +192,7 @@ public abstract class GUIParameter extends HBox { } /** - * Set the current parameter value as the reference value of the GUIParameter. + * Set the current parameter value as the reference value of the @code{GUIParameter}. * The new reference value will be used to determine the validity of the parameter, * should its value change. */ @@ -203,31 +205,28 @@ public abstract class GUIParameter extends HBox { * GUIParameter() as necessary. */ /** - * This method returns the Control object used to control the parameter. + * This method returns the @code{Control} object used to control the parameter. *

    - * Implementations of GUIParameter must override this method and return - * a control appropriate to the type of parameter. This will typically be - * done by referencing the protected field GUIParameter.parameter. + * Implementations of @code{GUIParameter} must override this method and return + * a @code{Control} appropriate to the type of parameter. This will typically be + * done by referencing the protected field @code{GUIParameter.parameter}. * * @return the Control object to be added to the GUIParameter. */ protected abstract Control makeControl(); /** - * Adds the necessary handlers to the Control object in order to modify + * Adds the necessary handlers to the @code{Control} object in order to modify * the underlying parameter. This will typically consist of filtering key * presses to ensure no invalid characters are inserted, applying the new * value to the underlying parameter and revalidating the parameters to * reflect the changes made. - *

    - * Note that changelisteners registered to the main content property of the - * control should always call handleChange() to update the */ protected abstract void setControlListeners(); /** - * This method is called to style the GUIParameter according to the status of - * the parameter, which can be obtained with parameter.getStatus(). While the + * This method is called to style the @code{GUIParameter} according to the status of + * the parameter, which can be obtained with @code{parameter.getStatus()}. While the * subclass is free to style itself in any way, the CSS strings defined here * (INVALID_PARAMETER_STYLE, WARNING_PARAMETER_STYLE, VALID_PARAMETER_STYLE) * provide a way to keep the GUI consistent. -- cgit v1.2.3