From 727801ab8002481fd2d213b45c3b43225b0f73bb Mon Sep 17 00:00:00 2001 From: Eduardo Pedroni Date: Wed, 12 Feb 2014 11:57:18 +0000 Subject: Added a few more tests, be back after the meeting. --- README | 9 + src/jcgp/function/Addition.java | 2 +- src/jcgp/function/Function.java | 3 +- .../function/InsufficientArgumentsException.java | 10 + src/jcgp/function/Subtraction.java | 2 +- src/jcgp/population/Output.java | 1 - src/jcgp/population/Population.java | 18 +- src/jcgp/tests/ChromosomeTests.java | 43 +++-- src/jcgp/tests/InputTests.java | 47 +++++ src/jcgp/tests/NodeTests.java | 212 ++++++++++++++++++++- src/jcgp/tests/OutputTests.java | 108 +++++++++++ src/jcgp/tests/PopulationTests.java | 94 +++++++++ 12 files changed, 513 insertions(+), 36 deletions(-) create mode 100644 src/jcgp/function/InsufficientArgumentsException.java create mode 100644 src/jcgp/tests/InputTests.java create mode 100644 src/jcgp/tests/OutputTests.java create mode 100644 src/jcgp/tests/PopulationTests.java diff --git a/README b/README index 40a2c5a..f08fe1b 100644 --- a/README +++ b/README @@ -107,3 +107,12 @@ acquiring node connections and copying them to a separate list, unless they are All chromosome tests have been implemented. + +12/2 + +Node tests done. Added exception support for Function in case not enough argument connections are given. +Chromosome tests refactored to include the special case where columns = 1. +Input and output tests written. + + + diff --git a/src/jcgp/function/Addition.java b/src/jcgp/function/Addition.java index faf11d5..45a8d35 100644 --- a/src/jcgp/function/Addition.java +++ b/src/jcgp/function/Addition.java @@ -9,7 +9,7 @@ public class Addition extends Function { if (connections.length > 0) { return connections[0].getValue() + connections[1].getValue(); } else { - return 0; + throw new InsufficientArgumentsException(); } } diff --git a/src/jcgp/function/Function.java b/src/jcgp/function/Function.java index f2d1125..4900c68 100644 --- a/src/jcgp/function/Function.java +++ b/src/jcgp/function/Function.java @@ -1,10 +1,11 @@ package jcgp.function; import jcgp.population.Connection; +import jcgp.population.InsufficientConnectionsException; public abstract class Function { - public abstract int run(Connection ... connections); + public abstract int run(Connection ... connections) throws InsufficientConnectionsException; public abstract int getArity(); diff --git a/src/jcgp/function/InsufficientArgumentsException.java b/src/jcgp/function/InsufficientArgumentsException.java new file mode 100644 index 0000000..a591b20 --- /dev/null +++ b/src/jcgp/function/InsufficientArgumentsException.java @@ -0,0 +1,10 @@ +package jcgp.function; + +public class InsufficientArgumentsException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = 2675108124600817777L; + +} diff --git a/src/jcgp/function/Subtraction.java b/src/jcgp/function/Subtraction.java index f8b9c7d..8f107b1 100644 --- a/src/jcgp/function/Subtraction.java +++ b/src/jcgp/function/Subtraction.java @@ -9,7 +9,7 @@ public class Subtraction extends Function { if (connections.length > 1) { return connections[0].getValue() - connections[1].getValue(); } else { - return 0; + throw new InsufficientArgumentsException(); } } diff --git a/src/jcgp/population/Output.java b/src/jcgp/population/Output.java index b3cb648..0171d7b 100644 --- a/src/jcgp/population/Output.java +++ b/src/jcgp/population/Output.java @@ -2,7 +2,6 @@ package jcgp.population; import java.util.ArrayList; - public class Output implements MutableElement { private Connection source; diff --git a/src/jcgp/population/Population.java b/src/jcgp/population/Population.java index fb968fb..56f1b50 100644 --- a/src/jcgp/population/Population.java +++ b/src/jcgp/population/Population.java @@ -8,15 +8,15 @@ public class Population implements Iterable { private Chromosome[] population; - public Population() { - population = new Chromosome[Parameters.getPopulationSize()]; - for (int c = 0; c < population.length; c++) { - population[c] = new Chromosome(); - } - } - public Population(Chromosome ... chromosomes) { - population = chromosomes; + if (chromosomes.length > 0) { + population = chromosomes; + } else { + population = new Chromosome[Parameters.getPopulationSize()]; + for (int c = 0; c < population.length; c++) { + population[c] = new Chromosome(); + } + } } @Override @@ -45,7 +45,7 @@ public class Population implements Iterable { public void remove() { // not allowed // since this would shift everything back one position, increment index - index++; + // index++; } }; diff --git a/src/jcgp/tests/ChromosomeTests.java b/src/jcgp/tests/ChromosomeTests.java index 3b7319f..cb2cb95 100644 --- a/src/jcgp/tests/ChromosomeTests.java +++ b/src/jcgp/tests/ChromosomeTests.java @@ -18,6 +18,7 @@ import jcgp.population.Node; import jcgp.population.Output; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; /** @@ -26,8 +27,8 @@ 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 allowed connection given a column - * - It should be able to return any random connection + * - It should be able to return a random allowed connection given a column. + * - It should be able to return a random connection. * - It should contain a freely modifiable fitness value. * - 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 @@ -36,6 +37,8 @@ import org.junit.Test; * specified Chromosome object. * - It should be able to return a list of active nodes. * + * TODO: bashing (strange value ranges, etc) + * * WARNING: changing parameters may cause the tests to incorrectly fail! * * @author Eduardo Pedroni @@ -45,8 +48,8 @@ public class ChromosomeTests { private Chromosome chromosome; - @Before - public void setUp() throws Exception { + @BeforeClass + public static void setUpBeforeClass() { // initialise function set FunctionSet functionSet = new FunctionSet(new Addition(), new Subtraction()); @@ -54,18 +57,20 @@ public class ChromosomeTests { Utilities.setResources(new Random(1234), functionSet); // initialise parameters - Parameters.setColumns(20); - Parameters.setRows(10); + Parameters.setColumns(1); + Parameters.setRows(20); Parameters.setInputs(2); Parameters.setOutputs(4); - Parameters.setLevelsBack(20); + Parameters.setLevelsBack(1); Parameters.setMutationRate(10); Parameters.setTotalGenerations(100); Parameters.setTotalRuns(5); Parameters.setMaxArity(functionSet.getMaxArity()); + } + @Before + public void setUp() throws Exception { chromosome = new Chromosome(); - } /** @@ -76,7 +81,7 @@ public class ChromosomeTests { 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++) { @@ -84,15 +89,15 @@ public class ChromosomeTests { } 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()); + 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++) { @@ -101,7 +106,7 @@ public class ChromosomeTests { clone.setInputs(testInputs); for (int i = 0; i < Parameters.getOutputs(); i++) { assertTrue("Incorrect output returned.", chromosome.getOutput(i).calculate() != - clone.getOutput(i).calculate()); + clone.getOutput(i).calculate()); } } @@ -126,10 +131,10 @@ public class ChromosomeTests { assertTrue("Connection is not an input.", connectionReturn); } - // get random connections with column 2 + // get random connections with column 1 // they should all be nodes, and their columns should be within range int connectionNodes = 0, connectionOutOfRange = 0, connectionInputs = 0, connectionPicks = 100000; - int chosenColumn = 2; + int chosenColumn = 1; for (int i = 0; i < connectionPicks; i++) { Connection c = chromosome.getRandomConnection(chosenColumn); if (c instanceof Node) { @@ -222,7 +227,7 @@ public class ChromosomeTests { } } } - + /** * */ @@ -233,12 +238,12 @@ public class ChromosomeTests { for (int i = 0; i < Parameters.getOutputs(); i++) { chromosome.getOutput(i).setConnection(chromosome.getNode(0, 0)); } - + chromosome.getNode(0, 0).setConnection(chromosome.getInput(0)); - + assertTrue("Active connection not in list.", chromosome.getActiveNodes().contains(chromosome.getInput(0))); assertTrue("Active connection not in list.", chromosome.getActiveNodes().contains(chromosome.getNode(0, 0))); - + // change outputs, print list chromosome.getOutput(0).setConnection(chromosome.getNode(0, Parameters.getColumns() - 1)); diff --git a/src/jcgp/tests/InputTests.java b/src/jcgp/tests/InputTests.java new file mode 100644 index 0000000..3803990 --- /dev/null +++ b/src/jcgp/tests/InputTests.java @@ -0,0 +1,47 @@ +package jcgp.tests; + +import static org.junit.Assert.assertTrue; +import jcgp.population.Input; + +import org.junit.Before; +import org.junit.Test; + +/** + * + * Tests which cover the behaviour specified for an input. + * + * - An input contains a single set value. This value can be freely set for + * fitness evaluation purposes. TODO this value should be generically typed. + * - It must be addressable by an index set upon construction only. + * + * + * @author Eduardo Pedroni + * + */ +public class InputTests { + + private Input input; + // these are the test values + private final int inputValue = 19; + private final int inputIndex = 12; + + @Before + public void setUp() throws Exception { + input = new Input(inputIndex); + } + + @Test + public void valueTest() { + // assign a value to input, check that the returned value is correct + input.setValue(inputValue); + + assertTrue("Incorrect value returned.", input.getValue() == inputValue); + } + + @Test + public void indexTest() { + // check that the index returned is the one passed to the constructor + assertTrue("Incorrect index returned.", input.getIndex() == inputIndex); + } + +} diff --git a/src/jcgp/tests/NodeTests.java b/src/jcgp/tests/NodeTests.java index 921879f..ecc87ff 100644 --- a/src/jcgp/tests/NodeTests.java +++ b/src/jcgp/tests/NodeTests.java @@ -1,8 +1,21 @@ package jcgp.tests; -import static org.junit.Assert.*; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Random; + +import jcgp.Parameters; +import jcgp.Utilities; +import jcgp.function.Function; +import jcgp.function.InsufficientArgumentsException; +import jcgp.population.Chromosome; +import jcgp.population.Connection; +import jcgp.population.InsufficientConnectionsException; +import jcgp.population.Node; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; /** @@ -12,7 +25,8 @@ import org.junit.Test; * - A node should contain read-only row and column values which are set upon construction. * - It should contain a fully accessible Function object. * - It should contain a set of connections which can be initialised and randomly - * modified. + * modified. It should be able to return an addressed connection. + * - It should be able to compute a value using its function with its connections as arguments. * * WARNING: changing parameters may cause the tests to incorrectly fail! * @@ -21,13 +35,203 @@ import org.junit.Test; */ public class NodeTests { + private Node node; + private static Chromosome chromosome; + // these numbers will be used by the node under test + private final int arg1 = 2; + private final int arg2 = 5; + + @BeforeClass + public static void setUpBeforeClass() { + // initialise utilities + Utilities.setResources(new Random(1234), null); + + // initialise parameters + Parameters.setColumns(0); + Parameters.setRows(0); + Parameters.setInputs(0); + Parameters.setOutputs(0); + Parameters.setLevelsBack(0); + Parameters.setMutationRate(10); + Parameters.setTotalGenerations(100); + Parameters.setTotalRuns(5); + Parameters.setMaxArity(2); + + chromosome = new Chromosome(); + } + @Before public void setUp() throws Exception { - + node = new Node(chromosome, 0, 0); + // make node with anonymous addition function and hard-coded value connections + node.initialise(new Function() { + private int arity = 2; + + @Override + public int run(Connection... connections) { + + // add together the first n inputs if they exist, else throw exception + if (connections.length >= arity) { + return connections[0].getValue() + connections[1].getValue(); + } else { + throw new InsufficientArgumentsException(); + } + } + + @Override + public int getArity() { + // addition with arity 2 + return arity; + } + + }, new Connection[]{new Connection() { + + @Override + public int getValue() { + // hardcode a value + return arg1; + } + + @Override + public void getActive(ArrayList activeNodes) { + // irrelevant for this test + } + + }, + new Connection() { + + @Override + public int getValue() { + // hardcode a value + return arg2; + } + + @Override + public void getActive(ArrayList activeNodes) { + // irrelevant for this test + } + + }}); + } + + @Test + public void rowAndColumnTest() { + assertTrue("Incorrect row.", node.getRow() == 0); + assertTrue("Incorrect column.", node.getColumn() == 0); } @Test - public void test() { + public void functionTest() { + // make a new function and assign to node + Function f = new Function() { + + @Override + public int run(Connection... connections) + throws InsufficientConnectionsException { + // blank + return 0; + } + + @Override + public int getArity() { + // blank + return 0; + } + }; + + node.setFunction(f); + + // check that the function returned by the node is f + assertTrue("Incorrect function returned.", node.getFunction() == f); + // check that it outputs 0 as it should + assertTrue("Incorrect function output.", node.getValue() == 0); + } + + @Test + public void evaluationTest() { + // check that addition is working + assertTrue("Node did not return expected value (sum of arguments).", node.getValue() == arg1 + arg2); + + // put in a different function, check the output has changed appropriately + node.setFunction(new Function() { + + private int arity = 2; + + @Override + public int run(Connection... connections) throws InsufficientConnectionsException { + // add together the first n inputs if they exist, else throw exception + if (connections.length >= arity) { + return connections[0].getValue() - connections[1].getValue(); + } else { + throw new InsufficientArgumentsException(); + } + } + + @Override + public int getArity() { + return arity; + } + }); + + assertTrue("Node did not return expected value (difference of arguments).", node.getValue() == arg1 - arg2); + + } + + @Test + public void connectionsTest() { + // make new blank connections, check that they are returned correctly when addressed + Connection conn0, conn1, conn2; + conn0 = new Connection() { + + @Override + public int getValue() { + // blank + return 0; + } + + @Override + public void getActive(ArrayList activeNodes) { + // blank + + } + }; + conn1 = new Connection() { + + @Override + public int getValue() { + // blank + return 0; + } + + @Override + public void getActive(ArrayList activeNodes) { + // blank + + } + }; + node.initialise(null, conn0, conn1); + + assertTrue("Connection 0 is incorrect.", node.getConnection(0) == conn0); + assertTrue("Connection 1 is incorrect.", node.getConnection(1) == conn1); + + // make yet another connection, set it randomly, check that it is one of the node's connections + conn2 = new Connection() { + + @Override + public int getValue() { + // blank + return 0; + } + + @Override + public void getActive(ArrayList activeNodes) { + // blank + + } + }; + node.setConnection(conn2); + + assertTrue("Connection was not found in node.", node.getConnection(0) == conn2 || node.getConnection(1) == conn2); } diff --git a/src/jcgp/tests/OutputTests.java b/src/jcgp/tests/OutputTests.java new file mode 100644 index 0000000..06295ae --- /dev/null +++ b/src/jcgp/tests/OutputTests.java @@ -0,0 +1,108 @@ +package jcgp.tests; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Random; + +import jcgp.Parameters; +import jcgp.Utilities; +import jcgp.population.Chromosome; +import jcgp.population.Connection; +import jcgp.population.Output; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * + * Tests which cover the behaviour specified for an output. + * + * - An output contains a single source Connection, which can be set and got. + * - It should return the value of its source connection. + * - It must be addressable by an index set upon construction only. + * + * + * @author Eduardo Pedroni + * + */ +public class OutputTests { + + private Output output; + private static Chromosome chromosome; + // these are the test values + private final int outputValue = 10; + private final int outputIndex = 2; + + @BeforeClass + public static void setUpBeforeClass() { + // initialise utilities + Utilities.setResources(new Random(1234), null); + + // initialise parameters + Parameters.setColumns(0); + Parameters.setRows(0); + Parameters.setInputs(0); + Parameters.setOutputs(0); + Parameters.setLevelsBack(0); + Parameters.setMutationRate(10); + Parameters.setTotalGenerations(100); + Parameters.setTotalRuns(5); + Parameters.setMaxArity(2); + + chromosome = new Chromosome(); + } + + @Before + public void setUp() throws Exception { + output = new Output(chromosome, outputIndex); + } + + @Test + public void evaluationsTest() { + // set source connection, check that the appropriate value is returned + output.setConnection(new Connection() { + + @Override + public int getValue() { + // test value + return outputValue; + } + + @Override + public void getActive(ArrayList activeNodes) { + // blank + } + }); + + assertTrue("Incorrect evaluation.", output.calculate() == outputValue); + } + + @Test + public void connectionTest() { + // set a new connection, check that it is correctly returned + Connection newConn = new Connection() { + + @Override + public int getValue() { + // blank + return 0; + } + + @Override + public void getActive(ArrayList activeNodes) { + // blank + } + }; + output.setConnection(newConn); + + assertTrue("Incorrect connection returned.", output.getSource() == newConn); + } + + @Test + public void indexTest() { + // check that the index returned is the one passed to the constructor + assertTrue("Incorrect index returned.", output.getIndex() == outputIndex); + } +} diff --git a/src/jcgp/tests/PopulationTests.java b/src/jcgp/tests/PopulationTests.java new file mode 100644 index 0000000..16a8d89 --- /dev/null +++ b/src/jcgp/tests/PopulationTests.java @@ -0,0 +1,94 @@ +package jcgp.tests; + +import static org.junit.Assert.*; + +import java.util.Random; + +import jcgp.Parameters; +import jcgp.Utilities; +import jcgp.function.Addition; +import jcgp.function.FunctionSet; +import jcgp.function.Subtraction; +import jcgp.population.Chromosome; +import jcgp.population.Population; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * + * Tests which cover the behaviour specified for a population. + * + * - A population is a collection of chromosomes. It should be Iterable + * so the chromosomes can be accessed. Chromosomes should never be removed + * via the iterator. + * - When constructed with no arguments, it should generate populationSize + * random chromosomes. + * - If an array of chromosomes is passed as an argument to the constructor, + * it should use those chromosomes as the population. + * + * + * @author Eduardo Pedroni + * + */ +public class PopulationTests { + + private Population population; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + // initialise function set + FunctionSet functionSet = new FunctionSet(new Addition(), new Subtraction()); + + // initialise utilities + Utilities.setResources(new Random(1234), functionSet); + + // initialise parameters + Parameters.setColumns(20); + Parameters.setRows(20); + Parameters.setInputs(2); + Parameters.setOutputs(4); + Parameters.setLevelsBack(1); + Parameters.setMutationRate(10); + Parameters.setTotalGenerations(100); + Parameters.setTotalRuns(5); + Parameters.setPopulationSize(5); + Parameters.setMaxArity(functionSet.getMaxArity()); + } + + @Before + public void setUp() throws Exception { + population = new Population(); + } + + @Test + public void iterableTest() { + // check that Population is really Iterable + assertTrue("Population must implement Iterable.", population instanceof Iterable); + + // iterate through, check that different chromosomes are returned and no chromosome can be removed + Chromosome comparison = null; + int iterationCount = 0; + for (Chromosome chromosome : population) { + iterationCount++; + assertTrue("Same chromosome returned twice.", comparison != chromosome); + comparison = chromosome; + population.iterator().remove(); + assertTrue("Chromosome removed.", comparison == chromosome); + } + // check that all chromosomes were iterated through + assertTrue("Not enough itearations occurred.", iterationCount == Parameters.getPopulationSize()); + } + + @Test + public void contentsTest() { + // check that the constructor really generates populationSize chromosomes when none is given + int populationCount = 0; + for (Chromosome chromosome : population) { + populationCount++; + } + assertTrue("Incorrect number of chromosomes generated.", populationCount == Parameters.getPopulationSize()); + } + +} -- cgit v1.2.3