diff options
Diffstat (limited to 'src/jcgp/backend/population/Chromosome.java')
| -rw-r--r-- | src/jcgp/backend/population/Chromosome.java | 192 | 
1 files changed, 166 insertions, 26 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;  	}  | 
