diff options
28 files changed, 634 insertions, 477 deletions
diff --git a/src/epar3.plu b/src/epar3.plu new file mode 100644 index 0000000..b554b66 --- /dev/null +++ b/src/epar3.plu @@ -0,0 +1,5 @@ +.i 3
+.o 1
+.p 1
+240 204 170 105
+.e
\ No newline at end of file diff --git a/src/jcgp/JCGP.java b/src/jcgp/JCGP.java index 1e847fb..f949944 100644 --- a/src/jcgp/JCGP.java +++ b/src/jcgp/JCGP.java @@ -7,9 +7,9 @@ import jcgp.backend.modules.es.MuPlusLambda; import jcgp.backend.modules.es.TournamentSelection; import jcgp.backend.modules.mutator.Mutator; import jcgp.backend.modules.mutator.PointMutator; -import jcgp.backend.modules.problem.DigitalCircuit; +import jcgp.backend.modules.problem.DigitalCircuitProblem; import jcgp.backend.modules.problem.Problem; -import jcgp.backend.modules.problem.SymbolicRegression; +import jcgp.backend.modules.problem.SymbolicRegressionProblem; import jcgp.backend.modules.problem.TestCaseProblem; import jcgp.backend.parser.ChromosomeParser; import jcgp.backend.parser.FunctionParser; @@ -60,8 +60,8 @@ public class JCGP { // problem types private Problem[] problems = new Problem[] { - new SymbolicRegression(resources), - new DigitalCircuit(resources) }; + new SymbolicRegressionProblem(resources), + new DigitalCircuitProblem(resources) }; private Problem problem; /* @@ -84,7 +84,7 @@ public class JCGP { jcgp.loadParameters(new File(args[0])); if (jcgp.getProblem() instanceof TestCaseProblem) { - TestCaseParser.parse(new File(args[2]), (TestCaseProblem<?>) jcgp.getProblem()); + ((TestCaseProblem<?>) jcgp.getProblem()).parse(new File(args[2])); } jcgp.start(); @@ -230,7 +230,8 @@ public class JCGP { private void reportGeneration() { if (resources.reportInterval() > 0) { if (resources.currentGeneration() % resources.reportInterval() == 0) { - resources.println("[CGP] Generation: " + resources.currentGeneration()); + resources.println("[CGP] Generation: " + resources.currentGeneration() + ", fittest chromosome (" + + population.getFittestIndex() + ") has fitness: " + population.getFittest().getFitness()); } } } @@ -260,7 +261,9 @@ public class JCGP { public void loadTestCases(File file) { if (problem instanceof TestCaseProblem) { - TestCaseParser.parse(file, (TestCaseProblem<?>) problem); + TestCaseParser.parseParameters(file, resources); + reset(); + ((TestCaseProblem<?>) problem).parse(file); } } diff --git a/src/jcgp/backend/function/BitwiseLogic.java b/src/jcgp/backend/function/BitwiseLogic.java index 14f4488..a4c2737 100644 --- a/src/jcgp/backend/function/BitwiseLogic.java +++ b/src/jcgp/backend/function/BitwiseLogic.java @@ -31,8 +31,8 @@ public class BitwiseLogic extends FunctionSet { public static class ConstantZero extends Function { @Override - public Long run(Connection... connections) { - return new Long(0); + public UnsignedInteger run(Connection... connections) { + return new UnsignedInteger(0); } @Override @@ -48,8 +48,8 @@ public class BitwiseLogic extends FunctionSet { public static class ConstantOne extends Function { @Override - public Long run(Connection... connections) { - return new Long(Long.MAX_VALUE); + public UnsignedInteger run(Connection... connections) { + return new UnsignedInteger(0xFFFF); } @Override @@ -65,11 +65,11 @@ public class BitwiseLogic extends FunctionSet { public static class WireA extends Function { @Override - public Long run(Connection... connections) { + public UnsignedInteger run(Connection... connections) { if (connections.length < getArity()) { throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); } else { - return ((Long) connections[0].getValue()).longValue(); + return ((UnsignedInteger) connections[0].getValue()); } } @@ -86,11 +86,11 @@ public class BitwiseLogic extends FunctionSet { public static class WireB extends Function { @Override - public Long run(Connection... connections) { + public UnsignedInteger run(Connection... connections) { if (connections.length < getArity()) { throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); } else { - return ((Long) connections[1].getValue()).longValue(); + return ((UnsignedInteger) connections[1].getValue()); } } @@ -107,11 +107,11 @@ public class BitwiseLogic extends FunctionSet { public static class NotA extends Function { @Override - public Long run(Connection... connections) { + public UnsignedInteger run(Connection... connections) { if (connections.length < getArity()) { throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); } else { - return ~((Long) connections[0].getValue()).longValue(); + return new UnsignedInteger(~((UnsignedInteger) connections[0].getValue()).get()); } } @@ -128,11 +128,11 @@ public class BitwiseLogic extends FunctionSet { public static class NotB extends Function { @Override - public Long run(Connection... connections) { + public UnsignedInteger run(Connection... connections) { if (connections.length < getArity()) { throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); } else { - return ~((Long) connections[1].getValue()).longValue(); + return new UnsignedInteger(~((UnsignedInteger) connections[1].getValue()).get()); } } @@ -149,15 +149,15 @@ public class BitwiseLogic extends FunctionSet { public static class And extends Function { @Override - public Long run(Connection... connections) { + public UnsignedInteger run(Connection... connections) { if (connections.length < getArity()) { throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); } else { - Long arg1 = ((Long) connections[0].getValue()); - Long arg2 = ((Long) connections[1].getValue()); - Long result = arg1 & arg2; + UnsignedInteger arg1 = ((UnsignedInteger) connections[0].getValue()); + UnsignedInteger arg2 = ((UnsignedInteger) connections[1].getValue()); + Integer result = arg1.get() & arg2.get(); - return result; + return new UnsignedInteger(result); } } @@ -174,15 +174,15 @@ public class BitwiseLogic extends FunctionSet { public static class AndNotA extends Function { @Override - public Long run(Connection... connections) { + public UnsignedInteger run(Connection... connections) { if (connections.length < getArity()) { throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); } else { - Long arg1 = ((Long) connections[0].getValue()); - Long arg2 = ((Long) connections[1].getValue()); - Long result = ~arg1 & arg2; + UnsignedInteger arg1 = ((UnsignedInteger) connections[0].getValue()); + UnsignedInteger arg2 = ((UnsignedInteger) connections[1].getValue()); + Integer result = ~(arg1.get()) & arg2.get(); - return result; + return new UnsignedInteger(result); } } @@ -199,15 +199,15 @@ public class BitwiseLogic extends FunctionSet { public static class AndNotB extends Function { @Override - public Long run(Connection... connections) { + public UnsignedInteger run(Connection... connections) { if (connections.length < getArity()) { throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); } else { - Long arg1 = ((Long) connections[0].getValue()); - Long arg2 = ((Long) connections[1].getValue()); - Long result = arg1 & ~arg2; + UnsignedInteger arg1 = ((UnsignedInteger) connections[0].getValue()); + UnsignedInteger arg2 = ((UnsignedInteger) connections[1].getValue()); + Integer result = arg1.get() & ~(arg2.get()); - return result; + return new UnsignedInteger(result); } } @@ -224,15 +224,15 @@ public class BitwiseLogic extends FunctionSet { public static class Nor extends Function { @Override - public Long run(Connection... connections) { + public UnsignedInteger run(Connection... connections) { if (connections.length < getArity()) { throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); } else { - Long arg1 = ((Long) connections[0].getValue()); - Long arg2 = ((Long) connections[1].getValue()); - Long result = arg1 | arg2; + UnsignedInteger arg1 = ((UnsignedInteger) connections[0].getValue()); + UnsignedInteger arg2 = ((UnsignedInteger) connections[1].getValue()); + Integer result = arg1.get() | arg2.get(); - return ~result; + return new UnsignedInteger(~result); } } @@ -249,15 +249,15 @@ public class BitwiseLogic extends FunctionSet { public static class Xor extends Function { @Override - public Long run(Connection... connections) { + public UnsignedInteger run(Connection... connections) { if (connections.length < getArity()) { throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); } else { - Long arg1 = ((Long) connections[0].getValue()); - Long arg2 = ((Long) connections[1].getValue()); - Long result = arg1 ^ arg2; + UnsignedInteger arg1 = ((UnsignedInteger) connections[0].getValue()); + UnsignedInteger arg2 = ((UnsignedInteger) connections[1].getValue()); + Integer result = arg1.get() ^ arg2.get(); - return result; + return new UnsignedInteger(result); } } @@ -274,15 +274,15 @@ public class BitwiseLogic extends FunctionSet { public static class Xnor extends Function { @Override - public Long run(Connection... connections) { + public UnsignedInteger run(Connection... connections) { if (connections.length < getArity()) { throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); } else { - Long arg1 = ((Long) connections[0].getValue()); - Long arg2 = ((Long) connections[1].getValue()); - Long result = arg1 ^ arg2; + UnsignedInteger arg1 = ((UnsignedInteger) connections[0].getValue()); + UnsignedInteger arg2 = ((UnsignedInteger) connections[1].getValue()); + Integer result = arg1.get() ^ arg2.get(); - return ~result; + return new UnsignedInteger(~result); } } @@ -299,15 +299,15 @@ public class BitwiseLogic extends FunctionSet { public static class Or extends Function { @Override - public Long run(Connection... connections) { + public UnsignedInteger run(Connection... connections) { if (connections.length < getArity()) { throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); } else { - Long arg1 = ((Long) connections[0].getValue()); - Long arg2 = ((Long) connections[1].getValue()); - Long result = arg1 | arg2; + UnsignedInteger arg1 = ((UnsignedInteger) connections[0].getValue()); + UnsignedInteger arg2 = ((UnsignedInteger) connections[1].getValue()); + Integer result = arg1.get() | arg2.get(); - return result; + return new UnsignedInteger(result); } } @@ -324,15 +324,15 @@ public class BitwiseLogic extends FunctionSet { public static class OrNotA extends Function { @Override - public Long run(Connection... connections) { + public UnsignedInteger run(Connection... connections) { if (connections.length < getArity()) { throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); } else { - Long arg1 = ((Long) connections[0].getValue()); - Long arg2 = ((Long) connections[1].getValue()); - Long result = ~arg1 | arg2; + UnsignedInteger arg1 = ((UnsignedInteger) connections[0].getValue()); + UnsignedInteger arg2 = ((UnsignedInteger) connections[1].getValue()); + Integer result = ~arg1.get() | arg2.get(); - return result; + return new UnsignedInteger(result); } } @@ -349,15 +349,15 @@ public class BitwiseLogic extends FunctionSet { public static class OrNotB extends Function { @Override - public Long run(Connection... connections) { + public UnsignedInteger run(Connection... connections) { if (connections.length < getArity()) { throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); } else { - Long arg1 = ((Long) connections[0].getValue()); - Long arg2 = ((Long) connections[1].getValue()); - Long result = arg1 | ~arg2; + UnsignedInteger arg1 = ((UnsignedInteger) connections[0].getValue()); + UnsignedInteger arg2 = ((UnsignedInteger) connections[1].getValue()); + Integer result = arg1.get() | ~arg2.get(); - return result; + return new UnsignedInteger(result); } } @@ -374,15 +374,15 @@ public class BitwiseLogic extends FunctionSet { public static class Nand extends Function { @Override - public Long run(Connection... connections) { + public UnsignedInteger run(Connection... connections) { if (connections.length < getArity()) { throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); } else { - Long arg1 = ((Long) connections[0].getValue()); - Long arg2 = ((Long) connections[1].getValue()); - Long result = arg1 & arg2; + UnsignedInteger arg1 = ((UnsignedInteger) connections[0].getValue()); + UnsignedInteger arg2 = ((UnsignedInteger) connections[1].getValue()); + Integer result = arg1.get() & arg2.get(); - return ~result; + return new UnsignedInteger(~result); } } diff --git a/src/jcgp/backend/function/BooleanLogic.java b/src/jcgp/backend/function/BooleanLogic.java deleted file mode 100644 index 9e7d1ff..0000000 --- a/src/jcgp/backend/function/BooleanLogic.java +++ /dev/null @@ -1,199 +0,0 @@ -package jcgp.backend.function; - - -import jcgp.backend.exceptions.InvalidArgumentsException; -import jcgp.backend.population.Connection; - -public class BooleanLogic extends FunctionSet { - - public BooleanLogic() { - name = "1-bit Logic"; - functionList = new Function[]{ - new And(), - new Or(), - new Not(), - new Xor(), - new Nand(), - new Nor(), - new Xnor()}; - - enableAll(); - } - - public static class And extends Function { - @Override - public Boolean run(Connection... connections) { - if (connections.length < 2) { - throw new InvalidArgumentsException("Not enough connections were given."); - } else { - Boolean arg1 = ((Boolean) connections[0].getValue()); - Boolean arg2 = ((Boolean) connections[1].getValue()); - Boolean result = arg1 && arg2; - - return result; - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "AND"; - } - } - - public static class Or extends Function { - @Override - public Boolean run(Connection... connections) { - if (connections.length < 2) { - throw new InvalidArgumentsException("Not enough connections were given."); - } else { - Boolean arg1 = ((Boolean) connections[0].getValue()); - Boolean arg2 = ((Boolean) connections[1].getValue()); - Boolean result = arg1 || arg2; - - return result; - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "OR"; - } - } - - public static class Not extends Function { - @Override - public Boolean run(Connection... connections) { - if (connections.length < 1) { - throw new InvalidArgumentsException("Not enough connections were given."); - } else { - Boolean arg1 = ((Boolean) connections[0].getValue()); - Boolean result = !arg1; - - return result; - } - } - - @Override - public int getArity() { - return 1; - } - - @Override - public String getName() { - return "NOT"; - } - } - - public static class Xor extends Function { - @Override - public Boolean run(Connection... connections) { - if (connections.length < 2) { - throw new InvalidArgumentsException("Not enough connections were given."); - } else { - Boolean arg1 = ((Boolean) connections[0].getValue()); - Boolean arg2 = ((Boolean) connections[1].getValue()); - Boolean result = arg1 ^ arg2; - - return result; - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "XOR"; - } - } - - public static class Nand extends Function { - @Override - public Boolean run(Connection... connections) { - if (connections.length < 2) { - throw new InvalidArgumentsException("Not enough connections were given."); - } else { - Boolean arg1 = ((Boolean) connections[0].getValue()); - Boolean arg2 = ((Boolean) connections[1].getValue()); - Boolean result = arg1 && arg2; - - return !result; - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "NAND"; - } - } - - public static class Nor extends Function { - @Override - public Boolean run(Connection... connections) { - if (connections.length < 2) { - throw new InvalidArgumentsException("Not enough connections were given."); - } else { - Boolean arg1 = ((Boolean) connections[0].getValue()); - Boolean arg2 = ((Boolean) connections[1].getValue()); - Boolean result = arg1 || arg2; - - return !result; - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "NOR"; - } - } - - public static class Xnor extends Function { - @Override - public Boolean run(Connection... connections) { - if (connections.length < 2) { - throw new InvalidArgumentsException("Not enough connections were given."); - } else { - Boolean arg1 = ((Boolean) connections[0].getValue()); - Boolean arg2 = ((Boolean) connections[1].getValue()); - Boolean result = arg1 ^ arg2; - - return !result; - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "XNOR"; - } - } - - - -} diff --git a/src/jcgp/backend/function/UnsignedInteger.java b/src/jcgp/backend/function/UnsignedInteger.java new file mode 100644 index 0000000..7feb33f --- /dev/null +++ b/src/jcgp/backend/function/UnsignedInteger.java @@ -0,0 +1,66 @@ +package jcgp.backend.function; + +/** + * Integer wrapper type for unsigned integer values. + * <br><br> + * Java offers no support for unsigned types save from + * unsigned conversion methods. This class uses those methods + * to simulate the unsigned int data type, useful for circuit + * truth table encodings. + * <br><br> + * When a string representation of an unsigned integer is parsed + * using Integer.parseUnsignedInt(), an Integer is created using + * all 32 bits for unsigned magnitude. The integer however is still + * signed and will behave as such for all arithmetic operations. + * Bitwise operations can still be performed as they work at the bit + * level, making this data type particularly suitable for circuit design. + * + * + * @author Eduardo Pedroni + * @see Integer + * + */ +public class UnsignedInteger { + + private Integer value; + + /** + * Makes a new instance of UnsignedInteger with a specified value. + * + * @param i the value with which to initialise + */ + public UnsignedInteger(int i) { + value = new Integer(i); + } + + /** + * Makes a new instance of UnsignedInteger with a specified value. + * + * @param i the value with which to initialise + */ + public UnsignedInteger(Integer i) { + value = i; + } + + /** + * Makes a new instance of UnsignedInteger from the string representation + * of an unsigned integer. + * + * @param i the string with which to initialise + */ + public UnsignedInteger(String i) { + value = Integer.parseUnsignedInt(i); + } + + /** + * @return the wrapped Integer object + */ + public Integer get() { + return value; + } + + @Override + public String toString() { + return Integer.toUnsignedString(value); + } +} diff --git a/src/jcgp/backend/modules/mutator/PointMutator.java b/src/jcgp/backend/modules/mutator/PointMutator.java index ab8efad..44c453a 100644 --- a/src/jcgp/backend/modules/mutator/PointMutator.java +++ b/src/jcgp/backend/modules/mutator/PointMutator.java @@ -7,18 +7,21 @@ import jcgp.backend.population.Output; import jcgp.backend.resources.Resources; import jcgp.backend.resources.parameters.BooleanParameter; import jcgp.backend.resources.parameters.DoubleParameter; +import jcgp.backend.resources.parameters.IntegerParameter; import jcgp.backend.resources.parameters.Parameter; import jcgp.backend.resources.parameters.ParameterStatus; public class PointMutator implements Mutator { private DoubleParameter mutationRate; + private IntegerParameter nodesMutated; private BooleanParameter report; public PointMutator(final Resources resources) { mutationRate = new DoubleParameter(50, "Percent mutation", false, false) { @Override public void validate(Number newValue) { + nodesMutated.set((int) ((newValue.intValue()) * (((((double) resources.nodes() + resources.outputs()))) / 100))); if (newValue.doubleValue() <= 0 || newValue.doubleValue() > 100) { status = ParameterStatus.INVALID; status.setDetails("Mutation rate must be > 0 and <= 100"); @@ -30,7 +33,12 @@ public class PointMutator implements Mutator { } } }; - + nodesMutated = new IntegerParameter(0, "Genes mutated", true, false) { + @Override + public void validate(Number newValue) { + // blank + } + }; report = new BooleanParameter(false, "Report") { @Override public void validate(Boolean newValue) { @@ -41,9 +49,8 @@ public class PointMutator implements Mutator { @Override public void mutate(Chromosome chromosome, Resources resources) { - int mutations = (int) ((mutationRate.get()) * (((((double) resources.nodes() + resources.outputs()))) / 100)); - if (report.get()) resources.reportln("[Mutator] Number of mutations to be performed: " + mutations); - for (int i = 0; i < mutations; i++) { + if (report.get()) resources.reportln("[Mutator] Number of mutations to be performed: " + nodesMutated.get()); + for (int i = 0; i < nodesMutated.get(); i++) { MutableElement m = chromosome.getRandomMutableElement(); if (report.get()) resources.report("[Mutator] Mutation " + i + " selected " + m.toString() + ", "); @@ -75,7 +82,7 @@ public class PointMutator implements Mutator { @Override public Parameter<?>[] getLocalParameters() { - return new Parameter[] {mutationRate, report}; + return new Parameter[] {mutationRate, nodesMutated, report}; } @Override diff --git a/src/jcgp/backend/modules/problem/DigitalCircuit.java b/src/jcgp/backend/modules/problem/DigitalCircuit.java deleted file mode 100644 index d94197d..0000000 --- a/src/jcgp/backend/modules/problem/DigitalCircuit.java +++ /dev/null @@ -1,25 +0,0 @@ -package jcgp.backend.modules.problem; - -import java.util.ArrayList; - -import jcgp.backend.function.BitwiseLogic; -import jcgp.backend.resources.Resources; - -public class DigitalCircuit extends TestCaseProblem<Integer> { - - public DigitalCircuit(Resources resources) { - super(resources); - functionSet = new BitwiseLogic(); - - ArrayList<TestCase<Integer>> tc = new ArrayList<TestCase<Integer>>(); - tc.add(new TestCase<Integer>(new Integer[]{1, 2, 3}, new Integer[]{-4, 5, 6})); - - setTestCases(tc); - } - - @Override - public String toString() { - return "Digital circuit"; - } - -} diff --git a/src/jcgp/backend/modules/problem/DigitalCircuitProblem.java b/src/jcgp/backend/modules/problem/DigitalCircuitProblem.java new file mode 100644 index 0000000..e92989e --- /dev/null +++ b/src/jcgp/backend/modules/problem/DigitalCircuitProblem.java @@ -0,0 +1,85 @@ +package jcgp.backend.modules.problem; + +import jcgp.backend.function.BitwiseLogic; +import jcgp.backend.function.UnsignedInteger; +import jcgp.backend.population.Chromosome; +import jcgp.backend.population.Population; +import jcgp.backend.resources.Resources; + +public class DigitalCircuitProblem extends TestCaseProblem<UnsignedInteger> { + + public DigitalCircuitProblem(Resources resources) { + super(resources); + functionSet = new BitwiseLogic(); + } + + @Override + public void evaluate(Population population, Resources resources) { + // set fittest to 0, change it whenever a fitter one is found + population.setFittest(0); + + // for every chromosome in the population + for (int i = 0; i < resources.populationSize(); i++) { + // assume an initial fitness of 0 + int fitness = 0; + + // iterate over every test case + for (int t = 0; t < testCases.size(); t++) { + population.getChromosome(i).setInputs((Object[]) testCases.get(t).getInputs()); + // check each output + for (int o = 0; o < resources.outputs(); o++) { + Integer output = ((UnsignedInteger) population.getChromosome(i).getOutput(o).calculate()).get(); + Integer matches = ~(output ^ testCases.get(t).getOutput(o).get()); + // check only the relevant bits + int bits = (int) Math.pow(2.0, (double) resources.inputs()); + for (int b = 0; b < bits; b++) { + if (((matches >>> b) & 1) == 1) { + fitness++; + } + } + } + } + + // assign the resulting fitness to the respective individual + population.getChromosome(i).setFitness(fitness); + if (fitness >= population.getFittest().getFitness()) { + population.setFittest(i); + } + } + } + + @Override + protected int getMaxFitness() { + int maxFitness = (int) Math.pow(2.0, (double) resources.inputs()) * resources.outputs(); + return maxFitness; + } + + @Override + public String toString() { + return "Digital circuit"; + } + + @Override + public void addTestCase(String[] inputs, String[] outputs) { + UnsignedInteger[] inputCases = new UnsignedInteger[inputs.length]; + UnsignedInteger[] outputCases = new UnsignedInteger[outputs.length]; + for (int i = 0; i < inputCases.length; i++) { + inputCases[i] = new UnsignedInteger(inputs[i]); + } + for (int o = 0; o < outputCases.length; o++) { + outputCases[o] = new UnsignedInteger(outputs[o]); + } + + addTestCase(new TestCase<UnsignedInteger>(inputCases, outputCases)); + } + + @Override + public boolean isPerfectSolution(Chromosome fittest) { + return fittest.getFitness() >= maxFitness.get(); + } + + @Override + public String getFileExtension() { + return ".plu"; + } +} diff --git a/src/jcgp/backend/modules/problem/SymbolicRegression.java b/src/jcgp/backend/modules/problem/SymbolicRegression.java deleted file mode 100644 index 46b8e09..0000000 --- a/src/jcgp/backend/modules/problem/SymbolicRegression.java +++ /dev/null @@ -1,26 +0,0 @@ -package jcgp.backend.modules.problem; - -import java.util.ArrayList; - -import jcgp.backend.function.DoubleArithmetic; -import jcgp.backend.resources.Resources; - -public class SymbolicRegression extends TestCaseProblem<Double> { - - public SymbolicRegression(Resources resources) { - super(resources); - functionSet = new DoubleArithmetic(); - - ArrayList<TestCase<Double>> tc = new ArrayList<TestCase<Double>>(); - tc.add(new TestCase<Double>(new Double[]{1.0, 2.0, 3.0}, new Double[]{-4.0, 5.0, 6.0})); - tc.add(new TestCase<Double>(new Double[]{3.0, 2.0, 5.0}, new Double[]{2.0, 5.0, 9.0})); - - setTestCases(tc); - } - - @Override - public String toString() { - return "Symbolic regression"; - } - -} diff --git a/src/jcgp/backend/modules/problem/SymbolicRegressionProblem.java b/src/jcgp/backend/modules/problem/SymbolicRegressionProblem.java new file mode 100644 index 0000000..52df0f2 --- /dev/null +++ b/src/jcgp/backend/modules/problem/SymbolicRegressionProblem.java @@ -0,0 +1,124 @@ +package jcgp.backend.modules.problem; + +import jcgp.backend.function.DoubleArithmetic; +import jcgp.backend.population.Chromosome; +import jcgp.backend.population.Population; +import jcgp.backend.resources.Resources; +import jcgp.backend.resources.parameters.BooleanParameter; +import jcgp.backend.resources.parameters.DoubleParameter; +import jcgp.backend.resources.parameters.Parameter; +import jcgp.backend.resources.parameters.ParameterStatus; + +public class SymbolicRegressionProblem extends TestCaseProblem<Double> { + + private DoubleParameter errorThreshold, perfectionThreshold; + private BooleanParameter hitsBasedFitness; + + public SymbolicRegressionProblem(Resources resources) { + super(resources); + functionSet = new DoubleArithmetic(); + errorThreshold = new DoubleParameter(0.01, "Error threshold") { + @Override + public void validate(Number newValue) { + if (newValue.doubleValue() < 0) { + status = ParameterStatus.INVALID; + status.setDetails("Error threshold must be a positive value."); + } else if (newValue.doubleValue() == 0) { + status = ParameterStatus.WARNING; + status.setDetails("An error threshold of 0 is very rigorous and difficult to achieve."); + } else { + status = ParameterStatus.VALID; + } + } + }; + perfectionThreshold = new DoubleParameter(0.000001, "Perfection threshold") { + @Override + public void validate(Number newValue) { + if (newValue.doubleValue() < 0) { + status = ParameterStatus.INVALID; + status.setDetails("Perfection threshold must be a positive value."); + } else if (newValue.doubleValue() == 0) { + status = ParameterStatus.WARNING; + status.setDetails("A perfection threshold of 0 is very rigorous and difficult to achieve."); + } else { + status = ParameterStatus.VALID; + } + } + }; + hitsBasedFitness = new BooleanParameter(true, "Hits-based fitness") { + @Override + public void validate(Boolean newValue) { + // blank + } + }; + } + + @Override + public void evaluate(Population population, Resources resources) { + // set fittest to 0, change it whenever a fitter one is found + population.setFittest(0); + + // for every chromosome in the population + for (int i = 0; i < resources.populationSize(); i++) { + // assume an initial fitness of 0 + double fitness = 0; + // for each test case + for (int t = 0; t < testCases.size(); t++) { + population.getChromosome(i).setInputs((Object[]) testCases.get(t).getInputs()); + // check each output + for (int o = 0; o < resources.outputs(); o++) { + Double cgpValue = (Double) population.getChromosome(i).getOutput(o).calculate(); + Double dataValue = testCases.get(t).getOutput(o); + if (hitsBasedFitness.get()) { + if (Math.abs(cgpValue - dataValue) <= errorThreshold.get()) { + fitness++; + } + } else { + fitness += 1 - Math.abs(cgpValue - dataValue); + } + + } + } + // assign the resulting fitness to the respective individual + population.getChromosome(i).setFitness(fitness); + if (fitness >= population.getFittest().getFitness()) { + population.setFittest(i); + } + } + } + + + @Override + public String toString() { + return "Symbolic regression"; + } + + @Override + public void addTestCase(String[] inputs, String[] outputs) { + Double[] inputCases = new Double[inputs.length]; + Double[] outputCases = new Double[outputs.length]; + for (int i = 0; i < inputCases.length; i++) { + inputCases[i] = Double.parseDouble(inputs[i]); + } + for (int o = 0; o < outputCases.length; o++) { + outputCases[o] = Double.parseDouble(outputs[o]); + } + + addTestCase(new TestCase<Double>(inputCases, outputCases)); + } + + @Override + public boolean isPerfectSolution(Chromosome fittest) { + return fittest.getFitness() >= maxFitness.get() - perfectionThreshold.get(); + } + + @Override + public Parameter<?>[] getLocalParameters() { + return new Parameter[]{maxFitness, errorThreshold, perfectionThreshold, hitsBasedFitness}; + } + + @Override + public String getFileExtension() { + return ".dat"; + } +} diff --git a/src/jcgp/backend/modules/problem/TestCaseProblem.java b/src/jcgp/backend/modules/problem/TestCaseProblem.java index ee72860..ff13c2e 100644 --- a/src/jcgp/backend/modules/problem/TestCaseProblem.java +++ b/src/jcgp/backend/modules/problem/TestCaseProblem.java @@ -1,11 +1,11 @@ package jcgp.backend.modules.problem; +import java.io.File; import java.util.List; import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import jcgp.backend.population.Chromosome; -import jcgp.backend.population.Population; +import jcgp.backend.parser.TestCaseParser; import jcgp.backend.resources.Resources; import jcgp.backend.resources.parameters.IntegerParameter; import jcgp.backend.resources.parameters.Parameter; @@ -48,15 +48,16 @@ public abstract class TestCaseProblem<U extends Object> extends Problem { } } - private ObservableList<TestCase<U>> testCases; - private IntegerParameter maxFitness; - private final int inputCount, outputCount; + protected ObservableList<TestCase<U>> testCases; + protected IntegerParameter maxFitness; + protected Resources resources; + + protected TestCaseParser parser; public TestCaseProblem(Resources resources) { super(); - inputCount = resources.inputs(); - outputCount = resources.outputs(); + this.resources = resources; maxFitness = new IntegerParameter(0, "Max fitness", true, false) { @Override @@ -65,34 +66,26 @@ public abstract class TestCaseProblem<U extends Object> extends Problem { } }; testCases = FXCollections.observableArrayList(); + + parser = new TestCaseParser(this); } - - @Override - public void evaluate(Population population, Resources resources) { - // set fittest to 0, change it whenever a fitter one is found - population.setFittest(0); + public TestCaseProblem(Resources resources, File testCase) { + super(); - // for every chromosome in the population - for (int i = 0; i < resources.populationSize(); i++) { - // assume an initial fitness of 0 - int fitness = 0; - // for each test case - for (int t = 0; t < testCases.size(); t++) { - population.getChromosome(i).setInputs(testCases.get(t).getInputs()); - // check each output - for (int o = 0; o < resources.outputs(); o++) { - if (population.getChromosome(i).getOutput(o).calculate() == testCases.get(t).getOutput(o)) { - fitness++; - } - } - } - // assign the resulting fitness to the respective individual - population.getChromosome(i).setFitness(fitness); - if (fitness >= population.getFittest().getFitness()) { - population.setFittest(i); + this.resources = resources; + + maxFitness = new IntegerParameter(0, "Max fitness", true, false) { + @Override + public void validate(Number newValue) { + // blank } - } + }; + testCases = FXCollections.observableArrayList(); + + parser = new TestCaseParser(this); + + parser.parse(testCase); } @Override @@ -100,12 +93,7 @@ public abstract class TestCaseProblem<U extends Object> extends Problem { return new Parameter[]{maxFitness}; } - @Override - public boolean isPerfectSolution(Chromosome fittest) { - return fittest.getFitness() >= maxFitness.get(); - } - - private int getMaxFitness() { + protected int getMaxFitness() { int fitness = 0; for (TestCase<U> tc : testCases) { @@ -125,13 +113,15 @@ public abstract class TestCaseProblem<U extends Object> extends Problem { return testCases; } + public abstract void addTestCase(String[] inputs, String[] outputs); + public void addTestCase(TestCase<U> testCase) { - if (testCase.getInputs().length != inputCount) { + if (testCase.getInputs().length != resources.inputs()) { throw new IllegalArgumentException("Received test case with " + testCase.getInputs().length + - "inputs but need exactly " + inputCount); - } else if (testCase.getOutputs().length != outputCount) { + " inputs but need exactly " + resources.inputs()); + } else if (testCase.getOutputs().length != resources.outputs()) { throw new IllegalArgumentException("Received test case with " + testCase.getOutputs().length + - "outputs but need exactly " + outputCount); + " outputs but need exactly " + resources.outputs()); } else { this.testCases.add(testCase); maxFitness.set(getMaxFitness()); @@ -144,12 +134,22 @@ public abstract class TestCaseProblem<U extends Object> extends Problem { } public int getInputCount() { - return inputCount; + return resources.inputs(); } public int getOutputCount() { - return outputCount; + return resources.outputs(); + } + + public void parse(File file) { + parser.parse(file); } + + public void clearTestCases() { + testCases.clear(); + } + + public abstract String getFileExtension(); } diff --git a/src/jcgp/backend/parser/ChromosomeParser.java b/src/jcgp/backend/parser/ChromosomeParser.java index 74f638c..d5af1bd 100644 --- a/src/jcgp/backend/parser/ChromosomeParser.java +++ b/src/jcgp/backend/parser/ChromosomeParser.java @@ -17,7 +17,7 @@ import jcgp.backend.resources.Resources; * @author Eduardo Pedroni * */ -public class ChromosomeParser { +public abstract class ChromosomeParser { /** * Use this method to parse .chr files into a given chromosome. diff --git a/src/jcgp/backend/parser/FunctionParser.java b/src/jcgp/backend/parser/FunctionParser.java index 64a095c..ab94899 100644 --- a/src/jcgp/backend/parser/FunctionParser.java +++ b/src/jcgp/backend/parser/FunctionParser.java @@ -40,17 +40,10 @@ private FunctionSet functionSet; } private void parseAndSet(String[] splitString) { -// System.out.println("new line"); -// for (int i= 0; i < splitString.length; i++) { -// System.out.println(i + ": " + splitString[i]); -// } int functionIndex = Integer.parseInt(splitString[splitString.length - 1]); - System.out.println("index: " + functionIndex); if (Integer.parseInt(splitString[0]) != 0) { - System.out.println("enabling: " + functionSet.getFunction(functionIndex).getName()); functionSet.enableFunction(functionIndex); } else { - System.out.println("disabling: " + functionSet.getFunction(functionIndex).getName()); functionSet.disableFunction(functionIndex); } diff --git a/src/jcgp/backend/parser/ParameterParser.java b/src/jcgp/backend/parser/ParameterParser.java index 473b632..f96ebf9 100644 --- a/src/jcgp/backend/parser/ParameterParser.java +++ b/src/jcgp/backend/parser/ParameterParser.java @@ -4,6 +4,7 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.util.Scanner; + import jcgp.backend.resources.ModifiableResources; public class ParameterParser { diff --git a/src/jcgp/backend/parser/TestCaseParser.java b/src/jcgp/backend/parser/TestCaseParser.java index c997177..d47d663 100644 --- a/src/jcgp/backend/parser/TestCaseParser.java +++ b/src/jcgp/backend/parser/TestCaseParser.java @@ -1,13 +1,90 @@ package jcgp.backend.parser; import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.util.Scanner; import jcgp.backend.modules.problem.TestCaseProblem; +import jcgp.backend.resources.ModifiableResources; public class TestCaseParser { - public static void parse(File file, TestCaseProblem<?> problem) { + private TestCaseProblem<?> problem; + + public TestCaseParser(TestCaseProblem<?> problem) { + this.problem = problem; + } + + public void parse(File file) { + FileReader fr; + try { + fr = new FileReader(file); + } catch (FileNotFoundException e) { + e.printStackTrace(); + return; + } + + Scanner in = new Scanner(fr); + boolean readingTestCases = false; + int inputs = 0, outputs = 0; + + problem.clearTestCases(); + + while (in.hasNextLine()) { + String nextLine = in.nextLine(); + + if (nextLine.startsWith(".i")) { + String[] split = nextLine.split(" +"); + inputs = Integer.parseInt(split[1]); + } else if (nextLine.startsWith(".o")) { + String[] split = nextLine.split(" +"); + outputs = Integer.parseInt(split[1]); + } else if (nextLine.startsWith(".p") || nextLine.startsWith(".t")) { + readingTestCases = true; + } else if (nextLine.startsWith(".e")) { + readingTestCases = false; + // set test cases? not safe probably + } else if (readingTestCases) { + String[] split = nextLine.split("( |\t)+"); + String[] inputCases = new String[inputs]; + String[] outputCases = new String[outputs]; + for (int i = 0; i < inputs; i++) { + inputCases[i] = split[i]; + } + for (int o = 0; o < outputs; o++) { + outputCases[o] = split[o + inputs]; + } + + problem.addTestCase(inputCases, outputCases); + } + } + in.close(); } + public static void parseParameters(File file, ModifiableResources resources) { + + FileReader fr; + try { + fr = new FileReader(file); + } catch (FileNotFoundException e) { + e.printStackTrace(); + return; + } + + Scanner in = new Scanner(fr); + + while (in.hasNextLine()) { + String nextLine = in.nextLine(); + if (nextLine.startsWith(".i")) { + String[] split = nextLine.split(" +"); + resources.setInputs(Integer.parseInt(split[1])); + } else if (nextLine.startsWith(".o")) { + String[] split = nextLine.split(" +"); + resources.setOutputs(Integer.parseInt(split[1])); + } + } + in.close(); + } } diff --git a/src/jcgp/backend/population/Chromosome.java b/src/jcgp/backend/population/Chromosome.java index 71e19ec..f8830fa 100644 --- a/src/jcgp/backend/population/Chromosome.java +++ b/src/jcgp/backend/population/Chromosome.java @@ -15,7 +15,7 @@ public class Chromosome { private ArrayList<Node> activeNodes; - private int fitness = 0; + private double fitness = 0; private boolean recomputeActiveNodes = true; /** @@ -154,11 +154,11 @@ public class Chromosome { return inputs[index]; } - public int getFitness() { + public double getFitness() { return fitness; } - public void setFitness(int newFitness) { + public void setFitness(double newFitness) { fitness = newFitness; } diff --git a/src/jcgp/backend/resources/Resources.java b/src/jcgp/backend/resources/Resources.java index 0bf1ee1..151d536 100644 --- a/src/jcgp/backend/resources/Resources.java +++ b/src/jcgp/backend/resources/Resources.java @@ -155,7 +155,7 @@ public class Resources { } }; - inputs = new IntegerParameter(3, "Inputs", false, true) { + inputs = new IntegerParameter(3, "Inputs", true, false) { @Override public void validate(Number newValue) { if (newValue.intValue() <= 0) { @@ -167,7 +167,7 @@ public class Resources { } }; - outputs = new IntegerParameter(3, "Outputs", false, true) { + outputs = new IntegerParameter(3, "Outputs", true, false) { @Override public void validate(Number newValue) { if (newValue.intValue() <= 0) { diff --git a/src/jcgp/backend/tests/NodeTests.java b/src/jcgp/backend/tests/NodeTests.java index c9f247a..4054661 100644 --- a/src/jcgp/backend/tests/NodeTests.java +++ b/src/jcgp/backend/tests/NodeTests.java @@ -2,8 +2,8 @@ package jcgp.backend.tests; import static org.junit.Assert.assertTrue; import jcgp.backend.exceptions.InvalidArgumentsException; -import jcgp.backend.function.Function; import jcgp.backend.function.DoubleArithmetic; +import jcgp.backend.function.Function; import jcgp.backend.population.Chromosome; import jcgp.backend.population.Connection; import jcgp.backend.population.Node; diff --git a/src/jcgp/gui/population/GUIInput.java b/src/jcgp/gui/population/GUIInput.java index 6a783db..065c125 100644 --- a/src/jcgp/gui/population/GUIInput.java +++ b/src/jcgp/gui/population/GUIInput.java @@ -7,7 +7,6 @@ import javafx.scene.paint.Paint; import javafx.scene.shape.Circle; import jcgp.backend.population.Connection; import jcgp.backend.population.Input; -import jcgp.backend.population.Output; import jcgp.gui.GUI; public class GUIInput extends GUIGene { @@ -40,20 +39,12 @@ public class GUIInput extends GUIGene { // the drag has entered this node, react appropriately // this happens even if we are the source of the drag ((GUIGene) event.getGestureSource()).setConnectionLine((GUIGene) event.getSource()); - - if (event.getGestureSource() instanceof GUINode) { - Connection source = ((GUINode) event.getGestureSource()).getChangingConnection(); - if (input == source) { - setState(GUIGeneState.NO_CHANGE_TARGET); - return; - } - } else if (event.getGestureSource() instanceof GUIOutput) { - Output source = ((GUIOutput) event.getGestureSource()).getGene(); - if (((GUIGene) event.getSource()).getGene() == source.getSource()) { - ((GUIGene) event.getSource()).setState(GUIGeneState.NO_CHANGE_TARGET); - } + Connection source = ((GUIGene) event.getGestureSource()).getChangingConnection(); + if (input == source) { + setState(GUIGeneState.NO_CHANGE_TARGET); + } else { + setState(GUIGeneState.TARGET); } - setState(GUIGeneState.TARGET); } }); @@ -170,7 +161,6 @@ public class GUIInput extends GUIGene { break; default: break; - } } @@ -241,7 +231,7 @@ public class GUIInput extends GUIGene { @Override public void updateText() { if (parent.isEvaluating()) { - text.setText("I: " + input.getIndex() + "\nValue: " + value.toString()); + text.setText("I: " + input.getIndex() + "\n" + value.toString()); } else { text.setText("I: " + input.getIndex()); } diff --git a/src/jcgp/gui/population/GUINode.java b/src/jcgp/gui/population/GUINode.java index 2b953c0..f50edd3 100644 --- a/src/jcgp/gui/population/GUINode.java +++ b/src/jcgp/gui/population/GUINode.java @@ -454,7 +454,7 @@ public class GUINode extends GUIGene { public void updateText() { if (parent.isEvaluating()) { - text.setText(node.getFunction().getName() + "\nValue: " + value.toString()); + text.setText(node.getFunction().getName() + "\n" + value.toString()); } else { text.setText(node.getFunction().getName()); } diff --git a/src/jcgp/gui/population/GUIOutput.java b/src/jcgp/gui/population/GUIOutput.java index 89c12b3..79159b9 100644 --- a/src/jcgp/gui/population/GUIOutput.java +++ b/src/jcgp/gui/population/GUIOutput.java @@ -321,7 +321,7 @@ public class GUIOutput extends GUIGene { @Override public void updateText() { if (parent.isEvaluating()) { - text.setText("O: " + output.getIndex() + "\nValue: " + value.toString()); + text.setText("O: " + output.getIndex() + "\n" + value.toString()); } else { text.setText("O: " + output.getIndex()); } diff --git a/src/jcgp/gui/settings/SettingsPane.java b/src/jcgp/gui/settings/SettingsPane.java index 4e8c342..4050a89 100644 --- a/src/jcgp/gui/settings/SettingsPane.java +++ b/src/jcgp/gui/settings/SettingsPane.java @@ -6,7 +6,6 @@ import java.util.ArrayList; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Insets; -import javafx.scene.Group; import javafx.scene.control.Button; import javafx.scene.control.CheckBox; import javafx.scene.control.ComboBox; @@ -125,8 +124,7 @@ public class SettingsPane extends AnchorPane { eaCBox.getSelectionModel().select(jcgp.getEvolutionaryStrategy()); eaCBox.prefWidthProperty().bind(mainContainer.widthProperty()); - final VBox eaParameters = new VBox(); - eaParameters.setSpacing(2); + final VBox eaParameters = new VBox(2); if (jcgp.getEvolutionaryStrategy().getLocalParameters() != null) { refreshParameters(jcgp.getEvolutionaryStrategy().getLocalParameters(), eaParameters); @@ -158,8 +156,7 @@ public class SettingsPane extends AnchorPane { mutatorCBox.getSelectionModel().select(jcgp.getMutator()); mutatorCBox.prefWidthProperty().bind(mainContainer.widthProperty()); - final VBox mutatorParameters = new VBox(); - mutatorParameters.setSpacing(2); + final VBox mutatorParameters = new VBox(2); if (jcgp.getEvolutionaryStrategy().getLocalParameters() != null) { refreshParameters(jcgp.getMutator().getLocalParameters(), mutatorParameters); } @@ -190,23 +187,27 @@ public class SettingsPane extends AnchorPane { problemCBox.getSelectionModel().select(jcgp.getProblem()); problemCBox.prefWidthProperty().bind(mainContainer.widthProperty()); - final VBox problemParameters = new VBox(); - problemParameters.setSpacing(2); + final VBox problemParameters = new VBox(2); problemParameters.setPadding(new Insets(0, 0, 4, 0)); if (jcgp.getProblem().getLocalParameters() != null) { refreshParameters(jcgp.getProblem().getLocalParameters(), problemParameters); } - final Group showTestCaseContainer = new Group(); + final HBox testCaseControlContainer = new HBox(2); + final Button showTestCaseButton = makeTestCaseButton(); + final Button loadTestCaseButton = makeLoadTestCaseButton(); + HBox.setHgrow(showTestCaseButton, Priority.ALWAYS); + showTestCaseButton.setMaxWidth(Double.MAX_VALUE); + HBox.setHgrow(loadTestCaseButton, Priority.ALWAYS); + loadTestCaseButton.setMaxWidth(Double.MAX_VALUE); + if (jcgp.getProblem() instanceof TestCaseProblem<?>) { - showTestCaseContainer.getChildren().add(showTestCaseButton); - - testCaseTable = new TestCaseTable((TestCaseProblem<Object>) jcgp.getProblem(), gui); + testCaseControlContainer.getChildren().addAll(showTestCaseButton, loadTestCaseButton); + remakeTestCaseTable(); } - nodeFunctions = new VBox(); - nodeFunctions.setSpacing(2); + nodeFunctions = new VBox(2); nodeFunctions.setPadding(new Insets(0, 0, 4, 0)); refreshFunctions(); @@ -220,20 +221,40 @@ public class SettingsPane extends AnchorPane { testCaseTable.close(); gui.setEvaluating(false); refreshFunctions(); - showTestCaseContainer.getChildren().clear(); + testCaseControlContainer.getChildren().clear(); if (jcgp.getProblem() instanceof TestCaseProblem) { - showTestCaseContainer.getChildren().add(showTestCaseButton); - testCaseTable = new TestCaseTable((TestCaseProblem<Object>) jcgp.getProblem(), gui); + testCaseControlContainer.getChildren().addAll(showTestCaseButton, loadTestCaseButton); + remakeTestCaseTable(); } gui.reset(); } }); - problemPane.getChildren().addAll(header, problemCBox, problemParameters, nodeFunctions, showTestCaseContainer); + problemPane.getChildren().addAll(header, problemCBox, problemParameters, nodeFunctions, testCaseControlContainer); mainContainer.getChildren().add(problemPane); } + private Button makeLoadTestCaseButton() { + Button b = new Button("Load test cases"); + b.setOnAction(new EventHandler<ActionEvent>() { + @Override + public void handle(ActionEvent event) { + FileChooser fc = new FileChooser(); + fc.setTitle("Open test case file..."); + fc.getExtensionFilters().add(new ExtensionFilter("CGP " + gui.getExperiment().getProblem() + " files", "*" + ((TestCaseProblem<?>) gui.getExperiment().getProblem()).getFileExtension())); + fc.getExtensionFilters().add(new ExtensionFilter("All files", "*.*")); + File chrFile = fc.showOpenDialog(gui.getStage()); + if (chrFile != null) { + gui.getExperiment().loadTestCases(chrFile); + remakeTestCaseTable(); + gui.reDraw(); + } + } + }); + return b; + } + private Button makeTestCaseButton() { Button b = new Button("Show test cases"); b.setOnAction(new EventHandler<ActionEvent>() { @@ -295,9 +316,9 @@ public class SettingsPane extends AnchorPane { File parFile = fc.showOpenDialog(gui.getStage()); if (parFile != null) { gui.getExperiment().loadParameters(parFile); + gui.reDraw(); + refreshFunctions(); } - gui.reDraw(); - refreshFunctions(); } }); @@ -311,14 +332,14 @@ public class SettingsPane extends AnchorPane { File chrFile = fc.showOpenDialog(gui.getStage()); if (chrFile != null) { gui.getExperiment().loadChromosome(chrFile); + gui.reDraw(); } - gui.reDraw(); } }); saveChromosome.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { - //gui.step(); + } }); @@ -458,10 +479,21 @@ public class SettingsPane extends AnchorPane { loadChromosome.setDisable(running); saveChromosome.setDisable(running); - testCaseTable.getTable().setDisable(running); } + private void remakeTestCaseTable() { + boolean wasShowing = false; + if (testCaseTable != null) { + wasShowing = testCaseTable.isShowing(); + testCaseTable.close(); + } + testCaseTable = new TestCaseTable((TestCaseProblem<Object>) gui.getExperiment().getProblem(), gui); + if (wasShowing) { + testCaseTable.show(); + } + } + public TestCaseTable getTestCaseTable() { return testCaseTable; } diff --git a/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java b/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java index 29648ca..9a6ec4a 100644 --- a/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java @@ -1,12 +1,12 @@ package jcgp.gui.settings.parameters; +import java.text.DecimalFormat; + import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; -import javafx.event.EventHandler; import javafx.geometry.Pos; import javafx.scene.control.Control; import javafx.scene.control.TextField; -import javafx.scene.input.KeyEvent; import jcgp.backend.resources.parameters.Parameter; import jcgp.backend.resources.parameters.ParameterStatus; import jcgp.gui.settings.SettingsPane; @@ -23,6 +23,7 @@ import jcgp.gui.settings.SettingsPane; public class GUIDoubleParameter extends GUIParameter<Number> { private TextField textField; + private DecimalFormat decimalFormat; /** * This default-visibility constructor is intended for use @@ -31,6 +32,8 @@ public class GUIDoubleParameter extends GUIParameter<Number> { */ GUIDoubleParameter(Parameter<Number> parameter, SettingsPane sp) { super(parameter, sp); + decimalFormat = new DecimalFormat(); + decimalFormat.setMaximumFractionDigits(10); } @Override @@ -38,23 +41,12 @@ public class GUIDoubleParameter extends GUIParameter<Number> { textField = new TextField(String.valueOf(parameter.get())); textField.setStyle(VALID_PARAMETER_STYLE); textField.setAlignment(Pos.CENTER_RIGHT); - + textField.prefWidthProperty().bind(widthProperty().divide(2)); return textField; } @Override protected void setControlListeners() { - /* filter keypresses and ignore anything that is not a number - * and any decimal point beyond the first ones */ - textField.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler<KeyEvent>() { - @Override - public void handle( KeyEvent t ) { - char ch = t.getCharacter().toCharArray()[t.getCharacter().toCharArray().length - 1]; - if (!((ch >= '0' && ch <= '9') || (ch == '.' && !textField.getText().contains(".")))) { - t.consume(); - } - } - }); /* pass the TextField value back to the parameter whenever it gets * modified, provided it is not empty and the experiment isn't running */ textField.textProperty().addListener(new ChangeListener<String>() { @@ -62,11 +54,17 @@ public class GUIDoubleParameter extends GUIParameter<Number> { public void changed( ObservableValue<? extends String> observable, String oldValue, String newValue) { - if (!newValue.isEmpty() && !settingsPane.isExperimentRunning()) { - parameter.set(Double.parseDouble(newValue)); - settingsPane.revalidateParameters(); + if (!settingsPane.isExperimentRunning()) { + if (newValue.matches("([0-9]*[.]*[0-9]*)")) { + if (!newValue.isEmpty()) { + double value = Double.parseDouble(newValue); + parameter.set(value); + settingsPane.revalidateParameters(); + } + } else { + refreshValue(); + } } - } }); /* if the TextField loses focus and is empty, set it to the current @@ -76,8 +74,8 @@ public class GUIDoubleParameter extends GUIParameter<Number> { public void changed( ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { - if (!newValue && textField.getText().isEmpty()) { - textField.setText(String.valueOf(parameter.get())); + if (!newValue) { + refreshValue(); } } }); @@ -102,6 +100,7 @@ public class GUIDoubleParameter extends GUIParameter<Number> { @Override public void refreshValue() { - textField.setText(String.valueOf((parameter).get())); + System.out.println("setting text to: " + parameter.get().doubleValue()); + textField.setText(decimalFormat.format(parameter.get().doubleValue())); } } diff --git a/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java b/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java index da2c11f..ee266f3 100644 --- a/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java @@ -2,11 +2,9 @@ package jcgp.gui.settings.parameters; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; -import javafx.event.EventHandler; import javafx.geometry.Pos; import javafx.scene.control.Control; import javafx.scene.control.TextField; -import javafx.scene.input.KeyEvent; import jcgp.backend.resources.parameters.Parameter; import jcgp.backend.resources.parameters.ParameterStatus; import jcgp.gui.settings.SettingsPane; @@ -38,22 +36,13 @@ public class GUIIntegerParameter extends GUIParameter<Number> { textField = new TextField(String.valueOf(parameter.get())); textField.setStyle(VALID_PARAMETER_STYLE); textField.setAlignment(Pos.CENTER_RIGHT); + textField.prefWidthProperty().bind(widthProperty().divide(2)); return textField; } @Override protected void setControlListeners() { - /* filter keypresses and ignore anything that is not a number */ - textField.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler<KeyEvent>() { - @Override - public void handle( KeyEvent t ) { - char ch = t.getCharacter().toCharArray()[t.getCharacter().toCharArray().length - 1]; - if (!(ch >= '0' && ch <= '9')) { - t.consume(); - } - } - }); /* pass the TextField value back to the parameter whenever it gets * modified, provided it is not empty and the experiment isn't running */ textField.textProperty().addListener(new ChangeListener<String>() { @@ -61,9 +50,16 @@ public class GUIIntegerParameter extends GUIParameter<Number> { public void changed( ObservableValue<? extends String> observable, String oldValue, String newValue) { - if (!newValue.isEmpty() && !settingsPane.isExperimentRunning()) { - parameter.set(Double.parseDouble(newValue)); - settingsPane.revalidateParameters(); + if (!settingsPane.isExperimentRunning()) { + if (newValue.matches("[0-9]*")) { + if (!newValue.isEmpty()) { + int value = Integer.parseInt(newValue); + parameter.set(value); + settingsPane.revalidateParameters(); + } + } else { + refreshValue(); + } } } }); @@ -74,8 +70,8 @@ public class GUIIntegerParameter extends GUIParameter<Number> { public void changed( ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { - if (!newValue && textField.getText().isEmpty()) { - textField.setText(String.valueOf(parameter.get())); + if (!newValue) { + refreshValue(); } } }); @@ -101,6 +97,6 @@ public class GUIIntegerParameter extends GUIParameter<Number> { @Override public void refreshValue() { - textField.setText(String.valueOf((parameter).get())); + textField.setText(String.valueOf(parameter.get())); } } diff --git a/src/jcgp/gui/settings/parameters/GUIParameter.java b/src/jcgp/gui/settings/parameters/GUIParameter.java index 159e709..7dd7231 100644 --- a/src/jcgp/gui/settings/parameters/GUIParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIParameter.java @@ -7,10 +7,9 @@ import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.geometry.Pos; import javafx.scene.control.Control; +import javafx.scene.control.Label; import javafx.scene.control.Tooltip; import javafx.scene.layout.HBox; -import javafx.scene.layout.Priority; -import javafx.scene.text.Text; import jcgp.backend.resources.parameters.BooleanParameter; import jcgp.backend.resources.parameters.DoubleParameter; import jcgp.backend.resources.parameters.IntegerParameter; @@ -46,7 +45,7 @@ public abstract class GUIParameter<T> extends HBox { public static final String WARNING_PARAMETER_STYLE = "-fx-background-color: " + GUI.NEUTRAL_SELECTION_COLOUR; public static final String VALID_PARAMETER_STYLE = "-fx-background-color: " + GUI.NEUTRAL_COLOUR; - private Text name; + private Label name; private Control valueControl; protected SettingsPane settingsPane; @@ -80,9 +79,9 @@ public abstract class GUIParameter<T> extends HBox { setAlignment(Pos.CENTER_LEFT); setSpacing(5); - name = new Text(parameter.getName()); + name = new Label(parameter.getName()); // set text width to half of the total width of the GUIParameter - name.wrappingWidthProperty().bind(widthProperty().divide(2)); + name.prefWidthProperty().bind(widthProperty().divide(2)); // the tooltip is the hover-over label containing status information, when appropriate tooltip = new Tooltip(); @@ -90,7 +89,6 @@ public abstract class GUIParameter<T> extends HBox { tooltip.setSkin(null); valueControl = makeControl(); - setHgrow(valueControl, Priority.ALWAYS); // if the parameter is a monitor, it should be permanently disabled valueControl.setDisable(parameter.isMonitor()); @@ -177,7 +175,7 @@ public abstract class GUIParameter<T> extends HBox { */ public void validate() { parameter.validate(parameter.get()); - refreshValue(); + //refreshValue(); setValidityStyle(); } diff --git a/src/jcgp/gui/settings/testcase/TestCaseTable.java b/src/jcgp/gui/settings/testcase/TestCaseTable.java index b84f9ab..d7b2e2b 100644 --- a/src/jcgp/gui/settings/testcase/TestCaseTable.java +++ b/src/jcgp/gui/settings/testcase/TestCaseTable.java @@ -19,7 +19,6 @@ import jcgp.backend.modules.problem.TestCaseProblem.TestCase; import jcgp.gui.GUI; /** - * Dont forget to override toString()! * * * @author Eduardo Pedroni @@ -29,17 +28,17 @@ public class TestCaseTable extends Stage { private TableView<TestCase<Object>> table; - public TestCaseTable(final TestCaseProblem<Object> problem, final GUI gui) { + public TestCaseTable(final TestCaseProblem<Object> testCaseProblem, final GUI gui) { super(); table = new TableView<TestCase<Object>>(); - ObservableList<TestCase<Object>> testCaseList = problem.getTestCases(); + ObservableList<TestCase<Object>> testCaseList = testCaseProblem.getTestCases(); - ArrayList<TableColumn<TestCase<Object>, String>> inputs = new ArrayList<TableColumn<TestCase<Object>, String>>(problem.getInputCount()); - ArrayList<TableColumn<TestCase<Object>, String>> outputs = new ArrayList<TableColumn<TestCase<Object>, String>>(problem.getOutputCount()); + ArrayList<TableColumn<TestCase<Object>, String>> inputs = new ArrayList<TableColumn<TestCase<Object>, String>>(testCaseProblem.getInputCount()); + ArrayList<TableColumn<TestCase<Object>, String>> outputs = new ArrayList<TableColumn<TestCase<Object>, String>>(testCaseProblem.getOutputCount()); TableColumn<TestCase<Object>, String> tc; - for (int i = 0; i < problem.getInputCount(); i++) { + for (int i = 0; i < testCaseProblem.getInputCount(); i++) { tc = new TableColumn<TestCase<Object>, String>("I: " + i); inputs.add(tc); final int index = i; @@ -50,10 +49,10 @@ public class TestCaseTable extends Stage { } }); tc.setSortable(false); - tc.prefWidthProperty().bind(table.widthProperty().divide(problem.getInputCount() + problem.getOutputCount())); + tc.prefWidthProperty().bind(table.widthProperty().divide(testCaseProblem.getInputCount() + testCaseProblem.getOutputCount())); } - for (int o = 0; o < problem.getOutputCount(); o++) { + for (int o = 0; o < testCaseProblem.getOutputCount(); o++) { tc = new TableColumn<TestCase<Object>, String>("O: " + o); outputs.add(tc); final int index = o; @@ -64,7 +63,7 @@ public class TestCaseTable extends Stage { } }); tc.setSortable(false); - tc.prefWidthProperty().bind(table.widthProperty().divide(problem.getInputCount() + problem.getOutputCount())); + tc.prefWidthProperty().bind(table.widthProperty().divide(testCaseProblem.getInputCount() + testCaseProblem.getOutputCount())); } table.getColumns().addAll(inputs); @@ -84,6 +83,7 @@ public class TestCaseTable extends Stage { @Override public void handle(WindowEvent event) { gui.hideGeneValues(); + table.getSelectionModel().select(null); } }); diff --git a/src/mult2.plu b/src/mult2.plu new file mode 100644 index 0000000..e629d28 --- /dev/null +++ b/src/mult2.plu @@ -0,0 +1,5 @@ +.i 4
+.o 4
+.p 1
+65280 61680 52428 43690 32768 19456 27328 41120
+.e
\ No newline at end of file diff --git a/src/quintic-polynomial.dat b/src/quintic-polynomial.dat new file mode 100644 index 0000000..347b95a --- /dev/null +++ b/src/quintic-polynomial.dat @@ -0,0 +1,26 @@ +.i 1
+.o 1
+.t 21
+-1.0 0.0
+-0.9 -0.03249
+-0.8 -0.10368
+-0.7 -0.18207
+-0.6 -0.24576
+-0.5 -0.28125
+-0.4 -0.28224
+-0.3 -0.24843
+-0.2 -0.18432
+-0.1 -0.09801
+0.0 0.0
+0.1 0.09801
+0.2 0.18432
+0.3 0.24843
+0.4 0.28224
+0.5 0.28125
+0.6 0.24576
+0.7 0.18207
+0.8 0.10368
+0.9 0.03249
+1.0 0.0
+.e
+
|