From dd3b6446671f31d716eb07e546c6150b4d080abd Mon Sep 17 00:00:00 2001 From: Eduardo Pedroni Date: Tue, 11 Feb 2014 16:50:27 +0000 Subject: Implemented more tests, refactored chromosome more, added the beginnings of active node detection --- README | 17 ++- src/jcgp/Utilities.java | 78 ------------ src/jcgp/ea/StandardMutator.java | 24 ++-- src/jcgp/population/Chromosome.java | 198 +++++++++++++++++++++-------- src/jcgp/population/Input.java | 10 +- src/jcgp/population/MutableElement.java | 4 - src/jcgp/population/Node.java | 16 ++- src/jcgp/population/Output.java | 24 ++-- src/jcgp/population/Population.java | 5 +- src/jcgp/tests/ChromosomeTests.java | 216 +++++++++++++++++++++++++------- 10 files changed, 378 insertions(+), 214 deletions(-) diff --git a/README b/README index 3095efa..18e3ebe 100644 --- a/README +++ b/README @@ -88,6 +88,19 @@ necessarily a problem. 7/2 The resource classes have been refactored so that tests can be implemented more easily. Parameters, Utilities and TruthTable -are now top-level classes and work independently from CGP, allowing them to be initialised for testing purposes. Some chromosome -tests have been written and more tests will be written in the next few days. +are now top-level classes and work independently from CGP, allowing them to be initialised for testing purposes. Some +chromosome tests have been written and more tests will be written in the next few days. +11/2 + +Methods to get random connections and mutable elements have been moved into Chromosome, as well as refactored to require +less random number generations. + +Chromosome and its associated members (Node, Output, Input) have been refactored to allow copying. A copy constructor has been +written which initialises a new chromosome as an exact copy of a given chromosome. + +Cloning has been tested and is working. + +Active node detection will be implemented via a getActiveNodes() method in Chromosome. The chromosome is notified whenever +one of its nodes or outputs is mutated, and re-computes the list of active elements when it is required. It does so by recursively +acquiring node connections and copying them to a separate list. \ No newline at end of file diff --git a/src/jcgp/Utilities.java b/src/jcgp/Utilities.java index f052595..394a481 100644 --- a/src/jcgp/Utilities.java +++ b/src/jcgp/Utilities.java @@ -24,62 +24,6 @@ public class Utilities { return numberGenerator.nextDouble() * limit; } - /** - * Returns a random allowed connection respecting levels back. - * This method may always pick inputs, as they can be picked - * regardless of the column. - * - * TODO remove? - * - * @param chromosome the chromosome to pick from - * @param column the column to use as reference - * @return a random connection - */ - public static Connection getRandomConnection(Chromosome chromosome, int column){ - // work out the allowed range obeying levels back - int allowedColumns = ((column >= Parameters.getLevelsBack()) ? Parameters.getLevelsBack() : column); - int offset = column - allowedColumns; - - // choose input or node - int connectionType = getRandomInt(Parameters.getInputs() + (Parameters.getRows() * allowedColumns)); - if (connectionType < Parameters.getInputs()) { - // input - return chromosome.getInput(getRandomInt(Parameters.getInputs())); - } else { - // node - return chromosome.getNode(getRandomInt(Parameters.getRows()), getRandomInt(allowedColumns) + offset); - } - } - - /** - * Returns a random allowed connection. - * - * This method may always pick inputs, as they can be picked - * regardless of the column. - * - * TODO remove? - * - * @param chromosome the chromosome to pick from - * @param column the column to use as reference - * @param levelsBack whether or not to respect levels back - * @return a random connection - */ - public static Connection getRandomConnection(Chromosome chromosome, int column, boolean levelsBack){ - if (levelsBack) { - return getRandomConnection(chromosome, column); - } else { - // choose input or node - int connectionType = getRandomInt(Parameters.getInputs() + (Parameters.getRows() * column)); - if (connectionType < Parameters.getInputs()) { - // input - return chromosome.getInput(getRandomInt(Parameters.getInputs())); - } else { - // node - return chromosome.getNode(getRandomInt(Parameters.getRows()), getRandomInt(column)); - } - } - } - /** * @param chromosome the chromosome to choose from * @return a random input @@ -131,28 +75,6 @@ public class Utilities { } } - /** - * This method picks a random mutable element from the given chromosome. - * - * It will pick outputs or nodes fairly. - * - * TODO probably remove this - * - * @param chromosome the chromosome to pick from - * @return a random mutable element - */ - public static MutableElement getRandomMutable(Chromosome chromosome){ - // choose output or node - int connectionType = getRandomInt(Parameters.getOutputs() + Parameters.getNodeCount()); - - if (connectionType < Parameters.getOutputs()) { - // outputs - return chromosome.getOutput(getRandomInt(Parameters.getOutputs())); - } else { - // node - return chromosome.getNode(getRandomInt(Parameters.getRows()), getRandomInt(Parameters.getRows())); - } - } /** * pick from any column - use this for setting outputs diff --git a/src/jcgp/ea/StandardMutator.java b/src/jcgp/ea/StandardMutator.java index 8af3f5c..6a4af1a 100644 --- a/src/jcgp/ea/StandardMutator.java +++ b/src/jcgp/ea/StandardMutator.java @@ -14,18 +14,18 @@ public class StandardMutator implements Mutator { int mutations = (int) (Parameters.getMutationRate() * ((double) Parameters.getNodeCount() / 100)); for (int i = 0; i < mutations; i++) { - MutableElement m = Utilities.getRandomMutable(chromosome); - - if (m instanceof Output) { - m.setConnection(Utilities.getRandomConnection(chromosome, m.getColumn())); - } else if (m instanceof Node) { - int geneType = Utilities.getRandomInt(1 + Parameters.getMaxArity()); - if (geneType < 1) { - ((Node) m).setFunction(Utilities.getRandomFunction()); - } else { - m.setConnection(Utilities.getRandomConnection(chromosome, m.getColumn())); - } - } + MutableElement m = chromosome.getRandomMutableElement(); +// +// if (m instanceof Output) { +// m.setConnection(chromosome.getRandomConnection(m.getColumn())); +// } else if (m instanceof Node) { +// int geneType = Utilities.getRandomInt(1 + Parameters.getMaxArity()); +// if (geneType < 1) { +// ((Node) m).setFunction(Utilities.getRandomFunction()); +// } else { +// m.setConnection(chromosome.getRandomConnection(m.getColumn())); +// } +// } } } } diff --git a/src/jcgp/population/Chromosome.java b/src/jcgp/population/Chromosome.java index 0dc3ff9..1f264f2 100644 --- a/src/jcgp/population/Chromosome.java +++ b/src/jcgp/population/Chromosome.java @@ -1,5 +1,7 @@ package jcgp.population; +import java.util.ArrayList; + import jcgp.Parameters; import jcgp.Utilities; import jcgp.fitness.ParameterMismatchException; @@ -9,11 +11,15 @@ public class Chromosome { private Input[] inputs; private Node[][] nodes; private Output[] outputs; + + private ArrayList activeNodes; private int fitness = 0; + private boolean recomputeActiveNodes = true; /** - * Good citizen. + * Initialise a chromosome with the specified parameters. Random valid connections + * are created. * * @param outputs * @param columns @@ -21,61 +27,110 @@ public class Chromosome { * @param inputs * */ - public Chromosome(int inputCount, int rows, int columns, int outputCount) { - - instantiateElements(inputCount, rows, columns, outputCount); - + public Chromosome() { + // allocate memory for all elements of the chromosome + instantiateElements(); + // set random connections so that the chromosome can be evaluated initialiseConnections(); + } + /** + * Copy constructor. + * + * Initialise a new chromosome with the exact same connections as a given instance of Chromosome. + * + * @param clone the chromosome to be copied + */ + public Chromosome(Chromosome clone) { + // allocate memory for all elements of the chromosome + instantiateElements(); + // initialise all connections based on argument + copyConnections(clone); } /** - * @param inputCount - * @param rows - * @param columns - * @param outputCount + * */ - private void instantiateElements(int inputCount, int rows, int columns, int outputCount) { - inputs = new Input[inputCount]; - for (int i = 0; i < inputCount; i++) { - inputs[i] = new Input(); + private void instantiateElements() { + inputs = new Input[Parameters.getInputs()]; + for (int i = 0; i < Parameters.getInputs(); i++) { + inputs[i] = new Input(i); } // rows first - nodes = new Node[rows][columns]; - for (int r = 0; r < rows; r++) { - //nodes[r] = new Node[Parameters.getColumns()]; - for (int c = 0; c < columns; c++) { - nodes[r][c] = new Node(r, c); + nodes = new Node[Parameters.getRows()][Parameters.getColumns()]; + for (int r = 0; r < Parameters.getRows(); r++) { + for (int c = 0; c < Parameters.getColumns(); c++) { + nodes[r][c] = new Node(this, r, c); } } - outputs = new Output[outputCount]; - for (int o = 0; o < outputCount; o++) { - outputs[o] = new Output(o); + outputs = new Output[Parameters.getOutputs()]; + for (int o = 0; o < Parameters.getOutputs(); o++) { + outputs[o] = new Output(this, o); } } + /** + * + */ private void initialiseConnections() { // initialise nodes - [rows][columns] for (int r = 0; r < nodes.length; r++) { - for (int c = 0; c < nodes.length; c++) { + for (int c = 0; c < nodes[r].length; c++) { Connection[] connections = new Connection[Parameters.getMaxArity()]; for (int i = 0; i < connections.length; i++) { - connections[i] = Utilities.getRandomConnection(this, c); + connections[i] = getRandomConnection(c); } nodes[r][c].initialise(Utilities.getRandomFunction(), connections); } } for (Output output : outputs) { - output.setConnection(Utilities.getRandomNode(this)); + output.setConnection(getRandomConnection()); } } - public int getActiveNodeCount() { - return 0; + /** + * @param clone + */ + private void copyConnections(Chromosome clone) { + // copy nodes - [rows][columns] + for (int r = 0; r < nodes.length; r++) { + for (int c = 0; c < nodes[r].length; c++) { + // make array of connections to initialise with + Connection[] connections = new Connection[Parameters.getMaxArity()]; + // populate with connections equivalent to clone + Connection copyConnection; + for (int i = 0; i < connections.length; i++) { + copyConnection = clone.getNode(r, c).getConnections(i); + if (copyConnection instanceof Input) { + connections[i] = inputs[((Input) copyConnection).getIndex()]; + } else if (copyConnection instanceof Node) { + connections[i] = nodes[((Node) copyConnection).getRow()][((Node) copyConnection).getColumn()]; + } else { + System.out.println("Warning: Connection of subtype " + copyConnection.getClass().toString() + " is not explicitly handled by copy constructor."); + } + } + // initialise with copied arguments + nodes[r][c].initialise(clone.getNode(r, c).getFunction(), connections); + } + } + + // do the same to outputs + Connection copyOutput; + for (int o = 0; o < outputs.length; o++) { + copyOutput = clone.getOutput(o).getSource(); + if (copyOutput instanceof Input) { + outputs[o].setConnection(inputs[((Input) copyOutput).getIndex()]); + } else if (copyOutput instanceof Node) { + outputs[o].setConnection(nodes[((Node) copyOutput).getRow()][((Node) copyOutput).getColumn()]); + } else { + // something bad happened + System.out.println("Warning: Connection of subtype " + copyOutput.getClass().toString() + " is not explicitly handled by copy constructor."); + } + } } public Node getNode(int row, int column) { @@ -110,6 +165,11 @@ public class Chromosome { } } + /** + * This method is useful for mutating chromosomes. + * + * @return a random element that can be mutated - Node or Output + */ public MutableElement getRandomMutableElement() { // choose output or node int index = Utilities.getRandomInt(outputs.length + Parameters.getNodeCount()); @@ -123,9 +183,9 @@ public class Chromosome { return nodes[index / Parameters.getColumns()][index % Parameters.getColumns()]; } } - + /** - * 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. * @@ -133,41 +193,71 @@ public class Chromosome { * @return a random connection */ public Connection getRandomConnection(int column) { - - - - return null; - + // work out the allowed range obeying levels back + int allowedColumns = ((column >= Parameters.getLevelsBack()) ? Parameters.getLevelsBack() : column); + int offset = ((column - allowedColumns) * nodes.length) - inputs.length; + + // choose input or allowed node + int index = Utilities.getRandomInt(inputs.length + (nodes.length * allowedColumns)); + if (index < inputs.length) { + // input + return inputs[index]; + } else { + // node + // offset it to address the right columns + index += offset; + return nodes[index % nodes.length][index / nodes.length]; + } } - + /** - * Returns a random allowed connection. + * This method will pick a completely random connection, independently + * of levels back, including inputs. * - * This method may always pick inputs, as they can be picked - * regardless of the column. + * Useful for setting outputs. * - * TODO optimise for less random generations * - * @param column the column to use as reference - * @param levelsBack whether or not to respect levels back * @return a random connection */ - public Connection getRandomConnection(int column, boolean levelsBack) { - - if (levelsBack) { - return getRandomConnection(chromosome, column); + public Connection getRandomConnection() { + // choose output or node + int index = Utilities.getRandomInt(inputs.length + Parameters.getNodeCount()); + + if (index < inputs.length) { + // outputs + return inputs[index]; } else { - // choose input or node - int connectionType = Utilities.getRandomInt(inputs.length + (nodes.length * column)); - if (connectionType < inputs.length) { - // input - return chromosome.getInput(getRandomInt(Parameters.getInputs())); - } else { - // node - return chromosome.getNode(getRandomInt(Parameters.getRows()), getRandomInt(column)); - } + // node + index -= inputs.length; + return nodes[index / Parameters.getColumns()][index % Parameters.getColumns()]; } - + } + + /** + * This causes the list of active nodes to be recomputed lazily (once it is actually requested). + */ + public void recomputeActiveNodes() { + recomputeActiveNodes = true; + } + + /** + * This method computes a list of active nodes (if necessary) and returns it. + * + * @return + */ + public ArrayList getActiveNodes() { + // lazy recomputation has been triggered, do it + if (recomputeActiveNodes) { + recomputeActiveNodes = false; + activeNodes = new ArrayList(); + + for (int r = 0; r < nodes.length; r++) { + for (int c = 0; c < nodes[r].length; c++) { + + } + } + } + return activeNodes; } } diff --git a/src/jcgp/population/Input.java b/src/jcgp/population/Input.java index e00573e..ee008ce 100644 --- a/src/jcgp/population/Input.java +++ b/src/jcgp/population/Input.java @@ -2,7 +2,11 @@ package jcgp.population; public class Input implements Connection { - private int value = 0; + private int value = 0, index; + + public Input(int index) { + this.index = index; + } public void setValue(int newValue) { value = newValue; @@ -13,4 +17,8 @@ public class Input implements Connection { return value; } + public int getIndex() { + return index; + } + } diff --git a/src/jcgp/population/MutableElement.java b/src/jcgp/population/MutableElement.java index c21ee0b..5eae4ef 100644 --- a/src/jcgp/population/MutableElement.java +++ b/src/jcgp/population/MutableElement.java @@ -4,8 +4,4 @@ public interface MutableElement { public void setConnection(Connection newConnection); - public int getColumn(); - - public int getRow(); - } diff --git a/src/jcgp/population/Node.java b/src/jcgp/population/Node.java index 3dbabb2..40ffa52 100644 --- a/src/jcgp/population/Node.java +++ b/src/jcgp/population/Node.java @@ -10,8 +10,10 @@ public class Node implements MutableElement, Connection { private Function function; private Connection[] connections; private int column, row; + private Chromosome chromosome; - public Node(int row, int column) { + public Node(Chromosome chromosome, int row, int column) { + this.chromosome = chromosome; this.column = column; this.row = row; } @@ -23,11 +25,13 @@ public class Node implements MutableElement, Connection { public void setFunction(Function newFunction) { function = newFunction; + chromosome.recomputeActiveNodes(); } @Override public void setConnection(Connection newConnection) { connections[Utilities.getRandomInt(connections.length)] = newConnection; + chromosome.recomputeActiveNodes(); } public void initialise(Function newFunction, Connection ... newConnections) throws InsufficientConnectionsException { @@ -41,14 +45,20 @@ public class Node implements MutableElement, Connection { } } - @Override public int getColumn() { return column; } - @Override public int getRow() { return row; } + + public Function getFunction() { + return function; + } + + public Connection getConnections(int index) { + return connections[index]; + } } diff --git a/src/jcgp/population/Output.java b/src/jcgp/population/Output.java index ce4f776..68045b0 100644 --- a/src/jcgp/population/Output.java +++ b/src/jcgp/population/Output.java @@ -1,15 +1,15 @@ package jcgp.population; -import jcgp.Parameters; - public class Output implements MutableElement { private Connection source; - private int row; + private Chromosome chromosome; + private int index; - public Output(int row) { - this.row = row; + public Output(Chromosome chromosome, int index) { + this.chromosome = chromosome; + this.index = index; } public int calculate() { @@ -19,17 +19,17 @@ public class Output implements MutableElement { @Override public void setConnection(Connection newConnection) { source = newConnection; - + chromosome.recomputeActiveNodes(); } - @Override - public int getColumn() { - return Parameters.getColumns(); + public int getIndex() { + return index; } - @Override - public int getRow() { - return row; + public Connection getSource() { + return source; } + + } diff --git a/src/jcgp/population/Population.java b/src/jcgp/population/Population.java index b56d992..fb968fb 100644 --- a/src/jcgp/population/Population.java +++ b/src/jcgp/population/Population.java @@ -11,10 +11,7 @@ public class Population implements Iterable { public Population() { population = new Chromosome[Parameters.getPopulationSize()]; for (int c = 0; c < population.length; c++) { - population[c] = new Chromosome(Parameters.getInputs(), - Parameters.getRows(), - Parameters.getColumns(), - Parameters.getOutputs()); + population[c] = new Chromosome(); } } diff --git a/src/jcgp/tests/ChromosomeTests.java b/src/jcgp/tests/ChromosomeTests.java index 50d17b9..ece8203 100644 --- a/src/jcgp/tests/ChromosomeTests.java +++ b/src/jcgp/tests/ChromosomeTests.java @@ -1,15 +1,17 @@ package jcgp.tests; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.util.Random; import jcgp.Parameters; import jcgp.Utilities; +import jcgp.fitness.ParameterMismatchException; import jcgp.function.Addition; import jcgp.function.FunctionSet; import jcgp.function.Subtraction; import jcgp.population.Chromosome; +import jcgp.population.Connection; import jcgp.population.Input; import jcgp.population.MutableElement; import jcgp.population.Node; @@ -24,24 +26,24 @@ import org.junit.Test; * * - The chromosome should be able to return a specified node, input or output. * - It should be able to return a random MutableElement. - * - It should be able to return a random Connection + * - It should be able to return a random allowed connection given a column + * - It should be able to return any random connection * - It should contain a freely modifiable fitness value. - * - It should be a good citizen - fully initialised upon instantiation. + * - For truth table evaluations, it should be able to have its inputs set. + * - For truth table evaluations, the output should return a value according to what + * it is set to. * - It should feature a clone constructor, which creates a deep copy of a * specified Chromosome object. - * - It should be able to return the number of active nodes. - * - For truth table evaluations, it should be able to have its inputs set. + * - It should be able to return a list of active nodes. + * + * WARNING: changing parameters may cause the tests to incorrectly fail! * * @author Eduardo Pedroni * */ public class ChromosomeTests { - Chromosome chromosome; - int inputCount = 3, - rows = 3, - columns = 3, - outputCount = 3; + private Chromosome chromosome; @Before public void setUp() throws Exception { @@ -52,54 +54,180 @@ public class ChromosomeTests { Utilities.setResources(new Random(1234), functionSet); // initialise parameters - Parameters.setColumns(columns); - Parameters.setRows(rows); - Parameters.setInputs(inputCount); - Parameters.setOutputs(outputCount); - Parameters.setLevelsBack(1); + Parameters.setColumns(20); + Parameters.setRows(10); + Parameters.setInputs(2); + Parameters.setOutputs(4); + Parameters.setLevelsBack(20); Parameters.setMutationRate(10); Parameters.setTotalGenerations(100); Parameters.setTotalRuns(5); Parameters.setMaxArity(functionSet.getMaxArity()); - chromosome = new Chromosome(inputCount, rows, columns, outputCount); - + chromosome = new Chromosome(); + } + /** + * + */ @Test - public void test() { - // pick arbitrary node, assume that if one node is right, all nodes are right - boolean nodeReturn = chromosome.getNode(1, 2).getColumn() == 2 - && chromosome.getNode(1, 2).getRow() == 1 - && chromosome.getNode(1, 2) instanceof Node; - assertTrue("Incorrect node returned.", nodeReturn); - - // set input values, check that acquired values are correct - chromosome.setInputs(4, 5, 6); - boolean inputReturn = chromosome.getInput(0).getValue() == 4 && chromosome.getInput(0) instanceof Input - && chromosome.getInput(1).getValue() == 5 && chromosome.getInput(0) instanceof Input - && chromosome.getInput(2).getValue() == 6 && chromosome.getInput(0) instanceof Input; - assertTrue("Incorrect inputs returned.", inputReturn); - - // connect outputs to inputs, check that calculated outputs return input values - for (int i = 0; i < outputCount; i++) { - chromosome.getOutput(i).setConnection(chromosome.getInput(0)); - } - boolean outputReturn = chromosome.getOutput(0).calculate() == 4 && chromosome.getOutput(0) instanceof Output - && chromosome.getOutput(1).calculate() == 4 && chromosome.getOutput(0) instanceof Output - && chromosome.getOutput(2).calculate() == 4 && chromosome.getOutput(0) instanceof Output; - assertTrue("Incorrect output returned.", outputReturn); - - // get a mutable element, check that it is a Mutable - boolean mutableReturn = chromosome.getRandomMutableElement() != chromosome.getRandomMutableElement() && chromosome.getRandomMutableElement() instanceof MutableElement; - assertTrue("Returned the same element.", mutableReturn); + public void cloneTest() { + int[] testInputs; + // create a clone, check to see if it really is a clone + Chromosome clone = new Chromosome(chromosome); + // set input values + testInputs = new int[Parameters.getInputs()]; + for (int i = 0; i < Parameters.getInputs(); i++) { + testInputs[i] = i * 2 - 3; + } + chromosome.setInputs(testInputs); + clone.setInputs(testInputs); + // connect outputs to inputs, check that the outputs match + for (int i = 0; i < Parameters.getOutputs(); i++) { + chromosome.getOutput(i).setConnection(chromosome.getInput(i % Parameters.getInputs())); + clone.getOutput(i).setConnection(clone.getInput(i % Parameters.getInputs())); + assertTrue("Incorrect output returned.", chromosome.getOutput(i).calculate() == + clone.getOutput(i).calculate()); + } + // change clone inputs, outputs should no longer match + testInputs = new int[Parameters.getInputs()]; + for (int i = 0; i < Parameters.getInputs(); i++) { + testInputs[i] = i * 2; + } + clone.setInputs(testInputs); + for (int i = 0; i < Parameters.getOutputs(); i++) { + assertTrue("Incorrect output returned.", chromosome.getOutput(i).calculate() != + clone.getOutput(i).calculate()); + } + } + + /** + * + */ + @Test + public void fitnessTest() { // set a fitness value, check if returned value is the same chromosome.setFitness(10); assertTrue("Incorrect fitness returned.", chromosome.getFitness() == 10); - } + /** + * + */ + @Test + public void randomConnectionTest() { + // get random connections with column 0, check that they are all inputs + for (int i = 0; i < 10000; i++) { + boolean connectionReturn = chromosome.getRandomConnection(0) instanceof Input; + assertTrue("Connection is not an input.", connectionReturn); + } + + // get random connections with column 2 + // they should all be nodes, and their columns should be within range + int connectionNodes = 0, connectionOutOfRange = 0, connectionInputs = 0, connectionPicks = 100000; + int chosenColumn = 2; + for (int i = 0; i < connectionPicks; i++) { + Connection c = chromosome.getRandomConnection(chosenColumn); + if (c instanceof Node) { + connectionNodes++; + if (((Node) c).getColumn() >= chosenColumn) { + connectionOutOfRange++; + } + assertTrue("Connection is not allowed : " + ((Node) c).getColumn(), ((Node) c).getColumn() < chosenColumn && ((Node) c).getColumn() < chosenColumn); + } else if (c instanceof Input) { + connectionInputs++; + } else { + fail("Return is neither Node nor Input."); + + } + } + System.out.println("Out of " + connectionPicks + " connections picked from " + ((chosenColumn >= Parameters.getLevelsBack()) ? Parameters.getLevelsBack() : chosenColumn) * Parameters.getRows() + + " nodes and " + Parameters.getInputs() + " allowed nodes, " + connectionNodes + " were nodes and " + connectionInputs + " were inputs."); + + System.out.println("Node/input ratio: " + ((double) ((chosenColumn >= Parameters.getLevelsBack()) ? Parameters.getLevelsBack() : chosenColumn) * Parameters.getRows()) / (double) Parameters.getInputs() + + ", picked ratio: " + (double) connectionNodes / (double) connectionInputs); + + System.out.println(connectionOutOfRange + " nodes that disrespected levels back were picked."); + } + + /** + * + */ + @Test + public void randomMutableTest() { + // get mutable elements, check Node to Output ratio + int mutablePicks = 100000; + int mutableNodes = 0, mutableOutputs = 0; + for (int i = 0; i < mutablePicks; i++) { + MutableElement m = chromosome.getRandomMutableElement(); + + if (m instanceof Node) { + mutableNodes++; + } else if (m instanceof Output) { + mutableOutputs++; + } else { + fail("Return is neither Node nor Output."); + } + } + System.out.println("Out of " + mutablePicks + " mutable elements picked from " + Parameters.getNodeCount() + + " nodes and " + Parameters.getOutputs() + " outputs, " + mutableNodes + " were nodes and " + + mutableOutputs + " were outputs."); + System.out.println("Node/output ratio: " + (double) Parameters.getNodeCount() / (double) Parameters.getOutputs() + + ", picked ratio: " + (double) mutableNodes / (double) mutableOutputs + "\n"); + } + + /** + * + */ + @Test + public void getOutputsTest() { + // connect outputs to inputs, check that calculated outputs return input values + for (int i = 0; i < Parameters.getOutputs(); i++) { + chromosome.getOutput(i).setConnection(chromosome.getInput(i % Parameters.getInputs())); + assertTrue("Incorrect output returned.", chromosome.getOutput(i).calculate() == + chromosome.getInput(i % Parameters.getInputs()).getValue()); + } + } + + /** + * @throws ParameterMismatchException + */ + @Test + public void setInputTest() throws ParameterMismatchException { + // set input values, check that acquired values are correct + int[] testInputs = new int[Parameters.getInputs()]; + for (int i = 0; i < Parameters.getInputs(); i++) { + testInputs[i] = i * 2 - 3; + } + chromosome.setInputs(testInputs); + for (int i = 0; i < Parameters.getInputs(); i++) { + assertTrue("Incorrect input returned.", chromosome.getInput(i).getValue() == i * 2 - 3); + } + } + + /** + * + */ + @Test + public void getNodeTest() { + // get all nodes one by one, check that they are all correct + for (int r = 0; r < Parameters.getRows(); r++) { + for (int c = 0; c < Parameters.getColumns(); c++) { + assertTrue("Incorrect node returned.", chromosome.getNode(r, c).getColumn() == c && + chromosome.getNode(r, c).getRow() == r); + } + } + } + + /** + * + */ + @Test + public void activeNodeTest() { + + } } -- cgit v1.2.3