diff options
Diffstat (limited to 'src')
44 files changed, 2486 insertions, 1924 deletions
diff --git a/src/jcgp/JCGP.java b/src/jcgp/JCGP.java index f980c00..1e6d628 100644 --- a/src/jcgp/JCGP.java +++ b/src/jcgp/JCGP.java @@ -5,12 +5,15 @@ import java.io.File; import jcgp.backend.modules.es.EvolutionaryStrategy; import jcgp.backend.modules.es.MuPlusLambda; import jcgp.backend.modules.es.TournamentSelection; +import jcgp.backend.modules.mutator.FixedPointMutator; import jcgp.backend.modules.mutator.Mutator; -import jcgp.backend.modules.mutator.PointMutator; +import jcgp.backend.modules.mutator.PercentPointMutator; +import jcgp.backend.modules.mutator.ProbabilisticMutator; import jcgp.backend.modules.problem.DigitalCircuitProblem; import jcgp.backend.modules.problem.Problem; import jcgp.backend.modules.problem.SymbolicRegressionProblem; import jcgp.backend.modules.problem.TestCaseProblem; +import jcgp.backend.modules.problem.TravellingSalesmanProblem; import jcgp.backend.parsers.ChromosomeParser; import jcgp.backend.parsers.FunctionParser; import jcgp.backend.parsers.ParameterParser; @@ -49,19 +52,22 @@ public class JCGP { */ // mutators private Mutator[] mutators = new Mutator[] { - new PointMutator(resources) }; + new PercentPointMutator(resources), + new FixedPointMutator(resources), + new ProbabilisticMutator(resources)}; private Mutator mutator = mutators[0]; // evolutionary algorithms private EvolutionaryStrategy[] evolutionaryStrategies = new EvolutionaryStrategy[] { new MuPlusLambda(resources), - new TournamentSelection(resources) }; + new TournamentSelection(resources)}; private EvolutionaryStrategy evolutionaryStrategy = evolutionaryStrategies[0]; // problem types private Problem[] problems = new Problem[] { new SymbolicRegressionProblem(resources), - new DigitalCircuitProblem(resources) }; + new DigitalCircuitProblem(resources), + new TravellingSalesmanProblem(resources)}; private Problem problem = problems[0]; /* @@ -178,7 +184,6 @@ public class JCGP { } public void nextGeneration() { - System.out.println("service: doing next gen"); if (!finished) { problem.evaluate(population, (Resources) resources); reportGeneration(); @@ -264,12 +269,9 @@ public class JCGP { reset(); } - public void loadTestCases(File file) { - if (problem instanceof TestCaseProblem) { - TestCaseParser.parseParameters(file, resources); - reset(); - TestCaseParser.parse(file, (TestCaseProblem<?>) problem, resources); - } + public void loadProblemData(File file) { + problem.parse(file, resources); + reset(); } public void loadChromosome(File file, int chromosomeIndex) { diff --git a/src/jcgp/backend/function/BitwiseLogic.java b/src/jcgp/backend/function/BitwiseLogic.java deleted file mode 100644 index 2466f36..0000000 --- a/src/jcgp/backend/function/BitwiseLogic.java +++ /dev/null @@ -1,506 +0,0 @@ -package jcgp.backend.function; - -import jcgp.backend.population.Connection; - -public class BitwiseLogic extends FunctionSet { - - public BitwiseLogic() { - name = "32-bit Logic"; - functionList = new Function[]{ - new ConstantZero(), - new ConstantOne(), - new WireA(), - new WireB(), - new NotA(), - new NotB(), - new And(), - new AndNotA(), - new AndNotB(), - new Nor(), - new Xor(), - new Xnor(), - new Or(), - new OrNotA(), - new OrNotB(), - new Nand() -// new Mux1(), -// new Mux2(), -// new Mux3(), -// new Mux4() - }; - - enableAll(); - } - - public static class ConstantZero extends Function { - @Override - public UnsignedInteger run(Connection... connections) { - return new UnsignedInteger(0); - } - - @Override - public int getArity() { - return 0; - } - - @Override - public String getName() { - return "0"; - } - } - - public static class ConstantOne extends Function { - @Override - public UnsignedInteger run(Connection... connections) { - return new UnsignedInteger(0xFFFF); - } - - @Override - public int getArity() { - return 0; - } - - @Override - public String getName() { - return "1"; - } - } - - public static class WireA extends Function { - @Override - public UnsignedInteger run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - return ((UnsignedInteger) connections[0].getValue()); - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "Wire A"; - } - } - - public static class WireB extends Function { - @Override - public UnsignedInteger run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - return ((UnsignedInteger) connections[1].getValue()); - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "Wire B"; - } - } - - public static class NotA extends Function { - @Override - public UnsignedInteger run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - return new UnsignedInteger(~((UnsignedInteger) connections[0].getValue()).get()); - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "Not A"; - } - } - - public static class NotB extends Function { - @Override - public UnsignedInteger run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - return new UnsignedInteger(~((UnsignedInteger) connections[1].getValue()).get()); - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "Not B"; - } - } - - public static class And extends Function { - @Override - public UnsignedInteger run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - UnsignedInteger in0 = ((UnsignedInteger) connections[0].getValue()); - UnsignedInteger in1 = ((UnsignedInteger) connections[1].getValue()); - Integer result = in0.get() & in1.get(); - - return new UnsignedInteger(result); - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "And"; - } - } - - public static class AndNotA extends Function { - @Override - public UnsignedInteger run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - UnsignedInteger in0 = ((UnsignedInteger) connections[0].getValue()); - UnsignedInteger in1 = ((UnsignedInteger) connections[1].getValue()); - Integer result = ~(in0.get()) & in1.get(); - - return new UnsignedInteger(result); - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "And !A"; - } - } - - public static class AndNotB extends Function { - @Override - public UnsignedInteger run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - UnsignedInteger in0 = ((UnsignedInteger) connections[0].getValue()); - UnsignedInteger in1 = ((UnsignedInteger) connections[1].getValue()); - Integer result = in0.get() & ~(in1.get()); - - return new UnsignedInteger(result); - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "And !B"; - } - } - - public static class Nor extends Function { - @Override - public UnsignedInteger run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - UnsignedInteger in0 = ((UnsignedInteger) connections[0].getValue()); - UnsignedInteger in1 = ((UnsignedInteger) connections[1].getValue()); - Integer result = in0.get() | in1.get(); - - return new UnsignedInteger(~result); - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "Nor"; - } - } - - public static class Xor extends Function { - @Override - public UnsignedInteger run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - UnsignedInteger in0 = ((UnsignedInteger) connections[0].getValue()); - UnsignedInteger in1 = ((UnsignedInteger) connections[1].getValue()); - Integer result = in0.get() ^ in1.get(); - - return new UnsignedInteger(result); - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "Xor"; - } - } - - public static class Xnor extends Function { - @Override - public UnsignedInteger run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - UnsignedInteger in0 = ((UnsignedInteger) connections[0].getValue()); - UnsignedInteger in1 = ((UnsignedInteger) connections[1].getValue()); - Integer result = in0.get() ^ in1.get(); - - return new UnsignedInteger(~result); - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "Xnor"; - } - } - - public static class Or extends Function { - @Override - public UnsignedInteger run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - UnsignedInteger in0 = ((UnsignedInteger) connections[0].getValue()); - UnsignedInteger in1 = ((UnsignedInteger) connections[1].getValue()); - Integer result = in0.get() | in1.get(); - - return new UnsignedInteger(result); - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "Or"; - } - } - - public static class OrNotA extends Function { - @Override - public UnsignedInteger run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - UnsignedInteger in0 = ((UnsignedInteger) connections[0].getValue()); - UnsignedInteger in1 = ((UnsignedInteger) connections[1].getValue()); - Integer result = ~in0.get() | in1.get(); - - return new UnsignedInteger(result); - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "Or !A"; - } - } - - public static class OrNotB extends Function { - @Override - public UnsignedInteger run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - UnsignedInteger in0 = ((UnsignedInteger) connections[0].getValue()); - UnsignedInteger in1 = ((UnsignedInteger) connections[1].getValue()); - Integer result = in0.get() | ~in1.get(); - - return new UnsignedInteger(result); - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "Or !B"; - } - } - - public static class Nand extends Function { - @Override - public UnsignedInteger run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - UnsignedInteger in0 = ((UnsignedInteger) connections[0].getValue()); - UnsignedInteger in1 = ((UnsignedInteger) connections[1].getValue()); - Integer result = in0.get() & in1.get(); - - return new UnsignedInteger(~result); - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "Nand"; - } - } - - public static class Mux1 extends Function { - @Override - public UnsignedInteger run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - UnsignedInteger in0 = ((UnsignedInteger) connections[0].getValue()); - UnsignedInteger in1 = ((UnsignedInteger) connections[1].getValue()); - UnsignedInteger in2 = ((UnsignedInteger) connections[2].getValue()); - Integer result = ((in0.get() & ~in2.get()) | (in1.get() & in2.get())); - - return new UnsignedInteger(result); - } - } - - @Override - public int getArity() { - return 3; - } - - @Override - public String getName() { - return "Mux1"; - } - } - - public static class Mux2 extends Function { - @Override - public UnsignedInteger run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - UnsignedInteger in0 = ((UnsignedInteger) connections[0].getValue()); - UnsignedInteger in1 = ((UnsignedInteger) connections[1].getValue()); - UnsignedInteger in2 = ((UnsignedInteger) connections[2].getValue()); - Integer result = ((in0.get() & ~in2.get()) | (~in1.get() & in2.get())); - - return new UnsignedInteger(result); - } - } - - @Override - public int getArity() { - return 3; - } - - @Override - public String getName() { - return "Mux2"; - } - } - - public static class Mux3 extends Function { - @Override - public UnsignedInteger run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - UnsignedInteger in0 = ((UnsignedInteger) connections[0].getValue()); - UnsignedInteger in1 = ((UnsignedInteger) connections[1].getValue()); - UnsignedInteger in2 = ((UnsignedInteger) connections[2].getValue()); - Integer result = ((~in0.get() & ~in2.get()) | (in1.get() & in2.get())); - - return new UnsignedInteger(result); - } - } - - @Override - public int getArity() { - return 3; - } - - @Override - public String getName() { - return "Mux3"; - } - } - - public static class Mux4 extends Function { - @Override - public UnsignedInteger run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - UnsignedInteger in0 = ((UnsignedInteger) connections[0].getValue()); - UnsignedInteger in1 = ((UnsignedInteger) connections[1].getValue()); - UnsignedInteger in2 = ((UnsignedInteger) connections[2].getValue()); - Integer result = ((~in0.get() & ~in2.get()) | (~in1.get() & in2.get())); - - return new UnsignedInteger(result); - } - } - - @Override - public int getArity() { - return 3; - } - - @Override - public String getName() { - return "Mux4"; - } - } -} diff --git a/src/jcgp/backend/function/DigitalCircuitFunctions.java b/src/jcgp/backend/function/DigitalCircuitFunctions.java new file mode 100644 index 0000000..31cdf17 --- /dev/null +++ b/src/jcgp/backend/function/DigitalCircuitFunctions.java @@ -0,0 +1,504 @@ +package jcgp.backend.function; + +public class DigitalCircuitFunctions extends FunctionSet { + + public DigitalCircuitFunctions() { + name = "32-bit Logic"; + functionList = new Function[]{ + new ConstantZero(), + new ConstantOne(), + new WireA(), + new WireB(), + new NotA(), + new NotB(), + new And(), + new AndNotA(), + new AndNotB(), + new Nor(), + new Xor(), + new Xnor(), + new Or(), + new OrNotA(), + new OrNotB(), + new Nand(), + new Mux1(), + new Mux2(), + new Mux3(), + new Mux4() + }; + + enableAll(); + } + + public static class ConstantZero extends Function { + @Override + public UnsignedInteger run(Object... args) { + return new UnsignedInteger(0); + } + + @Override + public int getArity() { + return 0; + } + + @Override + public String toString() { + return "0"; + } + } + + public static class ConstantOne extends Function { + @Override + public UnsignedInteger run(Object... args) { + return new UnsignedInteger(0xFFFF); + } + + @Override + public int getArity() { + return 0; + } + + @Override + public String toString() { + return "1"; + } + } + + public static class WireA extends Function { + @Override + public UnsignedInteger run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " in but arity is " + getArity() + "."); + } else { + return ((UnsignedInteger) args[0]); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Wire A"; + } + } + + public static class WireB extends Function { + @Override + public UnsignedInteger run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " in but arity is " + getArity() + "."); + } else { + return ((UnsignedInteger) args[1]); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Wire B"; + } + } + + public static class NotA extends Function { + @Override + public UnsignedInteger run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " in but arity is " + getArity() + "."); + } else { + return new UnsignedInteger(~((UnsignedInteger) args[0]).get()); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Not A"; + } + } + + public static class NotB extends Function { + @Override + public UnsignedInteger run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " in but arity is " + getArity() + "."); + } else { + return new UnsignedInteger(~((UnsignedInteger) args[1]).get()); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Not B"; + } + } + + public static class And extends Function { + @Override + public UnsignedInteger run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " in but arity is " + getArity() + "."); + } else { + UnsignedInteger in0 = ((UnsignedInteger) args[0]); + UnsignedInteger in1 = ((UnsignedInteger) args[1]); + Integer result = in0.get() & in1.get(); + + return new UnsignedInteger(result); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "And"; + } + } + + public static class AndNotA extends Function { + @Override + public UnsignedInteger run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " in but arity is " + getArity() + "."); + } else { + UnsignedInteger in0 = ((UnsignedInteger) args[0]); + UnsignedInteger in1 = ((UnsignedInteger) args[1]); + Integer result = ~(in0.get()) & in1.get(); + + return new UnsignedInteger(result); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "And !A"; + } + } + + public static class AndNotB extends Function { + @Override + public UnsignedInteger run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " in but arity is " + getArity() + "."); + } else { + UnsignedInteger in0 = ((UnsignedInteger) args[0]); + UnsignedInteger in1 = ((UnsignedInteger) args[1]); + Integer result = in0.get() & ~(in1.get()); + + return new UnsignedInteger(result); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "And !B"; + } + } + + public static class Nor extends Function { + @Override + public UnsignedInteger run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " in but arity is " + getArity() + "."); + } else { + UnsignedInteger in0 = ((UnsignedInteger) args[0]); + UnsignedInteger in1 = ((UnsignedInteger) args[1]); + Integer result = in0.get() | in1.get(); + + return new UnsignedInteger(~result); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Nor"; + } + } + + public static class Xor extends Function { + @Override + public UnsignedInteger run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " in but arity is " + getArity() + "."); + } else { + UnsignedInteger in0 = ((UnsignedInteger) args[0]); + UnsignedInteger in1 = ((UnsignedInteger) args[1]); + Integer result = in0.get() ^ in1.get(); + + return new UnsignedInteger(result); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Xor"; + } + } + + public static class Xnor extends Function { + @Override + public UnsignedInteger run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " in but arity is " + getArity() + "."); + } else { + UnsignedInteger in0 = ((UnsignedInteger) args[0]); + UnsignedInteger in1 = ((UnsignedInteger) args[1]); + Integer result = in0.get() ^ in1.get(); + + return new UnsignedInteger(~result); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Xnor"; + } + } + + public static class Or extends Function { + @Override + public UnsignedInteger run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " in but arity is " + getArity() + "."); + } else { + UnsignedInteger in0 = ((UnsignedInteger) args[0]); + UnsignedInteger in1 = ((UnsignedInteger) args[1]); + Integer result = in0.get() | in1.get(); + + return new UnsignedInteger(result); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Or"; + } + } + + public static class OrNotA extends Function { + @Override + public UnsignedInteger run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " in but arity is " + getArity() + "."); + } else { + UnsignedInteger in0 = ((UnsignedInteger) args[0]); + UnsignedInteger in1 = ((UnsignedInteger) args[1]); + Integer result = ~in0.get() | in1.get(); + + return new UnsignedInteger(result); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Or !A"; + } + } + + public static class OrNotB extends Function { + @Override + public UnsignedInteger run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " in but arity is " + getArity() + "."); + } else { + UnsignedInteger in0 = ((UnsignedInteger) args[0]); + UnsignedInteger in1 = ((UnsignedInteger) args[1]); + Integer result = in0.get() | ~in1.get(); + + return new UnsignedInteger(result); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Or !B"; + } + } + + public static class Nand extends Function { + @Override + public UnsignedInteger run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " in but arity is " + getArity() + "."); + } else { + UnsignedInteger in0 = ((UnsignedInteger) args[0]); + UnsignedInteger in1 = ((UnsignedInteger) args[1]); + Integer result = in0.get() & in1.get(); + + return new UnsignedInteger(~result); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Nand"; + } + } + + public static class Mux1 extends Function { + @Override + public UnsignedInteger run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " in but arity is " + getArity() + "."); + } else { + UnsignedInteger in0 = ((UnsignedInteger) args[0]); + UnsignedInteger in1 = ((UnsignedInteger) args[1]); + UnsignedInteger in2 = ((UnsignedInteger) args[2]); + Integer result = ((in0.get() & ~in2.get()) | (in1.get() & in2.get())); + + return new UnsignedInteger(result); + } + } + + @Override + public int getArity() { + return 3; + } + + @Override + public String toString() { + return "Mux1"; + } + } + + public static class Mux2 extends Function { + @Override + public UnsignedInteger run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " in but arity is " + getArity() + "."); + } else { + UnsignedInteger in0 = ((UnsignedInteger) args[0]); + UnsignedInteger in1 = ((UnsignedInteger) args[1]); + UnsignedInteger in2 = ((UnsignedInteger) args[2]); + Integer result = ((in0.get() & ~in2.get()) | (~in1.get() & in2.get())); + + return new UnsignedInteger(result); + } + } + + @Override + public int getArity() { + return 3; + } + + @Override + public String toString() { + return "Mux2"; + } + } + + public static class Mux3 extends Function { + @Override + public UnsignedInteger run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " in but arity is " + getArity() + "."); + } else { + UnsignedInteger in0 = ((UnsignedInteger) args[0]); + UnsignedInteger in1 = ((UnsignedInteger) args[1]); + UnsignedInteger in2 = ((UnsignedInteger) args[2]); + Integer result = ((~in0.get() & ~in2.get()) | (in1.get() & in2.get())); + + return new UnsignedInteger(result); + } + } + + @Override + public int getArity() { + return 3; + } + + @Override + public String toString() { + return "Mux3"; + } + } + + public static class Mux4 extends Function { + @Override + public UnsignedInteger run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " in but arity is " + getArity() + "."); + } else { + UnsignedInteger in0 = ((UnsignedInteger) args[0]); + UnsignedInteger in1 = ((UnsignedInteger) args[1]); + UnsignedInteger in2 = ((UnsignedInteger) args[2]); + Integer result = ((~in0.get() & ~in2.get()) | (~in1.get() & in2.get())); + + return new UnsignedInteger(result); + } + } + + @Override + public int getArity() { + return 3; + } + + @Override + public String toString() { + return "Mux4"; + } + } +} diff --git a/src/jcgp/backend/function/DoubleArithmetic.java b/src/jcgp/backend/function/DoubleArithmetic.java deleted file mode 100644 index ce4826f..0000000 --- a/src/jcgp/backend/function/DoubleArithmetic.java +++ /dev/null @@ -1,589 +0,0 @@ -package jcgp.backend.function; - -import jcgp.backend.population.Connection; - -public class DoubleArithmetic extends FunctionSet { - - public final static double DIVISION_LIMIT = 0.0001; - - public DoubleArithmetic() { - name = "Double Arithmetic"; - functionList = new Function[]{ - new Absolute(), - new SquareRoot(), - new Reciprocal(), - new Sine(), - new Cosine(), - new Tangent(), - new Exponential(), - new HyperbolicSine(), - new HyperbolicCosine(), - new HyperbolicTangent(), - new NaturalLog(), - new LogBaseTen(), - new SineAB(), - new CosineAB(), - new Hypotenuse(), - new Power(), - new Addition(), - new Subtraction(), - new Multiplication(), - new Division()}; - - enableAll(); - } - - /** - * Absolute returns the positive value of input 0. - * - * @see Math.abs() - */ - public static class Absolute extends Function { - @Override - public Double run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - Double in0 = ((Double) connections[0].getValue()); - return Math.abs(in0); - } - } - - @Override - public int getArity() { - return 1; - } - - @Override - public String getName() { - return "Absolute"; - } - } - - /** - * Protected square root function, returns the square root of the absolute - * value of input 0. - * - * @see Math.abs(), Math.sqrt() - */ - public static class SquareRoot extends Function { - @Override - public Double run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - Double in0 = ((Double) connections[0].getValue()); - return Math.sqrt(Math.abs(in0)); - } - } - - @Override - public int getArity() { - return 1; - } - - @Override - public String getName() { - return "Square root"; - } - } - - /** - * Protected reciprocal function, returns (1 / input 0). If input 0 is less than - * {@link DoubleArithmetic.}DIVISION_LIMIT, this returns it unchanged. - * - */ - public static class Reciprocal extends Function { - @Override - public Double run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - Double in0 = ((Double) connections[0].getValue()); - return in0 < DIVISION_LIMIT ? in0 : (1 / in0); - } - } - - @Override - public int getArity() { - return 1; - } - - @Override - public String getName() { - return "Reciprocal"; - } - } - - /** - * Sine function, in radians. - * - * @see Math.sin() - */ - public static class Sine extends Function { - @Override - public Double run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - Double in0 = ((Double) connections[0].getValue()); - return Math.sin(in0); - } - } - - @Override - public int getArity() { - return 1; - } - - @Override - public String getName() { - return "Sin"; - } - } - - /** - * Cosine function, in radians. - * - * @see Math.cos() - */ - public static class Cosine extends Function { - @Override - public Double run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - Double in0 = ((Double) connections[0].getValue()); - return Math.cos(in0); - } - } - - @Override - public int getArity() { - return 1; - } - - @Override - public String getName() { - return "Cos"; - } - } - - /** - * Protected tangent function, in radians. Returns the tangent of input 0. - * If input 0 is less than {@link DoubleArithmetic.}DIVISION_LIMIT, - * this returns it unchanged. - * - * @see Math.tan() - */ - public static class Tangent extends Function { - @Override - public Double run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - Double in0 = ((Double) connections[0].getValue()); - return in0 < DIVISION_LIMIT ? in0 : Math.tan(in0); - } - } - - @Override - public int getArity() { - return 1; - } - - @Override - public String getName() { - return "Tan"; - } - } - - /** - * Exponential function. Returns the exponential of input 0. - * - * @see Math.exp() - */ - public static class Exponential extends Function { - @Override - public Double run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - Double in0 = ((Double) connections[0].getValue()); - return Math.exp(in0); - } - } - - @Override - public int getArity() { - return 1; - } - - @Override - public String getName() { - return "Exp"; - } - } - - /** - * Returns the hyperbolic sine of input 0. - * - * @see Math.sinh() - */ - public static class HyperbolicSine extends Function { - @Override - public Double run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - Double in0 = ((Double) connections[0].getValue()); - return Math.sinh(in0); - } - } - - @Override - public int getArity() { - return 1; - } - - @Override - public String getName() { - return "Sinh"; - } - } - - /** - * Returns the hyperbolic cosine of input 0. - * - * @see Math.cosh() - */ - public static class HyperbolicCosine extends Function { - @Override - public Double run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - Double in0 = ((Double) connections[0].getValue()); - return Math.cosh(in0); - } - } - - @Override - public int getArity() { - return 1; - } - - @Override - public String getName() { - return "Cosh"; - } - } - - /** - * Returns the hyperbolic tangent of input 0. - * - * @see Math.tanh() - */ - public static class HyperbolicTangent extends Function { - @Override - public Double run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - Double in0 = ((Double) connections[0].getValue()); - return Math.tanh(in0); - } - } - - @Override - public int getArity() { - return 1; - } - - @Override - public String getName() { - return "Tanh"; - } - } - - /** - * Protected natural log function. Returns the natural log of the absolute - * value of input 0. If input 0 is less than {@link DoubleArithmetic.}DIVISION_LIMIT, - * this returns it unchanged. - * - * @see Math.log(), Math.abs() - */ - public static class NaturalLog extends Function { - @Override - public Double run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - Double in0 = ((Double) connections[0].getValue()); - return in0 < DIVISION_LIMIT ? in0 : Math.log(Math.abs(in0)); - } - } - - @Override - public int getArity() { - return 1; - } - - @Override - public String getName() { - return "Ln"; - } - } - - /** - * Protected log base 10 function. Returns the log to base 10 the absolute - * value of input 0. If input 0 is less than {@link DoubleArithmetic.}DIVISION_LIMIT, - * this returns it unchanged. - * - * @see Math.log10(), Math.abs() - */ - public static class LogBaseTen extends Function { - @Override - public Double run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - Double in0 = ((Double) connections[0].getValue()); - return in0 < DIVISION_LIMIT ? in0 : Math.log10(Math.abs(in0)); - } - } - - @Override - public int getArity() { - return 1; - } - - @Override - public String getName() { - return "Log"; - } - } - - /** - * Sine of sum. Returns the sine of the sum of inputs 0 and 1. - * - * @see Math.sin() - */ - public static class SineAB extends Function { - @Override - public Double run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - Double in0 = ((Double) connections[0].getValue()); - Double in1 = ((Double) connections[1].getValue()); - return Math.sin(in0 + in1); - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "Sin(a+b)"; - } - } - - /** - * Cosine of sum. Returns the cosine of the sum of inputs 0 and 1. - * - * @see Math.cos() - */ - public static class CosineAB extends Function { - @Override - public Double run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - Double in0 = ((Double) connections[0].getValue()); - Double in1 = ((Double) connections[1].getValue()); - return Math.cos(in0 + in1); - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "Cos(a+b)"; - } - } - - /** - * Hypotenuse function. Returns the square root of input 0 squared - * plus input 1 squared. - * - * @see Math.hypot() - */ - public static class Hypotenuse extends Function { - @Override - public Double run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - Double in0 = ((Double) connections[0].getValue()); - Double in1 = ((Double) connections[1].getValue()); - return Math.hypot(in0, in1); - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "Hypotenuse"; - } - } - - /** - * Power function. Returns the absolute value of input 0 to the power of input 1. - * - * @see Math.abs(), Math.pow - */ - public static class Power extends Function { - @Override - public Double run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - Double in0 = ((Double) connections[0].getValue()); - Double in1 = ((Double) connections[1].getValue()); - return Math.pow(Math.abs(in0), in1); - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "Power"; - } - } - - /** - * Addition returns the sum of inputs 0 and 1. - * - */ - public static class Addition extends Function { - @Override - public Double run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - Double in0 = ((Double) connections[0].getValue()); - Double in1 = ((Double) connections[1].getValue()); - return in0 + in1; - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "Addition"; - } - } - - /** - * Subtraction returns the difference between inputs 0 and 1. - * - */ - public static class Subtraction extends Function { - @Override - public Double run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - Double in0 = ((Double) connections[0].getValue()); - Double in1 = ((Double) connections[1].getValue()); - return in0 - in1; - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "Subtraction"; - } - } - - /** - * Multiplication returns the product of inputs 0 and 1. - * - */ - public static class Multiplication extends Function { - @Override - public Double run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - Double in0 = ((Double) connections[0].getValue()); - Double in1 = ((Double) connections[1].getValue()); - return in0 * in1; - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "Multiplication"; - } - } - - /** - * Protected division, returns the quotient of input 0 (the dividend) and input 1 (the divisor). - * If the divisor is less than {@link DoubleArithmetic.}DIVISION_LIMIT, this returns the it unchanged. - * - */ - public static class Division extends Function { - @Override - public Double run(Connection... connections) { - if (connections.length < getArity()) { - throw new IllegalArgumentException(getName() + " received " + connections.length + " connections but arity is " + getArity() + "."); - } else { - Double in0 = ((Double) connections[0].getValue()); - Double in1 = ((Double) connections[1].getValue()); - - return in1 < DIVISION_LIMIT ? in0 : (in0 / in1); - } - } - - @Override - public int getArity() { - return 2; - } - - @Override - public String getName() { - return "Division"; - } - } -} diff --git a/src/jcgp/backend/function/Function.java b/src/jcgp/backend/function/Function.java index 30bbcf0..fdacac0 100644 --- a/src/jcgp/backend/function/Function.java +++ b/src/jcgp/backend/function/Function.java @@ -1,13 +1,28 @@ package jcgp.backend.function; -import jcgp.backend.exceptions.InvalidArgumentsException; -import jcgp.backend.population.Connection; - +/** + * Function is a callback wrapper. + * <br><br> + * A concrete implementation of Function overrides {@code run()} to perform + * any arbitrary operation on the arguments specified. It must also override + * {@code getArity()} to return the function arity. + * + * + * @author Eduardo Pedroni + * + */ public abstract class Function { - - public abstract Object run(Connection ... connections) throws InvalidArgumentsException; - public abstract int getArity(); + /** + * Executes the function. + * + * @param args the function arguments + * @return the function result + */ + public abstract Object run(Object... args); - public abstract String getName(); + /** + * @return the arity of the function. + */ + public abstract int getArity(); } diff --git a/src/jcgp/backend/function/FunctionSet.java b/src/jcgp/backend/function/FunctionSet.java index 1712b51..926ed68 100644 --- a/src/jcgp/backend/function/FunctionSet.java +++ b/src/jcgp/backend/function/FunctionSet.java @@ -6,6 +6,22 @@ import java.util.Iterator; /** + * FunctionSet encapsulates a group of functions. This is usually done to + * simplify the implementation of problem types. + * <br><br> + * FunctionSet contains a variety of useful methods for acquiring general + * information, such as the maximum arity across all functions and the total + * number of functions. + * <br><br> + * In addition, FunctionSet offers the ability to enable and disable functions. + * Accessing the functions through {@code getAllowedFunction()} will return + * allowed functions only, providing an easy way to control which functions + * can be used in mutations. + * <br><br> + * An implementation of FunctionSet must simply set the name field and initialise + * the functionList array with all of the functions. It is advisable to call + * {@code enableAll()} to enable all functions once the array is initialised. + * * * @author Eduardo Pedroni * @@ -15,25 +31,55 @@ public abstract class FunctionSet { protected ArrayList<Integer> allowedFunctions; protected String name; + /** + * @return the number of currently allowed functions. + */ public int getAllowedFunctionCount() { return allowedFunctions.size(); } + /** + * @return the total number of functions, including disabled ones. + */ public int getTotalFunctionCount() { return functionList.length; } + /** + * Returns an allowed function. This throws an + * ArrayIndexOutOfBoundsException if the supplied + * index is beyond the count of allowed functions. + * + * @param index the allowed function index. + * @return the allowed function object. + */ public Function getAllowedFunction(int index) { return functionList[allowedFunctions.get(index)]; } + /** + * Returns a function from the complete collection, + * enabled or disabled alike. This throws an + * ArrayIndexOutOfBoundsException if the supplied + * index is beyond the count of allowed functions. + * + * @param index the function index. + * @return the function object. + */ public Function getFunction(int index) { return functionList[index]; } + /** + * Computes and returns the maximum arity out of + * all the function, enabled or disabled. + * + * @return + */ public int getMaxArity(){ int arity = 0; for (Function function : functionList) { + // if a higher arity is found, store it if (function.getArity() > arity) { arity = function.getArity(); } @@ -41,25 +87,39 @@ public abstract class FunctionSet { return arity; } - public String getName() { - return name; - } - + /** + * Disables the indexed function. If the function + * is already disabled, this does nothing. + * + * @param index the function to disable. + */ public void disableFunction(int index) { + /* + * allowedFunctions is a list of the indices of all allowed functions, + * as addressed in functionList. This method iterates through the whole + * list of allowed functions and removes any elements which are equal + * to the specified index. + */ if (index < functionList.length) { for (Iterator<Integer> iterator = allowedFunctions.iterator(); iterator.hasNext();) { int function = iterator.next(); if (function == index) { iterator.remove(); - break; } } } else { - throw new IndexOutOfBoundsException("Function " + index + " does not exist, the set only has " + functionList.length + " functions."); + throw new ArrayIndexOutOfBoundsException("Function " + index + " does not exist, the set only has " + functionList.length + " functions."); } } + /** + * Disables the indexed function. If the function is + * already enabled, this does nothing. + * + * @param index the function to disable. + */ public void enableFunction(int index) { + // add the specified index to the list of allowed indices if (!allowedFunctions.contains(index)) { allowedFunctions.add(index); Collections.sort(allowedFunctions); @@ -71,16 +131,26 @@ public abstract class FunctionSet { return name; } - public boolean isEnabled(Function f) { + /** + * Checks if a specified function is enabled. If the function + * does not belong in the FunctionSet, this returns false. + * + * @param function the function to check. + * @return true if the function is enabled. + */ + public boolean isEnabled(Function function) { for (int i = 0; i < allowedFunctions.size(); i++) { - if (functionList[allowedFunctions.get(i)] == f) { + if (functionList[allowedFunctions.get(i)] == function) { return true; } } return false; } - protected void enableAll() { + /** + * Enables all functions. + */ + public void enableAll() { allowedFunctions = new ArrayList<Integer>(); for (int i = 0; i < functionList.length; i++) { allowedFunctions.add(i); diff --git a/src/jcgp/backend/function/SymbolicRegressionFunctions.java b/src/jcgp/backend/function/SymbolicRegressionFunctions.java new file mode 100644 index 0000000..a35f258 --- /dev/null +++ b/src/jcgp/backend/function/SymbolicRegressionFunctions.java @@ -0,0 +1,587 @@ +package jcgp.backend.function; + +public class SymbolicRegressionFunctions extends FunctionSet { + + public final static double DIVISION_LIMIT = 0.0001; + + public SymbolicRegressionFunctions() { + name = "Symbolic regression functions"; + functionList = new Function[] { + new Absolute(), + new SquareRoot(), + new Reciprocal(), + new Sine(), + new Cosine(), + new Tangent(), + new Exponential(), + new HyperbolicSine(), + new HyperbolicCosine(), + new HyperbolicTangent(), + new NaturalLog(), + new LogBaseTen(), + new SineAB(), + new CosineAB(), + new Hypotenuse(), + new Power(), + new Addition(), + new Subtraction(), + new Multiplication(), + new Division()}; + + enableAll(); + } + + /** + * Absolute returns the positive value of input 0. + * + * @see Math + */ + public static class Absolute extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + return Math.abs(in0); + } + } + + @Override + public int getArity() { + return 1; + } + + @Override + public String toString() { + return "Absolute"; + } + } + + /** + * Protected square root function, returns the square root of the absolute + * value of input 0. + * + * @see Math + */ + public static class SquareRoot extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + return Math.sqrt(Math.abs(in0)); + } + } + + @Override + public int getArity() { + return 1; + } + + @Override + public String toString() { + return "Square root"; + } + } + + /** + * Protected reciprocal function, returns (1 / input 0). If input 0 is less than + * {@code DoubleArithmetic.DIVISION_LIMIT}, this returns it unchanged. + * + */ + public static class Reciprocal extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + return in0 < DIVISION_LIMIT ? in0 : (1 / in0); + } + } + + @Override + public int getArity() { + return 1; + } + + @Override + public String toString() { + return "Reciprocal"; + } + } + + /** + * Sine function, in radians. + * + * @see Math + */ + public static class Sine extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + return Math.sin(in0); + } + } + + @Override + public int getArity() { + return 1; + } + + @Override + public String toString() { + return "Sin"; + } + } + + /** + * Cosine function, in radians. + * + * @see Math + */ + public static class Cosine extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + return Math.cos(in0); + } + } + + @Override + public int getArity() { + return 1; + } + + @Override + public String toString() { + return "Cos"; + } + } + + /** + * Protected tangent function, in radians. Returns the tangent of input 0. + * If input 0 is less than {@link DoubleArithmetic.}DIVISION_LIMIT, + * this returns it unchanged. + * + * @see Math + */ + public static class Tangent extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + return in0 < DIVISION_LIMIT ? in0 : Math.tan(in0); + } + } + + @Override + public int getArity() { + return 1; + } + + @Override + public String toString() { + return "Tan"; + } + } + + /** + * Exponential function. Returns the exponential of input 0. + * + * @see Math + */ + public static class Exponential extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + return Math.exp(in0); + } + } + + @Override + public int getArity() { + return 1; + } + + @Override + public String toString() { + return "Exp"; + } + } + + /** + * Returns the hyperbolic sine of input 0. + * + * @see Math + */ + public static class HyperbolicSine extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + return Math.sinh(in0); + } + } + + @Override + public int getArity() { + return 1; + } + + @Override + public String toString() { + return "Sinh"; + } + } + + /** + * Returns the hyperbolic cosine of input 0. + * + * @see Math + */ + public static class HyperbolicCosine extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + return Math.cosh(in0); + } + } + + @Override + public int getArity() { + return 1; + } + + @Override + public String toString() { + return "Cosh"; + } + } + + /** + * Returns the hyperbolic tangent of input 0. + * + * @see Math + */ + public static class HyperbolicTangent extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + return Math.tanh(in0); + } + } + + @Override + public int getArity() { + return 1; + } + + @Override + public String toString() { + return "Tanh"; + } + } + + /** + * Protected natural log function. Returns the natural log of the absolute + * value of input 0. If input 0 is less than {@link DoubleArithmetic.}DIVISION_LIMIT, + * this returns it unchanged. + * + * @see Math + */ + public static class NaturalLog extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + return in0 < DIVISION_LIMIT ? in0 : Math.log(Math.abs(in0)); + } + } + + @Override + public int getArity() { + return 1; + } + + @Override + public String toString() { + return "Ln"; + } + } + + /** + * Protected log base 10 function. Returns the log to base 10 the absolute + * value of input 0. If input 0 is less than {@link DoubleArithmetic.}DIVISION_LIMIT, + * this returns it unchanged. + * + * @see Math + */ + public static class LogBaseTen extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + return in0 < DIVISION_LIMIT ? in0 : Math.log10(Math.abs(in0)); + } + } + + @Override + public int getArity() { + return 1; + } + + @Override + public String toString() { + return "Log"; + } + } + + /** + * Sine of sum. Returns the sine of the sum of inputs 0 and 1. + * + * @see Math + */ + public static class SineAB extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + Double in1 = ((Double) args[1]); + return Math.sin(in0 + in1); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Sin(a+b)"; + } + } + + /** + * Cosine of sum. Returns the cosine of the sum of inputs 0 and 1. + * + * @see Math + */ + public static class CosineAB extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + Double in1 = ((Double) args[1]); + return Math.cos(in0 + in1); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Cos(a+b)"; + } + } + + /** + * Hypotenuse function. Returns the square root of input 0 squared + * plus input 1 squared. + * + * @see Math + */ + public static class Hypotenuse extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + Double in1 = ((Double) args[1]); + return Math.hypot(in0, in1); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Hypotenuse"; + } + } + + /** + * Power function. Returns the absolute value of input 0 to the power of input 1. + * + * @see Math + */ + public static class Power extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + Double in1 = ((Double) args[1]); + return Math.pow(Math.abs(in0), in1); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Power"; + } + } + + /** + * Addition returns the sum of inputs 0 and 1. + * + */ + public static class Addition extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + Double in1 = ((Double) args[1]); + return in0 + in1; + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Addition"; + } + } + + /** + * Subtraction returns the difference between inputs 0 and 1. + * + */ + public static class Subtraction extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + Double in1 = ((Double) args[1]); + return in0 - in1; + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Subtraction"; + } + } + + /** + * Multiplication returns the product of inputs 0 and 1. + * + */ + public static class Multiplication extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + Double in1 = ((Double) args[1]); + return in0 * in1; + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Multiplication"; + } + } + + /** + * Protected division, returns the quotient of input 0 (the dividend) and input 1 (the divisor). + * If the divisor is less than {@code DoubleArithmetic.DIVISION_LIMIT}, this returns it unchanged. + * + */ + public static class Division extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + Double in1 = ((Double) args[1]); + + return in1 < DIVISION_LIMIT ? in0 : (in0 / in1); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Division"; + } + } +} diff --git a/src/jcgp/backend/function/TravellingSalesmanFunctions.java b/src/jcgp/backend/function/TravellingSalesmanFunctions.java new file mode 100644 index 0000000..472b7ad --- /dev/null +++ b/src/jcgp/backend/function/TravellingSalesmanFunctions.java @@ -0,0 +1,396 @@ +package jcgp.backend.function; + +public class TravellingSalesmanFunctions extends FunctionSet { + + public TravellingSalesmanFunctions() { + name = "Travelling salesman functions"; + functionList = new Function[]{ + new SquareRoot(), + new Square(), + new Cube(), + new ScaledExponential(), + new AbsoluteSineAB(), + new AbsoluteCosineAB(), + new AbsoluteHyperbolicTangentAB(), + new ScaledHypotenuse(), + new ScaledAddition(), + new SymmetricSubtraction(), + new Multiplication(), + new BoundedDivision() }; + + enableAll(); + } + + /** + * Protected square root function, returns the square root of the absolute + * value of input 0. + * + * @see Math + */ + public static class SquareRoot extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + return Math.sqrt(Math.abs(in0)); + } + } + + @Override + public int getArity() { + return 1; + } + + @Override + public String toString() { + return "Square root"; + } + } + + /** + * Square function, returns the square of the + * value of input 0. + * + */ + public static class Square extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + return in0 * in0; + } + } + + @Override + public int getArity() { + return 1; + } + + @Override + public String toString() { + return "Square"; + } + } + + /** + * Cube function, returns the value of input 0 cubed. + * + */ + public static class Cube extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + return in0 * in0 * in0; + } + } + + @Override + public int getArity() { + return 1; + } + + @Override + public String toString() { + return "Cube"; + } + } + + /** + * Scaled exponential function. Returns the exponential of input 0 + * scaled to the range 0 < x < 1. + * + * @see Math + */ + public static class ScaledExponential extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + return (Math.exp(in0) - 1) / (Math.exp(-1) - 1); + } + } + + @Override + public int getArity() { + return 1; + } + + @Override + public String toString() { + return "Scaled Exp"; + } + } + + /** + * Sine of sum. Returns the absolute value of the sine + * of the sum of inputs 0 and 1, in radians. + * + * @see Math + */ + public static class AbsoluteSineAB extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + Double in1 = ((Double) args[1]); + return Math.abs(Math.sin(in0 + in1)); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Abs Sin(a+b)"; + } + } + + /** + * Cosine of sum. Returns the absolute value of the cosine + * of the sum of inputs 0 and 1, in radians. + * + * @see Math + */ + public static class AbsoluteCosineAB extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + Double in1 = ((Double) args[1]); + return Math.abs(Math.cos(in0 + in1)); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Abs Cos(a+b)"; + } + } + + /** + * Hyperbolic tangent of sum. Returns the absolute value of the sine + * of the sum of inputs 0 and 1, in radians. + * + * @see Math + */ + public static class AbsoluteHyperbolicTangentAB extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + Double in1 = ((Double) args[1]); + return Math.abs(Math.tanh(in0 + in1)); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Abs Tanh(a+b)"; + } + } + + /** + * Scaled hypotenuse function. Returns the square root of input 0 squared + * plus input 1 squared, scaled to the range 0 < x < 1. + * + * @see Math + */ + public static class ScaledHypotenuse extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + Double in1 = ((Double) args[1]); + return Math.hypot(in0, in1) / Math.sqrt(2); + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Scaled Hypotenuse"; + } + } + + /** + * Scaled addition returns the sum of inputs 0 and 1 scaled + * to the range 0 < x < 1. + * + */ + public static class ScaledAddition extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + Double in1 = ((Double) args[1]); + return (in0 + in1) / 2.0; + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Scaled Addition"; + } + } + + /** + * Symmetric subtraction returns the absolute difference between inputs 0 and 1, + * scaled to the range 0 < x < 1. + * + */ + public static class SymmetricSubtraction extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + Double in1 = ((Double) args[1]); + return Math.abs(in0 - in1) / 2.0; + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Symmetric Subtraction"; + } + } + + /** + * Multiplication returns the product of inputs 0 and 1. + * + */ + public static class Multiplication extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + Double in1 = ((Double) args[1]); + return in0 * in1; + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Multiplication"; + } + } + + /** + * Multiplication returns the product of inputs 0 and 1, squared. + * + */ + public static class SquaredMultiplication extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = ((Double) args[0]); + Double in1 = ((Double) args[1]); + return in0 * in1 * in0 * in1; + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Squared Multiplication"; + } + } + + /** + * Bounded division, returns the quotient of the two inputs where the larger + * is the denominator. + * + */ + public static class BoundedDivision extends Function { + @Override + public Double run(Object... args) { + if (args.length < getArity()) { + throw new IllegalArgumentException(toString() + " received " + args.length + " arguments but arity is " + getArity() + "."); + } else { + Double in0 = Math.abs((Double) args[0]); + Double in1 = Math.abs((Double) args[1]); + Double result; + + if (in1 < 1e-10) { + result = 1.0; + } else if (in1 > in0) { + result = in0 / in1; + } else { + result = in1 / in0; + } + + return result; + } + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String toString() { + return "Bounded Division"; + } + } +} diff --git a/src/jcgp/backend/modules/Module.java b/src/jcgp/backend/modules/Module.java index a6b4d73..7efbf3a 100644 --- a/src/jcgp/backend/modules/Module.java +++ b/src/jcgp/backend/modules/Module.java @@ -2,8 +2,22 @@ package jcgp.backend.modules; import jcgp.backend.resources.parameters.Parameter; +/** + * This interface defines the expected behaviour of a module. Specifically, a module + * is expected to be able to return a list of local parameters. When a user interface + * is used, it is expected to display the parameters of each module and allow user + * interaction for parameters which are not monitors. + * + * @see Parameter + * + * @author Eduardo Pedroni + * + */ public interface Module { + /** + * @return a list of generic parameters exposed by the module. + */ public abstract Parameter<?>[] getLocalParameters(); } diff --git a/src/jcgp/backend/modules/es/EvolutionaryStrategy.java b/src/jcgp/backend/modules/es/EvolutionaryStrategy.java index 8ab287d..70e3cd2 100644 --- a/src/jcgp/backend/modules/es/EvolutionaryStrategy.java +++ b/src/jcgp/backend/modules/es/EvolutionaryStrategy.java @@ -5,8 +5,42 @@ import jcgp.backend.modules.mutator.Mutator; import jcgp.backend.population.Population; import jcgp.backend.resources.Resources; +/** + * This interface specifies the required behaviour of an evolutionary strategy. The evolutionary + * strategy's job is to generate the next population of solutions. In JCGP this is done by modifying + * the provided population object rather than creating a new one. + * <br><br> + * A typical implementation of EvolutionaryStratey iterates through the chromosomes + * in the population and selects the individual(s) to be promoted. It then uses + * {@code mutator.mutate()} to generically mutate the promoted individual(s). Parameter-dependent + * strategies can be implemented by accessing the parameters via the resources + * argument. + * <br><br> + * Parameters may be specified to control the implemented strategy. Any parameters + * returned by {@code getLocalParameters()} should be displayed by the user interface, + * if it is being used. See {@link Parameter} for more information. + * <br><br> + * It is advisable to use {@code Resources.reportln()} and {@code Resources.report()} + * to print any relevant information. Note that reportln() and report() are affected + * by the report interval base parameter. Use {@code Resources.println()} and + * {@code Resources.print()} to print information regardless of the current generation. + * See {@link Resources} for more information. + * + * @see Module + * + * @author Eduardo Pedroni + * + */ public interface EvolutionaryStrategy extends Module { + /** + * Performs the selection algorithm and uses the mutator to create + * the next generation of solutions. + * + * @param population the population to evolve. + * @param mutator the mutator with which to mutate the promoted individuals. + * @param resources parameters and utilities for optional reference. + */ public abstract void evolve(Population population, Mutator mutator, Resources resources); } diff --git a/src/jcgp/backend/modules/es/MuPlusLambda.java b/src/jcgp/backend/modules/es/MuPlusLambda.java index 6a3883b..50bf265 100644 --- a/src/jcgp/backend/modules/es/MuPlusLambda.java +++ b/src/jcgp/backend/modules/es/MuPlusLambda.java @@ -9,41 +9,59 @@ import jcgp.backend.resources.parameters.Parameter; import jcgp.backend.resources.parameters.ParameterStatus; /** - * (μ + λ) EA. - * + * (μ + λ)-ES + * <br><br> + * This strategy selects the μ fittest chromosomes from the population. + * The promoted individuals are copied into the new population and mutated + * λ times, but also carried forward unchanged. The total population size + * is μ + λ. + * <br><br> + * Two integer parameters are used to control this strategy: parents + * and offspring. They are constrained in that they must always add up to + * the population size, and must never be smaller than 1. + * <br> + * One additional parameter, report, controls whether a detailed log of the + * algorithm's operation is to be printed or not. Reports respect the report + * interval base parameter. * + * @see EvolutionaryStrategy * @author Eduardo Pedroni * */ public class MuPlusLambda implements EvolutionaryStrategy { - private IntegerParameter parents, offspring; + private IntegerParameter mu, lambda; private BooleanParameter report; + /** + * Creates a new instance of MuPlusLambda. + * + * @param resources a reference to the experiment's resources. + */ public MuPlusLambda(final Resources resources) { - parents = new IntegerParameter(1, "Parents") { + mu = new IntegerParameter(1, "Parents (μ)") { @Override public void validate(Number newValue) { - if (newValue.intValue() + offspring.get() != resources.populationSize()) { + if (newValue.intValue() + lambda.get() != resources.populationSize()) { status = ParameterStatus.INVALID; status.setDetails("Parents + offspring must equal population size."); } else if (newValue.intValue() <= 0) { status = ParameterStatus.INVALID; - status.setDetails("EA needs at least 1 parent."); + status.setDetails("ES needs at least 1 parent."); } else { status = ParameterStatus.VALID; } } }; - offspring = new IntegerParameter(4, "Offspring") { + lambda = new IntegerParameter(4, "Offspring (λ)") { @Override public void validate(Number newValue) { - if (newValue.intValue() + parents.get() != resources.populationSize()) { + if (newValue.intValue() + mu.get() != resources.populationSize()) { status = ParameterStatus.INVALID; status.setDetails("Parents + offspring must equal population size."); } else if (newValue.intValue() <= 0) { status = ParameterStatus.INVALID; - status.setDetails("EA needs at least 1 offspring."); + status.setDetails("ES needs at least 1 offspring."); } else { status = ParameterStatus.VALID; } @@ -58,32 +76,31 @@ public class MuPlusLambda implements EvolutionaryStrategy { } @Override - public void evolve(Population population, Mutator mutator, Resources resources) { - // select fittest chromosomes - if (report.get()) resources.reportln("[ES] Selected chromosome: " + population.getFittestIndex()); - - // create copies of fittest chromosome, mutate them - for (int i = 0; i < resources.populationSize(); i++) { - if (i != population.getFittestIndex()) { - if (report.get()) resources.reportln("[ES] Copying fittest chromosome to population position " + i); - population.copyChromosome(population.getFittestIndex(), i); - if (report.get()) resources.reportln("[ES] Mutating copied chromosome"); - mutator.mutate(population.getChromosome(i), resources); - } + public void evolve(Population population, Mutator mutator, Resources resources) { + /* Population is sorted in ascending order of fitness, so leave the last + * mu chromosomes intact + */ + for (int i = 0; i < resources.populationSize() - mu.get(); i++) { + // select a random parent out of the lambda offspring individuals + int randomParent = resources.populationSize() - 1 - resources.getRandomInt(mu.get()); + if (report.get()) resources.reportln("[ES] Copying Chr " + randomParent + " to population position " + i); + population.copyChromosome(randomParent, i); + + // mutate selected chromosome + if (report.get()) resources.reportln("[ES] Mutating copied chromosome"); + mutator.mutate(population.getChromosome(i), resources); } - if (report.get()) resources.reportln("[ES] Generation is complete."); - + if (report.get()) resources.reportln("[ES] Generation is complete"); } @Override public Parameter<?>[] getLocalParameters() { - return new Parameter[] {parents, offspring, report}; + return new Parameter[] {mu, lambda, report}; } @Override public String toString() { return "(μ + λ)"; } - } diff --git a/src/jcgp/backend/modules/es/TournamentSelection.java b/src/jcgp/backend/modules/es/TournamentSelection.java index 7cc9706..43fea81 100644 --- a/src/jcgp/backend/modules/es/TournamentSelection.java +++ b/src/jcgp/backend/modules/es/TournamentSelection.java @@ -1,35 +1,118 @@ package jcgp.backend.modules.es; +import java.util.Arrays; + import jcgp.backend.modules.mutator.Mutator; +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.IntegerParameter; import jcgp.backend.resources.parameters.Parameter; +import jcgp.backend.resources.parameters.ParameterStatus; +/** + * Tournament selection + * <br><br> + * This strategy generates a new population by selecting a specified number + * of chromosomes from the original population and selecting the fittest out + * of the isolated subset (the tournament). The selected individual is mutated + * using the specified mutator. This process is repeated until the new population + * is complete. + * <br><br> + * One integer parameter is used to control this strategy: tournament + * size. This must always be greater than 0 and smaller than or equal to the + * population size. Setting it to equal population size results in the same + * chromosome being selected for every tournament, and setting it to 1 leads + * to an effectively random search. + * <br> + * One additional parameter, report, controls whether a detailed log of the + * algorithm's operation is to be printed or not. Reports respect the report + * interval base parameter. + * + * @see EvolutionaryStrategy + * @author Eduardo Pedroni + * + */ public class TournamentSelection implements EvolutionaryStrategy { private IntegerParameter tournamentSize; + private BooleanParameter report; - public TournamentSelection(Resources resources) { + /** + * Creates a new instance of TournamentSelection. + * + * @param resources a reference to the experiment's resources. + */ + public TournamentSelection(final Resources resources) { tournamentSize = new IntegerParameter(1, "Tournament size") { @Override public void validate(Number newValue) { - // TODO this + if (newValue.intValue() <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Tournament size must be greater than 0."); + } else if (newValue.intValue() > resources.populationSize()) { + status = ParameterStatus.INVALID; + status.setDetails("Tournament size must not be greater than the population size."); + } else if (newValue.intValue() == 1) { + status = ParameterStatus.WARNING; + status.setDetails("A tournament size of 1 results in a random search."); + } else if (newValue.intValue() == resources.populationSize()) { + status = ParameterStatus.WARNING; + status.setDetails("A tournament size equal to population size results in the same individual being selected every time."); + } else { + status = ParameterStatus.VALID; + } + } + }; + report = new BooleanParameter(false, "Report") { + @Override + public void validate(Boolean newValue) { + // blank } }; } @Override public Parameter<?>[] getLocalParameters() { - return new Parameter[] {tournamentSize}; + return new Parameter[] {tournamentSize, report}; } @Override - public void evolve(Population population, Mutator mutator, - Resources parameters) { - tournamentSize.set(tournamentSize.get() + 1); - // TODO implement this - + public void evolve(Population population, Mutator mutator, Resources resources) { + /* Create an entirely new population by isolating random subsets of + * the original population and choosing the fittest individual within + * that subset. Each chosen individual is mutated and copied back into the + * population. + */ + Chromosome[] newPopulation = new Chromosome[resources.populationSize()]; + + // start by selecting all of the chromosomes that will be promoted + for (int i = 0; i < resources.populationSize(); i++) { + if (report.get()) resources.reportln("[ES] Starting tournament " + i); + + /* the population is sorted in ascending order of fitness, + * meaning the higher the index of the contender, the fitter + * it is + */ + int[] contenders = new int[tournamentSize.get()]; + for (int t = 0; t < tournamentSize.get() - 1; t++) { + contenders[t] = resources.getRandomInt(resources.populationSize()); + } + if (report.get()) resources.reportln("[ES] Selected contenders: " + Arrays.toString(contenders)); + Arrays.sort(contenders); + if (report.get()) resources.reportln("[ES] Chr " + contenders[contenders.length - 1] + " wins the tournament, copying and mutating..."); + // create a copy of the selected chromosome and mutate it + newPopulation[i] = new Chromosome(population.getChromosome(contenders[contenders.length - 1])); + mutator.mutate(newPopulation[i], resources); + } + if (report.get()) resources.reportln("[ES] Tournaments are finished, copying new chromosomes into population"); + // newPopulation has been generated, copy into the population + for (int c = 0; c < resources.populationSize(); c++) { + population.getChromosome(c).copyGenes(newPopulation[c]); + } + + if (report.get()) resources.reportln("[ES] Generation is complete"); } @Override diff --git a/src/jcgp/backend/modules/mutator/FixedPointMutator.java b/src/jcgp/backend/modules/mutator/FixedPointMutator.java new file mode 100644 index 0000000..4088918 --- /dev/null +++ b/src/jcgp/backend/modules/mutator/FixedPointMutator.java @@ -0,0 +1,64 @@ +package jcgp.backend.modules.mutator; + +import jcgp.backend.resources.Resources; +import jcgp.backend.resources.parameters.BooleanParameter; +import jcgp.backend.resources.parameters.IntegerParameter; +import jcgp.backend.resources.parameters.Parameter; +import jcgp.backend.resources.parameters.ParameterStatus; + +/** + * Fixed point mutator + * <br><br> + * This operator uses the point mutator + * algorithm to mutate a user-defined fixed + * number of genes. + * + * + * @see PointMutator + * @author Eduardo Pedroni + * + */ +public class FixedPointMutator extends PointMutator { + + private IntegerParameter geneMutated; + private BooleanParameter report; + + /** + * Creates a new instance of FixedPointMutator. + * + * @param resources a reference to the experiment's resources. + */ + public FixedPointMutator(final Resources resources) { + geneMutated = new IntegerParameter(5, "Genes mutated", false, false) { + @Override + public void validate(Number newValue) { + if (newValue.intValue() <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("At least 1 mutation must take place."); + } else if (newValue.intValue() > (resources.nodes() * (resources.arity() + 1)) + resources.outputs()) { + status = ParameterStatus.WARNING; + status.setDetails("More genes are mutated than there are genes in the genotype."); + } else { + status = ParameterStatus.VALID; + } + } + }; + report = new BooleanParameter(false, "Report") { + @Override + public void validate(Boolean newValue) { + // blank + } + }; + } + + @Override + public Parameter<?>[] getLocalParameters() { + return new Parameter[] {geneMutated, report}; + } + + @Override + public String toString() { + return "Fixed point mutation"; + } + +} diff --git a/src/jcgp/backend/modules/mutator/Mutator.java b/src/jcgp/backend/modules/mutator/Mutator.java index 1d5b99a..7435cc1 100644 --- a/src/jcgp/backend/modules/mutator/Mutator.java +++ b/src/jcgp/backend/modules/mutator/Mutator.java @@ -4,8 +4,35 @@ import jcgp.backend.modules.Module; import jcgp.backend.population.Chromosome; import jcgp.backend.resources.Resources; +/** + * This interface specifies the required behaviour of a mutation operator. Its job is + * to modify the connections and functions of the chromosome according to the operator's + * parameters. + * <br><br> + * Parameters may be specified to control the implemented mutation. Any parameters + * returned by {@code getLocalParameters()} should be displayed by the user interface, + * if it is being used. See {@link Parameter} for more information. + * <br><br> + * It is advisable to use {@code Resources.reportln()} and {@code Resources.report()} + * to print any relevant information. Note that reportln() and report() are affected + * by the report interval base parameter. Use {@code Resources.println()} and + * {@code Resources.print()} to print information regardless of the current generation. + * See {@link Resources} for more information. + * + * @see Module + * + * @author Eduardo Pedroni + * + */ public interface Mutator extends Module { + /** + * Applies mutations to the specified chromosome according + * to the parameter values. + * + * @param chromosome the chromosome to mutate. + * @param resources parameters and utilities for optional reference. + */ void mutate(Chromosome chromosome, Resources resources); } diff --git a/src/jcgp/backend/modules/mutator/PercentPointMutator.java b/src/jcgp/backend/modules/mutator/PercentPointMutator.java new file mode 100644 index 0000000..4057027 --- /dev/null +++ b/src/jcgp/backend/modules/mutator/PercentPointMutator.java @@ -0,0 +1,73 @@ +package jcgp.backend.modules.mutator; + +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; + +/** + * Percent point mutator + * <br><br> + * This operator calculates how many genes to mutate based on the mutation rate + * parameter. The total number of genes is computed from the number of nodes, + * the arity and the number of outputs. It then uses the point mutation + * algorithm to perform the required number of mutations. + * + * + * @see PointMutator + * @author Eduardo Pedroni + * + */ +public class PercentPointMutator extends PointMutator { + + private DoubleParameter mutationRate; + private IntegerParameter genesMutated; + private BooleanParameter report; + + /** + * Creates a new instance of PointMutator. + * + * @param resources a reference to the experiment's resources. + */ + public PercentPointMutator(final Resources resources) { + mutationRate = new DoubleParameter(10, "Percent mutation", false, false) { + @Override + public void validate(Number newValue) { + genesMutated.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"); + } else if (genesMutated.get() <= 0) { + status = ParameterStatus.WARNING; + status.setDetails("With mutation rate " + mutationRate.get() + ", 0 genes will be mutated."); + } else { + status = ParameterStatus.VALID; + } + } + }; + genesMutated = 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) { + // blank + } + }; + } + + @Override + public Parameter<?>[] getLocalParameters() { + return new Parameter[] {mutationRate, genesMutated, report}; + } + + @Override + public String toString() { + return "Percent point mutation"; + } +} diff --git a/src/jcgp/backend/modules/mutator/PointMutator.java b/src/jcgp/backend/modules/mutator/PointMutator.java index 44c453a..17c6996 100644 --- a/src/jcgp/backend/modules/mutator/PointMutator.java +++ b/src/jcgp/backend/modules/mutator/PointMutator.java @@ -6,88 +6,69 @@ import jcgp.backend.population.Node; 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; +/** + * Point mutator + * <br><br> + * In point mutation, a number of random genes + * is picked and mutated until all required + * mutations have been performed. The actual number + * of genes to be mutated can be defined in any + * arbitrary way. + * + * @author Eduardo Pedroni + * + */ +public abstract class PointMutator implements Mutator { - 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"); - } else if ((int) ((newValue.doubleValue() / 100) * (double) resources.nodes()) <= 0) { - status = ParameterStatus.WARNING; - status.setDetails("With mutation rate " + mutationRate.get() + ", 0 genes will be mutated."); - } else { - status = ParameterStatus.VALID; - } - } - }; - 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) { - // blank - } - }; - } + protected IntegerParameter genesMutated; + protected BooleanParameter report; @Override public void mutate(Chromosome chromosome, Resources resources) { - if (report.get()) resources.reportln("[Mutator] Number of mutations to be performed: " + nodesMutated.get()); - for (int i = 0; i < nodesMutated.get(); i++) { + if (report.get()) resources.reportln("[Mutator] Number of mutations to be performed: " + genesMutated.get()); + + // for however many genes must be mutated + for (int i = 0; i < genesMutated.get(); i++) { + MutableElement m = chromosome.getRandomMutableElement(); - if (report.get()) resources.report("[Mutator] Mutation " + i + " selected " + m.toString() + ", "); + if (report.get()) resources.report("[Mutator] Mutation " + i + " selected " + m + ", "); + + // outputs and nodes are mutated differently if (m instanceof Output) { - if (report.get()) resources.report("changed source from " + ((Output) m).getSource().toString() + " "); + if (report.get()) resources.report("changed source from " + ((Output) m).getSource() + " "); + // outputs are easy, simply set to a different random connection, any will do m.setConnection(0, chromosome.getRandomConnection()); - if (report.get()) resources.reportln("to " + ((Output) m).getSource().toString()); + if (report.get()) resources.reportln("to " + ((Output) m).getSource()); } else if (m instanceof Node) { + /* nodes are more complicated, first we must decide whether to mutate the function + * or a connection + * we do this by generating a random int between 0 and 1 + arity + */ int geneType = resources.getRandomInt(1 + resources.arity()); + + // if the int is less than 1, mutate function, else mutate connections if (geneType < 1) { - if (report.get()) resources.report("changed function from " + ((Node) m).getFunction().getName() + " "); + if (report.get()) resources.report("changed function from " + ((Node) m).getFunction() + " "); ((Node) m).setFunction(resources.getRandomFunction()); - if (report.get()) resources.reportln("to " + ((Node) m).getFunction().getName()); + if (report.get()) resources.reportln("to " + ((Node) m).getFunction()); } else { - int connection = resources.getRandomInt(resources.arity()); - if (report.get()) resources.report("changed connection " + connection + " from " + ((Node) m).getConnection(connection) + " "); + // if we decided to mutate connection, subtract 1 from geneType so it fits into the arity range + geneType -= 1; + if (report.get()) resources.report("changed connection " + geneType + " from " + ((Node) m).getConnection(geneType) + " "); - m.setConnection(connection, chromosome.getRandomConnection(((Node) m).getColumn())); + m.setConnection(geneType, chromosome.getRandomConnection(((Node) m).getColumn())); - if (report.get()) resources.reportln("to " + ((Node) m).getConnection(connection)); + if (report.get()) resources.reportln("to " + ((Node) m).getConnection(geneType)); } } } } - @Override - public Parameter<?>[] getLocalParameters() { - return new Parameter[] {mutationRate, nodesMutated, report}; - } - - @Override - public String toString() { - return "Point mutation"; - } - } diff --git a/src/jcgp/backend/modules/mutator/ProbabilisticMutator.java b/src/jcgp/backend/modules/mutator/ProbabilisticMutator.java new file mode 100644 index 0000000..e3c1d03 --- /dev/null +++ b/src/jcgp/backend/modules/mutator/ProbabilisticMutator.java @@ -0,0 +1,131 @@ +package jcgp.backend.modules.mutator; + +import jcgp.backend.population.Chromosome; +import jcgp.backend.population.Node; +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.Parameter; +import jcgp.backend.resources.parameters.ParameterStatus; + +/** + * Probabilistic mutator + * <br><br> + * This operator iterates through every mutable gene in the chromosome and + * decides whether to mutate each of them individually. + * The decision is made based on the difference between the mutation probability + * and a randomly generated double between 0 and 100. + * + * + * @see Mutator + * @author Eduardo Pedroni + * + */ +public class ProbabilisticMutator implements Mutator { + + private DoubleParameter mutationProbability; + private BooleanParameter report; + + private Resources resources; + + /** + * Creates a new instance of ProbabilisticMutator. + * + * @param resources a reference to the experiment's resources. + */ + public ProbabilisticMutator(Resources resources) { + this.resources = resources; + + mutationProbability = new DoubleParameter(10, "Mutation probability", false, false) { + @Override + public void validate(Number newValue) { + if (newValue.doubleValue() <= 0 || newValue.doubleValue() > 100) { + status = ParameterStatus.INVALID; + status.setDetails("Mutation rate must be > 0 and <= 100"); + } else { + status = ParameterStatus.VALID; + } + } + }; + report = new BooleanParameter(false, "Report") { + @Override + public void validate(Boolean newValue) { + // blank + } + }; + } + + @Override + public Parameter<?>[] getLocalParameters() { + return new Parameter<?>[] {mutationProbability, report}; + } + + @Override + public void mutate(Chromosome chromosome, Resources resources) { + if (report.get()) resources.reportln("[Mutator] Starting mutations"); + + // go through nodes - [rows][columns] + for (int r = 0; r < resources.rows(); r++) { + for (int c = 0; c < resources.columns(); c++) { + // go through all connections + for (int a = 0; a < resources.arity(); a++) { + if (mutate()) { + Node n = chromosome.getNode(r, c); + + if (report.get()) resources.report("[Mutator] Mutating " + n + + ", changed connection " + a + " from " + n.getConnection(a) + " "); + + n.setConnection(a, chromosome.getRandomConnection(c)); + + if (report.get()) resources.reportln("to " + n.getConnection(a)); + + } + } + // deal with node function next + if (mutate()) { + Node n = chromosome.getNode(r, c); + if (report.get()) resources.report("[Mutator] Mutating " + n + + ", changed function from " + n.getFunction()); + + n.setFunction(resources.getRandomFunction()); + + if (report.get()) resources.reportln(" to " + n.getFunction()); + } + } + } + // finally, mutate outputs + for (int o = 0; o < resources.outputs(); o++) { + if (mutate()) { + Output out = chromosome.getOutput(o); + + if (report.get()) resources.report("[Mutator] Mutating " + out + + ", changed source from " + out.getSource()); + + out.setConnection(0, chromosome.getRandomConnection()); + + if (report.get()) resources.reportln("to " + out.getSource()); + } + } + + if (report.get()) resources.reportln("[Mutator] Mutation finished "); + + } + + /** + * This method offers a shorthand to decide whether a mutation should occur or not. + * A random double is generated in the range 0 <= x < 100 and compared with the + * mutation probability parameter. If the generated number is less than the mutation + * probability, this returns true meaning a mutation should occur. + * + * @return true if a mutation should be performed, false if otherwise. + */ + private boolean mutate() { + return resources.getRandomDouble(100) < mutationProbability.get(); + } + + @Override + public String toString() { + return "Probabilistic mutation"; + } +} diff --git a/src/jcgp/backend/modules/problem/DigitalCircuitProblem.java b/src/jcgp/backend/modules/problem/DigitalCircuitProblem.java index 3c30e4c..099f527 100644 --- a/src/jcgp/backend/modules/problem/DigitalCircuitProblem.java +++ b/src/jcgp/backend/modules/problem/DigitalCircuitProblem.java @@ -1,6 +1,6 @@ package jcgp.backend.modules.problem; -import jcgp.backend.function.BitwiseLogic; +import jcgp.backend.function.DigitalCircuitFunctions; import jcgp.backend.function.UnsignedInteger; import jcgp.backend.population.Chromosome; import jcgp.backend.population.Population; @@ -10,14 +10,13 @@ public class DigitalCircuitProblem extends TestCaseProblem<UnsignedInteger> { public DigitalCircuitProblem(Resources resources) { super(resources); - functionSet = new BitwiseLogic(); + functionSet = new DigitalCircuitFunctions(); + setProblemName("Symbolic regression"); + setFileExtension(".plu"); } @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 @@ -42,10 +41,10 @@ public class DigitalCircuitProblem extends TestCaseProblem<UnsignedInteger> { // assign the resulting fitness to the respective individual population.getChromosome(i).setFitness(fitness); - if (fitness >= population.getFittest().getFitness()) { - population.setFittest(i); - } } + + // sort population + population.sortAscending(); } @Override @@ -77,9 +76,4 @@ public class DigitalCircuitProblem extends TestCaseProblem<UnsignedInteger> { public boolean isPerfectSolution(Chromosome fittest) { return fittest.getFitness() >= maxFitness.get(); } - - @Override - public String getFileExtension() { - return ".plu"; - } } diff --git a/src/jcgp/backend/modules/problem/Problem.java b/src/jcgp/backend/modules/problem/Problem.java index d01f5b0..07183ea 100644 --- a/src/jcgp/backend/modules/problem/Problem.java +++ b/src/jcgp/backend/modules/problem/Problem.java @@ -1,14 +1,19 @@ package jcgp.backend.modules.problem; +import java.io.File; + import jcgp.backend.function.FunctionSet; import jcgp.backend.modules.Module; import jcgp.backend.population.Chromosome; import jcgp.backend.population.Population; +import jcgp.backend.resources.ModifiableResources; import jcgp.backend.resources.Resources; public abstract class Problem implements Module { protected FunctionSet functionSet; + private String fileExtension = ".*"; + private String name = this.getClass().getSimpleName(); public abstract void evaluate(Population population, Resources resources); @@ -17,4 +22,27 @@ public abstract class Problem implements Module { } public abstract boolean isPerfectSolution(Chromosome fittest); + + public abstract void parse(File file, ModifiableResources resources); + + public void setFileExtension(String fileExtension) { + this.fileExtension = fileExtension; + } + + public String getFileExtension() { + return fileExtension; + } + + public void setProblemName(String newName) { + this.name = newName; + } + + public String getProblemName() { + return name; + } + + @Override + public String toString() { + return name; + } } diff --git a/src/jcgp/backend/modules/problem/SymbolicRegressionProblem.java b/src/jcgp/backend/modules/problem/SymbolicRegressionProblem.java index 52df0f2..5468157 100644 --- a/src/jcgp/backend/modules/problem/SymbolicRegressionProblem.java +++ b/src/jcgp/backend/modules/problem/SymbolicRegressionProblem.java @@ -1,6 +1,6 @@ package jcgp.backend.modules.problem; -import jcgp.backend.function.DoubleArithmetic; +import jcgp.backend.function.SymbolicRegressionFunctions; import jcgp.backend.population.Chromosome; import jcgp.backend.population.Population; import jcgp.backend.resources.Resources; @@ -16,7 +16,9 @@ public class SymbolicRegressionProblem extends TestCaseProblem<Double> { public SymbolicRegressionProblem(Resources resources) { super(resources); - functionSet = new DoubleArithmetic(); + functionSet = new SymbolicRegressionFunctions(); + setProblemName("Symbolic regression"); + setFileExtension(".dat"); errorThreshold = new DoubleParameter(0.01, "Error threshold") { @Override public void validate(Number newValue) { @@ -45,7 +47,7 @@ public class SymbolicRegressionProblem extends TestCaseProblem<Double> { } } }; - hitsBasedFitness = new BooleanParameter(true, "Hits-based fitness") { + hitsBasedFitness = new BooleanParameter(true, "HITS-based fitness") { @Override public void validate(Boolean newValue) { // blank @@ -55,9 +57,6 @@ public class SymbolicRegressionProblem extends TestCaseProblem<Double> { @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 @@ -81,18 +80,12 @@ public class SymbolicRegressionProblem extends TestCaseProblem<Double> { } // assign the resulting fitness to the respective individual population.getChromosome(i).setFitness(fitness); - if (fitness >= population.getFittest().getFitness()) { - population.setFittest(i); - } } + + // sort population + population.sortAscending(); } - - @Override - public String toString() { - return "Symbolic regression"; - } - @Override public void addTestCase(String[] inputs, String[] outputs) { Double[] inputCases = new Double[inputs.length]; @@ -116,9 +109,4 @@ public class SymbolicRegressionProblem extends TestCaseProblem<Double> { 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 d8dd32b..6c4a7dc 100644 --- a/src/jcgp/backend/modules/problem/TestCaseProblem.java +++ b/src/jcgp/backend/modules/problem/TestCaseProblem.java @@ -1,9 +1,12 @@ package jcgp.backend.modules.problem; +import java.io.File; import java.util.List; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import jcgp.backend.parsers.TestCaseParser; +import jcgp.backend.resources.ModifiableResources; import jcgp.backend.resources.Resources; import jcgp.backend.resources.parameters.DoubleParameter; import jcgp.backend.resources.parameters.Parameter; @@ -116,7 +119,10 @@ public abstract class TestCaseProblem<U extends Object> extends Problem { testCases.clear(); } - public abstract String getFileExtension(); + public void parse(File file, ModifiableResources resources) { + TestCaseParser.parseParameters(file, resources); + TestCaseParser.parse(file, this, resources); + } } diff --git a/src/jcgp/backend/modules/problem/TravellingSalesmanProblem.java b/src/jcgp/backend/modules/problem/TravellingSalesmanProblem.java new file mode 100644 index 0000000..6491ec6 --- /dev/null +++ b/src/jcgp/backend/modules/problem/TravellingSalesmanProblem.java @@ -0,0 +1,43 @@ +package jcgp.backend.modules.problem; + +import java.io.File; + +import jcgp.backend.function.TravellingSalesmanFunctions; +import jcgp.backend.population.Chromosome; +import jcgp.backend.population.Population; +import jcgp.backend.resources.ModifiableResources; +import jcgp.backend.resources.Resources; +import jcgp.backend.resources.parameters.Parameter; + +public class TravellingSalesmanProblem extends Problem { + + public TravellingSalesmanProblem(Resources resources) { + functionSet = new TravellingSalesmanFunctions(); + setProblemName("Travelling salesman"); + setFileExtension(".tsp"); + } + + @Override + public Parameter<?>[] getLocalParameters() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void evaluate(Population population, Resources resources) { + // TODO Auto-generated method stub + + } + + @Override + public boolean isPerfectSolution(Chromosome fittest) { + // TODO Auto-generated method stub + return false; + } + + @Override + public void parse(File file, ModifiableResources resources) { + // TODO Auto-generated method stub + + } +} diff --git a/src/jcgp/backend/parser/ChromosomeParser.java b/src/jcgp/backend/parser/ChromosomeParser.java deleted file mode 100644 index fd03b7d..0000000 --- a/src/jcgp/backend/parser/ChromosomeParser.java +++ /dev/null @@ -1,177 +0,0 @@ -package jcgp.backend.parser; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.PrintWriter; -import java.util.Scanner; - -import jcgp.backend.population.Chromosome; -import jcgp.backend.population.Connection; -import jcgp.backend.population.Input; -import jcgp.backend.population.Node; -import jcgp.backend.resources.Resources; - -/** - * This class includes a method for parsing .chr files and another - * for writing .chr files from given chromosomes. - * - * @author Eduardo Pedroni - * - */ -public abstract class ChromosomeParser { - - /** - * Use this method to parse .chr files into a given chromosome. - * <br><br> - * This is not fully defensive as it doesn't check for number of inputs, - * doesn't compare rows and columns individually and doesn't account for levels back. It - * is not viable to implement these defensive measures with the chromosome format used - * by CGP. - * - * @param file the .chr file to parse from - * @param chromosome the chromosome to configure - * @param resources the experiment resources - */ - public static void parse(File file, Chromosome chromosome, Resources resources) { - /* - * Count the nodes to make sure the size of the .chr file matches the experiment parameters. - * - * We do this by using the scanner to get the node and output portions of the file as they - * are separated by 3 tab characters. Every number is replaced by a single known character, - * and the length of the string with the new characters is compared with that of a string - * where the new known character has been removed, yielding the total number of values. - * - */ - FileReader fr; - try { - fr = new FileReader(file); - } catch (FileNotFoundException e) { - e.printStackTrace(); - return; - } - Scanner in = new Scanner(fr); - in.useDelimiter("\\t\\t\\t"); - String geneString = in.next().replaceAll("[0-9]+", "g"); - String outString = in.next().replaceAll("[0-9]+", "o"); - int geneCount = geneString.length() - geneString.replace("g", "").length(); - int outCount = outString.length() - outString.replace("o", "").length(); - in.close(); - - - // if the acquired values match the current parameters, apply them to the chromosome - if ((geneCount == resources.nodes() * (resources.arity() + 1)) - && outCount == resources.outputs()) { - // prepare a new scanner - try { - fr = new FileReader(file); - } catch (FileNotFoundException e) { - e.printStackTrace(); - return; - } - in = new Scanner(fr); - - int gene; - Connection newConnection; - Node changingNode; - // for all nodes, columns first - for (int c = 0; c < resources.columns(); c++) { - for (int r = 0; r < resources.rows(); r++) { - // store the changing node - changingNode = chromosome.getNode(r, c); - - // for every connection - for (int i = 0; i < resources.arity(); i++) { - // get connection number from the .chr file - gene = in.nextInt(); - System.out.println("r: " + r + " c: " + c + " i: " + i + " gene: " + gene); - if (gene < resources.inputs()) { - // connection was an input - newConnection = chromosome.getInput(gene); - } else { - // connection was another node, calculate which from its number - newConnection = chromosome.getNode((gene - resources.inputs()) % resources.rows(), - (gene - resources.inputs()) / resources.rows()); - } - changingNode.setConnection(i, newConnection); - } - - // set the function, straight indexing should work - this is not entirely - // safe, but it is not viable to check for functionset compatibility - changingNode.setFunction(resources.getFunction(in.nextInt())); - } - } - - // outputs - for (int o = 0; o < resources.outputs(); o ++) { - gene = in.nextInt(); - if (gene < resources.inputs()) { - // connection was an input - newConnection = chromosome.getInput(gene); - } else { - // connection was another node, calculate which from its number - newConnection = chromosome.getNode((gene - resources.inputs()) % resources.rows(), - (gene - resources.inputs()) / resources.rows()); - } - chromosome.getOutput(o).setConnection(0, newConnection); - } - in.close(); - } else { - throw new IllegalArgumentException("The topology of the chromosome in " + file.getName() + " does not match that of the experiment."); - } - } - - /** - * Writes a chromosome into the specified .chr file. - * - * - * @param file the file to write to - * @param chromosome the chromosome to save - */ - public static void save(File file, Chromosome chromosome, Resources resources) { - PrintWriter writer; - try { - writer = new PrintWriter(file); - } catch (FileNotFoundException e) { - // something bad happened - return; - } - - // for all nodes, columns first - for (int c = 0; c < resources.columns(); c++) { - for (int r = 0; r < resources.rows(); r++) { - for (int i = 0; i < resources.arity(); i++) { - // print the connections, separated by spaces - Connection conn = chromosome.getNode(r, c).getConnection(i); - if (conn instanceof Input) { - writer.print(" " + ((Input) conn).getIndex()); - } else if (conn instanceof Node) { - writer.print(" " + (((((Node) conn).getColumn() + 1) * resources.inputs()) + ((Node) conn).getRow())); - } else { - throw new ClassCastException("Could not handle " + conn.getClass() + " as a subclass of Connection"); - } - } - // print the function numbers - writer.print(" " + resources.getFunctionIndex(chromosome.getNode(r, c).getFunction())); - // node is done, print tab - writer.print("\t"); - } - } - // nodes are done, print two tabs to separate from output - writer.print("\t\t"); - for (int o = 0; o < resources.outputs(); o ++) { - Connection source = chromosome.getOutput(o).getSource(); - if (source instanceof Input) { - writer.print(" " + ((Input) source).getIndex()); - } else if (source instanceof Node) { - writer.print(" " + (((((Node) source).getColumn() + 1) * resources.inputs()) + ((Node) source).getRow())); - } else { - throw new ClassCastException("Could not handle " + source.getClass() + " as a subclass of Connection"); - } - } - - writer.close(); - System.out.println("writer is closed"); - } - -} diff --git a/src/jcgp/backend/parser/FunctionParser.java b/src/jcgp/backend/parser/FunctionParser.java deleted file mode 100644 index ab94899..0000000 --- a/src/jcgp/backend/parser/FunctionParser.java +++ /dev/null @@ -1,51 +0,0 @@ -package jcgp.backend.parser; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.util.Scanner; - -import jcgp.backend.function.FunctionSet; -import jcgp.backend.modules.problem.Problem; - -public class FunctionParser { - -private FunctionSet functionSet; - - private FunctionParser(Problem problem) { - this.functionSet = problem.getFunctionSet(); - } - - public static void parseFunctions(File file, Problem problem) { - FunctionParser pp = new FunctionParser(problem); - - FileReader fr; - try { - fr = new FileReader(file); - } catch (FileNotFoundException e) { - e.printStackTrace(); - return; - } - - Scanner in = new Scanner(fr); - - while (in.hasNextLine()) { - String line = in.nextLine(); - if (line.substring(line.length() - 1).matches("[0-9]")) { - pp.parseAndSet(line.split("[^0-9]+")); - } - } - - in.close(); - } - - private void parseAndSet(String[] splitString) { - int functionIndex = Integer.parseInt(splitString[splitString.length - 1]); - if (Integer.parseInt(splitString[0]) != 0) { - functionSet.enableFunction(functionIndex); - } else { - functionSet.disableFunction(functionIndex); - } - - } -} diff --git a/src/jcgp/backend/parser/ParameterParser.java b/src/jcgp/backend/parser/ParameterParser.java deleted file mode 100644 index f96ebf9..0000000 --- a/src/jcgp/backend/parser/ParameterParser.java +++ /dev/null @@ -1,77 +0,0 @@ -package jcgp.backend.parser; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.util.Scanner; - -import jcgp.backend.resources.ModifiableResources; - -public class ParameterParser { - - private ModifiableResources resources; - - private ParameterParser(ModifiableResources resources) { - this.resources = resources; - } - - public static void parseParameters(File file, ModifiableResources resources) { - ParameterParser pp = new ParameterParser(resources); - - FileReader fr; - try { - fr = new FileReader(file); - } catch (FileNotFoundException e) { - e.printStackTrace(); - return; - } - - Scanner in = new Scanner(fr); - - while (in.hasNextLine()) { - pp.parseAndSet(in.nextLine().split("( |\t)+")); - } - - in.close(); - } - - private void parseAndSet(String[] splitString) { - switch (splitString[1]) { - - case "population_size": - resources.setPopulationSize(Integer.parseInt(splitString[0])); - break; - - case "num_generations": - resources.setGenerations(Integer.parseInt(splitString[0])); - break; - - case "num_runs_total": - resources.setRuns(Integer.parseInt(splitString[0])); - break; - - case "num_rows": - resources.setRows(Integer.parseInt(splitString[0])); - break; - - case "num_cols": - resources.setColumns(Integer.parseInt(splitString[0])); - break; - - case "levels_back": - resources.setLevelsBack(Integer.parseInt(splitString[0])); - break; - - case "report_interval": - resources.setReportInterval(Integer.parseInt(splitString[0])); - break; - - case "global_seed": - resources.setSeed(Integer.parseInt(splitString[0])); - break; - - default: - break; - } - } -} diff --git a/src/jcgp/backend/parser/TestCaseParser.java b/src/jcgp/backend/parser/TestCaseParser.java deleted file mode 100644 index d47d663..0000000 --- a/src/jcgp/backend/parser/TestCaseParser.java +++ /dev/null @@ -1,90 +0,0 @@ -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 { - - 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/parsers/ChromosomeParser.java b/src/jcgp/backend/parsers/ChromosomeParser.java index fe58ff8..ed1399a 100644 --- a/src/jcgp/backend/parsers/ChromosomeParser.java +++ b/src/jcgp/backend/parsers/ChromosomeParser.java @@ -11,6 +11,7 @@ import jcgp.backend.population.Connection; import jcgp.backend.population.Input; import jcgp.backend.population.Node; import jcgp.backend.resources.ModifiableResources; +import jcgp.backend.resources.Resources; /** * This class includes a method for parsing .chr files and another @@ -33,7 +34,7 @@ public abstract class ChromosomeParser { * @param chromosome the chromosome to configure * @param resources the experiment resources */ - public static void parse(File file, Chromosome chromosome, ModifiableResources resources) { + public static void parse(File file, Chromosome chromosome, Resources resources) { /* * Count the nodes to make sure the size of the .chr file matches the experiment parameters. * diff --git a/src/jcgp/backend/parsers/FunctionParser.java b/src/jcgp/backend/parsers/FunctionParser.java index 6d6c73b..cc52ce9 100644 --- a/src/jcgp/backend/parsers/FunctionParser.java +++ b/src/jcgp/backend/parsers/FunctionParser.java @@ -11,7 +11,7 @@ import jcgp.backend.resources.ModifiableResources; /** * Parses the functions from a .par file. - * Functions marked with a 1 will be enabled, + * Functions marked with a 1 are enabled, * and those marked with 0 are disabled. * * @author Eduardo Pedroni @@ -25,7 +25,7 @@ public abstract class FunctionParser { try { fr = new FileReader(file); } catch (FileNotFoundException e) { - resources.println("[Parser] Error: could not find " + file.getAbsolutePath() + "."); + resources.println("[Parser] Error: could not find " + file.getAbsolutePath()); return; } @@ -40,14 +40,14 @@ public abstract class FunctionParser { int functionIndex = Integer.parseInt(splitString[splitString.length - 1]); if (Integer.parseInt(splitString[0]) != 0 && !functionSet.isEnabled(functionSet.getFunction(functionIndex))) { functionSet.enableFunction(functionIndex); - resources.println("[Parser] Enabled function: " + functionSet.getFunction(functionIndex).getName() + "."); + resources.println("[Parser] Enabled function: " + functionSet.getFunction(functionIndex)); } else if (Integer.parseInt(splitString[0]) == 0 && functionSet.isEnabled(functionSet.getFunction(functionIndex))) { functionSet.disableFunction(functionIndex); - resources.println("[Parser] Disabled function: " + functionSet.getFunction(functionIndex).getName() + "."); + resources.println("[Parser] Disabled function: " + functionSet.getFunction(functionIndex)); } } } in.close(); - resources.println("[Parser] Finished parsing functions."); + resources.println("[Parser] Finished parsing functions"); } } diff --git a/src/jcgp/backend/population/Chromosome.java b/src/jcgp/backend/population/Chromosome.java index f8830fa..9e53f85 100644 --- a/src/jcgp/backend/population/Chromosome.java +++ b/src/jcgp/backend/population/Chromosome.java @@ -5,7 +5,7 @@ import java.util.ArrayList; import jcgp.backend.exceptions.ParameterMismatchException; import jcgp.backend.resources.Resources; -public class Chromosome { +public class Chromosome implements Comparable<Chromosome> { private Resources resources; @@ -276,7 +276,7 @@ public class Chromosome { } } - public boolean compareTo(Chromosome chromosome) { + public boolean compareGenesTo(Chromosome chromosome) { for (int r = 0; r < resources.rows(); r++) { for (int c = 0; c < resources.columns(); c++) { if (!(nodes[r][c].copyOf(chromosome.getNode(r, c)))) { @@ -294,7 +294,7 @@ public class Chromosome { return true; } - public boolean compareActiveTo(Chromosome chromosome) { + public boolean compareActiveGenesTo(Chromosome chromosome) { // update list if it is out of date computeActiveNodes(); @@ -310,7 +310,6 @@ public class Chromosome { } public void printNodes() { - // TODO make this proper int arity = resources.arity(); for (int r = 0; r < resources.rows(); r++) { @@ -320,7 +319,7 @@ public class Chromosome { for (int i = 0; i < arity; i++) { System.out.print("C" + i + ": (" + nodes[r][c].getConnection(i).toString() + ") "); } - System.out.print("F: " + nodes[r][c].getFunction().getName() + "\t"); + System.out.print("F: " + nodes[r][c].getFunction() + "\t"); } System.out.print("\n"); } @@ -335,4 +334,15 @@ public class Chromosome { public Resources getResources() { return resources; } + + @Override + public int compareTo(Chromosome o) { + if (fitness < o.getFitness()) { + return -1; + } else if (fitness > o.getFitness()) { + return 1; + } else { + return 0; + } + } } diff --git a/src/jcgp/backend/population/Population.java b/src/jcgp/backend/population/Population.java index b6dd055..d99c64e 100644 --- a/src/jcgp/backend/population/Population.java +++ b/src/jcgp/backend/population/Population.java @@ -1,12 +1,14 @@ package jcgp.backend.population; +import java.util.Arrays; +import java.util.Collections; + import jcgp.backend.resources.Resources; public class Population { private final Chromosome[] chromosomes; private final Resources resources; - private int fittest = 0; /** * Initialise a random population according to the parameters specified @@ -77,25 +79,26 @@ public class Population { chromosomes[c].reinitialiseConnections(); } } - - public void setFittest(int fittest) { - this.fittest = fittest; - } - - public void setFittest(Chromosome fittest) { - for (int i = 0; i < chromosomes.length; i++) { - if (chromosomes[i] == fittest) { - this.fittest = i; - return; - } - } - } public Chromosome getFittest() { - return chromosomes[fittest]; + return chromosomes[chromosomes.length - 1]; } public int getFittestIndex() { - return fittest; + return chromosomes.length - 1; + } + + /** + * Sort the population into ascending order of fitness. + */ + public void sortAscending() { + Arrays.sort(chromosomes); + } + + /** + * Sort the population into descending order of fitness. + */ + public void sortDescending() { + Arrays.sort(chromosomes, Collections.reverseOrder()); } } diff --git a/src/jcgp/backend/resources/ModifiableResources.java b/src/jcgp/backend/resources/ModifiableResources.java index 048e460..53dc815 100644 --- a/src/jcgp/backend/resources/ModifiableResources.java +++ b/src/jcgp/backend/resources/ModifiableResources.java @@ -1,7 +1,10 @@ package jcgp.backend.resources; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; import jcgp.backend.function.FunctionSet; import jcgp.backend.resources.parameters.IntegerParameter; +import jcgp.backend.resources.parameters.ParameterStatus; /** * @@ -10,13 +13,13 @@ import jcgp.backend.resources.parameters.IntegerParameter; * and only classes with access to a JCGP instance may modify * the resources. * - * @author eddy + * @author Eduardo Pedroni * */ public class ModifiableResources extends Resources { public ModifiableResources() { - super(); + createBaseParameters(); } public void setValues(String filePath) { @@ -214,21 +217,159 @@ public class ModifiableResources extends Resources { this.console = console; } - /* - * Console functionality - * These are not affected by parameter report interval - */ - public void println(String s) { - System.out.println(s); - if (console != null) { - console.println(s); - } - } - - public void print(String s) { - System.out.print(s); - if (console != null) { - console.print(s); - } + private void createBaseParameters() { + rows = new IntegerParameter(5, "Rows", false, true) { + @Override + public void validate(Number newValue) { + if (newValue.intValue() <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Chromosome must have at least 1 row."); + } else { + status = ParameterStatus.VALID; + } + } + }; + + columns = new IntegerParameter(5, "Columns", false, true) { + @Override + public void validate(Number newValue) { + if (newValue.intValue() <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Chromosome must have at least 1 column."); + } else { + status = ParameterStatus.VALID; + } + } + }; + + inputs = new IntegerParameter(3, "Inputs", true, false) { + @Override + public void validate(Number newValue) { + if (newValue.intValue() <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Chromosome must have at least 1 input."); + } else { + status = ParameterStatus.VALID; + } + } + }; + + outputs = new IntegerParameter(3, "Outputs", true, false) { + @Override + public void validate(Number newValue) { + if (newValue.intValue() <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Chromosome must have at least 1 output."); + } else { + status = ParameterStatus.VALID; + } + } + }; + + populationSize = new IntegerParameter(5, "Population", false, true) { + @Override + public void validate(Number newValue) { + if (newValue.intValue() <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Population size must be at least 1."); + } else { + status = ParameterStatus.VALID; + } + } + }; + + levelsBack = new IntegerParameter(2, "Levels back", false, true) { + @Override + public void validate(Number newValue) { + if (newValue.intValue() <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Levels back must be at least 1."); + } else if (newValue.intValue() > columns.get()) { + status = ParameterStatus.INVALID; + status.setDetails("Levels back must be less than or equal to the number of columns."); + } else { + status = ParameterStatus.VALID; + } + } + }; + + generations = new IntegerParameter(1000000, "Generations") { + @Override + public void validate(Number newValue) { + if (newValue.intValue() <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Number of generations must be greater than 0."); + } else if (newValue.intValue() < currentGeneration.get()) { + status = ParameterStatus.WARNING_RESET; + status.setDetails("Setting generations to less than the current generation will cause the experiment to restart."); + } else { + status = ParameterStatus.VALID; + } + } + }; + + currentGeneration = new IntegerParameter(1, "Generation", true, false) { + @Override + public void validate(Number newValue) { + // blank + } + }; + + runs = new IntegerParameter(5, "Runs") { + @Override + public void validate(Number newValue) { + if (newValue.intValue() <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Number of runs must be greater than 0."); + } else if (newValue.intValue() < currentRun.get()) { + status = ParameterStatus.WARNING_RESET; + status.setDetails("Setting runs to less than the current run will cause the experiment to restart."); + } else { + status = ParameterStatus.VALID; + } + } + }; + + currentRun = new IntegerParameter(1, "Run", true, false) { + @Override + public void validate(Number newValue) { + // blank + } + }; + + arity = new IntegerParameter(0, "Max arity", true, false) { + @Override + public void validate(Number newValue) { + // blank + } + }; + + seed = new IntegerParameter(1234, "Seed", false, true) { + @Override + public void validate(Number newValue) { + status = ParameterStatus.VALID; + } + }; + seed.valueProperty().addListener(new ChangeListener<Number>() { + @Override + public void changed( + ObservableValue<? extends Number> observable, + Number oldValue, Number newValue) { + numberGenerator.setSeed(newValue.longValue()); + } + }); + numberGenerator.setSeed(seed.get()); + + reportInterval = new IntegerParameter(1, "Report interval", false, false) { + @Override + public void validate(Number newValue) { + if (newValue.intValue() > generations.get()) { + status = ParameterStatus.WARNING; + status.setDetails("No reports will be printed."); + } else { + status = ParameterStatus.VALID; + } + } + }; } } diff --git a/src/jcgp/backend/resources/Resources.java b/src/jcgp/backend/resources/Resources.java index e83680f..e1438f1 100644 --- a/src/jcgp/backend/resources/Resources.java +++ b/src/jcgp/backend/resources/Resources.java @@ -2,12 +2,9 @@ package jcgp.backend.resources; import java.util.Random; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; import jcgp.backend.function.Function; import jcgp.backend.function.FunctionSet; import jcgp.backend.resources.parameters.IntegerParameter; -import jcgp.backend.resources.parameters.ParameterStatus; /** * @@ -28,10 +25,6 @@ public class Resources { // GUI console protected Console console; - public Resources() { - createBaseParameters(); - } - /** * @return the rows */ @@ -129,162 +122,6 @@ public class Resources { public int reportInterval() { return reportInterval.get(); } - - private void createBaseParameters() { - rows = new IntegerParameter(5, "Rows", false, true) { - @Override - public void validate(Number newValue) { - if (newValue.intValue() <= 0) { - status = ParameterStatus.INVALID; - status.setDetails("Chromosome must have at least 1 row."); - } else { - status = ParameterStatus.VALID; - } - } - }; - - columns = new IntegerParameter(5, "Columns", false, true) { - @Override - public void validate(Number newValue) { - if (newValue.intValue() <= 0) { - status = ParameterStatus.INVALID; - status.setDetails("Chromosome must have at least 1 column."); - } else { - status = ParameterStatus.VALID; - } - } - }; - - inputs = new IntegerParameter(3, "Inputs", true, false) { - @Override - public void validate(Number newValue) { - if (newValue.intValue() <= 0) { - status = ParameterStatus.INVALID; - status.setDetails("Chromosome must have at least 1 input."); - } else { - status = ParameterStatus.VALID; - } - } - }; - - outputs = new IntegerParameter(3, "Outputs", true, false) { - @Override - public void validate(Number newValue) { - if (newValue.intValue() <= 0) { - status = ParameterStatus.INVALID; - status.setDetails("Chromosome must have at least 1 output."); - } else { - status = ParameterStatus.VALID; - } - } - }; - - populationSize = new IntegerParameter(5, "Population", false, true) { - @Override - public void validate(Number newValue) { - if (newValue.intValue() <= 0) { - status = ParameterStatus.INVALID; - status.setDetails("Population size must be at least 1."); - } else { - status = ParameterStatus.VALID; - } - } - }; - - levelsBack = new IntegerParameter(2, "Levels back", false, true) { - @Override - public void validate(Number newValue) { - if (newValue.intValue() <= 0) { - status = ParameterStatus.INVALID; - status.setDetails("Levels back must be at least 1."); - } else if (newValue.intValue() > columns.get()) { - status = ParameterStatus.INVALID; - status.setDetails("Levels back must be less than or equal to the number of columns."); - } else { - status = ParameterStatus.VALID; - } - } - }; - - generations = new IntegerParameter(1000000, "Generations") { - @Override - public void validate(Number newValue) { - if (newValue.intValue() <= 0) { - status = ParameterStatus.INVALID; - status.setDetails("Number of generations must be greater than 0."); - } else if (newValue.intValue() < currentGeneration.get()) { - status = ParameterStatus.WARNING_RESET; - status.setDetails("Setting generations to less than the current generation will cause the experiment to restart."); - } else { - status = ParameterStatus.VALID; - } - } - }; - - currentGeneration = new IntegerParameter(1, "Generation", true, false) { - @Override - public void validate(Number newValue) { - // blank - } - }; - - runs = new IntegerParameter(5, "Runs") { - @Override - public void validate(Number newValue) { - if (newValue.intValue() <= 0) { - status = ParameterStatus.INVALID; - status.setDetails("Number of runs must be greater than 0."); - } else if (newValue.intValue() < currentRun.get()) { - status = ParameterStatus.WARNING_RESET; - status.setDetails("Setting runs to less than the current run will cause the experiment to restart."); - } else { - status = ParameterStatus.VALID; - } - } - }; - - currentRun = new IntegerParameter(1, "Run", true, false) { - @Override - public void validate(Number newValue) { - // blank - } - }; - - arity = new IntegerParameter(0, "Max arity", true, false) { - @Override - public void validate(Number newValue) { - // blank - } - }; - - seed = new IntegerParameter(1234, "Seed", false, true) { - @Override - public void validate(Number newValue) { - status = ParameterStatus.VALID; - } - }; - seed.valueProperty().addListener(new ChangeListener<Number>() { - @Override - public void changed( - ObservableValue<? extends Number> observable, - Number oldValue, Number newValue) { - numberGenerator.setSeed(newValue.longValue()); - } - }); - numberGenerator.setSeed(seed.get()); - - reportInterval = new IntegerParameter(1, "Report interval", false, false) { - @Override - public void validate(Number newValue) { - if (newValue.intValue() > generations.get()) { - status = ParameterStatus.WARNING; - status.setDetails("No reports will be printed."); - } else { - status = ParameterStatus.VALID; - } - } - }; - } /* * Utility functions @@ -355,4 +192,22 @@ public class Resources { } } } + + /* + * Console functionality + * These are not affected by parameter report interval + */ + public void println(String s) { + System.out.println(s); + if (console != null) { + console.println(s); + } + } + + public void print(String s) { + System.out.print(s); + if (console != null) { + console.print(s); + } + } }
\ No newline at end of file diff --git a/src/jcgp/backend/tests/ChromosomeTests.java b/src/jcgp/backend/tests/ChromosomeTests.java index 36278ba..bc0c57d 100644 --- a/src/jcgp/backend/tests/ChromosomeTests.java +++ b/src/jcgp/backend/tests/ChromosomeTests.java @@ -2,7 +2,7 @@ package jcgp.backend.tests; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import jcgp.backend.function.DoubleArithmetic; +import jcgp.backend.function.SymbolicRegressionFunctions; import jcgp.backend.population.Chromosome; import jcgp.backend.population.Connection; import jcgp.backend.population.Input; @@ -46,7 +46,7 @@ public class ChromosomeTests { @BeforeClass public static void setUpBeforeClass() { resources = new ModifiableResources(); - resources.setFunctionSet(new DoubleArithmetic()); + resources.setFunctionSet(new SymbolicRegressionFunctions()); } @Before @@ -285,12 +285,12 @@ public class ChromosomeTests { public void compareActiveTest() { // create a clone of the chromosome, compare active nodes - should return true Chromosome c = new Chromosome(chromosome); - assertTrue("Active nodes did not match.", chromosome.compareActiveTo(c)); - assertTrue("Symmetry not obeyed.", c.compareActiveTo(chromosome)); + assertTrue("Active nodes did not match.", chromosome.compareActiveGenesTo(c)); + assertTrue("Symmetry not obeyed.", c.compareActiveGenesTo(chromosome)); // create a new random chromosome, this time they should not match c = new Chromosome(resources); - assertTrue("Active nodes did match.", !chromosome.compareActiveTo(c)); + assertTrue("Active nodes did match.", !chromosome.compareActiveGenesTo(c)); } /** @@ -300,12 +300,12 @@ public class ChromosomeTests { public void compareTest() { // create a clone of the chromosome, compare - should return true Chromosome c = new Chromosome(chromosome); - assertTrue("Chromosomes did not match.", chromosome.compareTo(c)); - assertTrue("Symmetry not obeyed.", c.compareTo(chromosome)); + assertTrue("Chromosomes did not match.", chromosome.compareGenesTo(c)); + assertTrue("Symmetry not obeyed.", c.compareGenesTo(chromosome)); // create a new random chromosome, this time they should not match c = new Chromosome(resources); - assertTrue("Chromosomes did match.", !chromosome.compareTo(c)); + assertTrue("Chromosomes did match.", !chromosome.compareGenesTo(c)); } /** * Utility for creating a chromosome of known configuration. diff --git a/src/jcgp/backend/tests/NodeTests.java b/src/jcgp/backend/tests/NodeTests.java index 4054661..c63b41e 100644 --- a/src/jcgp/backend/tests/NodeTests.java +++ b/src/jcgp/backend/tests/NodeTests.java @@ -2,7 +2,7 @@ package jcgp.backend.tests; import static org.junit.Assert.assertTrue; import jcgp.backend.exceptions.InvalidArgumentsException; -import jcgp.backend.function.DoubleArithmetic; +import jcgp.backend.function.SymbolicRegressionFunctions; import jcgp.backend.function.Function; import jcgp.backend.population.Chromosome; import jcgp.backend.population.Connection; @@ -41,7 +41,7 @@ public class NodeTests { public static void setUpBeforeClass() { resources = new ModifiableResources(); - resources.setFunctionSet(new DoubleArithmetic()); + resources.setFunctionSet(new SymbolicRegressionFunctions()); chromosome = new Chromosome(resources); } @@ -49,7 +49,7 @@ public class NodeTests { public void setUp() throws Exception { node = new Node(chromosome, 0, 0, resources.arity()); // make node with anonymous addition function and hard-coded value connections - node.initialise(new DoubleArithmetic.Addition(), + node.initialise(new SymbolicRegressionFunctions.Addition(), new Connection[]{new Connection() { @Override @@ -80,7 +80,7 @@ public class NodeTests { Function f = new Function() { @Override - public Object run(Connection... connections) { + public Object run(Object... connections) { // blank return 0; } @@ -90,12 +90,6 @@ public class NodeTests { // blank return 0; } - - @Override - public String getName() { - // blank - return null; - } }; node.setFunction(f); @@ -113,7 +107,7 @@ public class NodeTests { ((int) node.getValue()) == arg1 + arg2); // put in a different function, check the output has changed appropriately - node.setFunction(new DoubleArithmetic.Subtraction()); + node.setFunction(new SymbolicRegressionFunctions.Subtraction()); assertTrue("Node did not return expected value (difference of arguments).", ((Integer) node.getValue()) == arg1 - arg2); @@ -142,7 +136,7 @@ public class NodeTests { Function function = new Function() { @Override - public Object run(Connection... connections) + public Object run(Object... connections) throws InvalidArgumentsException { // blank return null; @@ -152,12 +146,6 @@ public class NodeTests { public int getArity() { return 2; } - - @Override - public String getName() { - // blank - return null; - } }; node.initialise(function, conn0, conn1); diff --git a/src/jcgp/backend/tests/OutputTests.java b/src/jcgp/backend/tests/OutputTests.java index 95b7fc7..8cc10a8 100644 --- a/src/jcgp/backend/tests/OutputTests.java +++ b/src/jcgp/backend/tests/OutputTests.java @@ -1,7 +1,7 @@ package jcgp.backend.tests; import static org.junit.Assert.assertTrue; -import jcgp.backend.function.DoubleArithmetic; +import jcgp.backend.function.SymbolicRegressionFunctions; import jcgp.backend.population.Chromosome; import jcgp.backend.population.Connection; import jcgp.backend.population.Output; @@ -35,7 +35,7 @@ public class OutputTests { @BeforeClass public static void setUpBeforeClass() { resources = new ModifiableResources(); - resources.setFunctionSet(new DoubleArithmetic()); + resources.setFunctionSet(new SymbolicRegressionFunctions()); chromosome = new Chromosome(resources); } diff --git a/src/jcgp/backend/tests/PopulationTests.java b/src/jcgp/backend/tests/PopulationTests.java index fca9c4f..2f36ce1 100644 --- a/src/jcgp/backend/tests/PopulationTests.java +++ b/src/jcgp/backend/tests/PopulationTests.java @@ -1,7 +1,7 @@ package jcgp.backend.tests; import static org.junit.Assert.assertTrue; -import jcgp.backend.function.DoubleArithmetic; +import jcgp.backend.function.SymbolicRegressionFunctions; import jcgp.backend.population.Chromosome; import jcgp.backend.population.Population; import jcgp.backend.resources.ModifiableResources; @@ -32,7 +32,7 @@ public class PopulationTests { @BeforeClass public static void setUpBeforeClass() throws Exception { resources = new ModifiableResources(); - resources.setFunctionSet(new DoubleArithmetic()); + resources.setFunctionSet(new SymbolicRegressionFunctions()); } @Before @@ -64,6 +64,6 @@ public class PopulationTests { // initialise a population with a copy of it population = new Population(oc, resources); // check that the first parent chromosome is identical to, but not the same instance as, the one given - assertTrue("Incorrect chromosome in population.", population.getChromosome(0).compareTo(oc) && population.getChromosome(0) != oc); + assertTrue("Incorrect chromosome in population.", population.getChromosome(0).compareGenesTo(oc) && population.getChromosome(0) != oc); } } diff --git a/src/jcgp/gui/console/GUIConsole.java b/src/jcgp/gui/console/GUIConsole.java index e77e222..de4b378 100644 --- a/src/jcgp/gui/console/GUIConsole.java +++ b/src/jcgp/gui/console/GUIConsole.java @@ -38,7 +38,6 @@ public class GUIConsole extends AnchorPane implements Console { @Override public Event dispatchEvent(Event event, EventDispatchChain tail) { if (event instanceof MouseEvent) { - //shot in the dark guess for OSX, might not work MouseEvent mouseEvent = (MouseEvent)event; if (mouseEvent.getButton() == MouseButton.SECONDARY || (mouseEvent.getButton() == MouseButton.PRIMARY && mouseEvent.isControlDown())) { diff --git a/src/jcgp/gui/population/FunctionSelector.java b/src/jcgp/gui/population/FunctionSelector.java index 28eb54d..69d0d31 100644 --- a/src/jcgp/gui/population/FunctionSelector.java +++ b/src/jcgp/gui/population/FunctionSelector.java @@ -31,7 +31,7 @@ public class FunctionSelector extends VBox { for (int i = 0; i < fs.getAllowedFunctionCount(); i++) { final int index = i; - Label l = new Label(fs.getAllowedFunction(i).getName()); + Label l = new Label(fs.getAllowedFunction(i).toString()); l.setMaxWidth(Double.MAX_VALUE); l.setStyle("-fx-background-color: #FFFFFF; -fx-border-color: #A0A0A0; -fx-border-width: 0 0 1 0; -fx-padding: 2"); diff --git a/src/jcgp/gui/population/GUINode.java b/src/jcgp/gui/population/GUINode.java index f50edd3..731db8f 100644 --- a/src/jcgp/gui/population/GUINode.java +++ b/src/jcgp/gui/population/GUINode.java @@ -454,9 +454,9 @@ public class GUINode extends GUIGene { public void updateText() { if (parent.isEvaluating()) { - text.setText(node.getFunction().getName() + "\n" + value.toString()); + text.setText(node.getFunction() + "\n" + value.toString()); } else { - text.setText(node.getFunction().getName()); + text.setText(node.getFunction().toString()); } } diff --git a/src/jcgp/gui/settings/SettingsPane.java b/src/jcgp/gui/settings/SettingsPane.java index 302e96d..7fc5621 100644 --- a/src/jcgp/gui/settings/SettingsPane.java +++ b/src/jcgp/gui/settings/SettingsPane.java @@ -126,17 +126,13 @@ public class SettingsPane extends AnchorPane { final VBox eaParameters = new VBox(2); - if (jcgp.getEvolutionaryStrategy().getLocalParameters() != null) { - refreshParameters(jcgp.getEvolutionaryStrategy().getLocalParameters(), eaParameters); - } + refreshParameters(jcgp.getEvolutionaryStrategy().getLocalParameters(), eaParameters); eaCBox.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { jcgp.setEvolutionaryStrategy(eaCBox.getSelectionModel().getSelectedIndex()); - if (eaCBox.getSelectionModel().getSelectedItem().getLocalParameters() != null) { - refreshParameters(eaCBox.getSelectionModel().getSelectedItem().getLocalParameters(), eaParameters); - } + refreshParameters(eaCBox.getSelectionModel().getSelectedItem().getLocalParameters(), eaParameters); } }); @@ -157,17 +153,13 @@ public class SettingsPane extends AnchorPane { mutatorCBox.prefWidthProperty().bind(mainContainer.widthProperty()); final VBox mutatorParameters = new VBox(2); - if (jcgp.getEvolutionaryStrategy().getLocalParameters() != null) { - refreshParameters(jcgp.getMutator().getLocalParameters(), mutatorParameters); - } + refreshParameters(jcgp.getMutator().getLocalParameters(), mutatorParameters); mutatorCBox.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { jcgp.setMutator(mutatorCBox.getSelectionModel().getSelectedIndex()); - if (mutatorCBox.getSelectionModel().getSelectedItem().getLocalParameters() != null) { - refreshParameters(mutatorCBox.getSelectionModel().getSelectedItem().getLocalParameters(), mutatorParameters); - } + refreshParameters(mutatorCBox.getSelectionModel().getSelectedItem().getLocalParameters(), mutatorParameters); } }); @@ -189,22 +181,22 @@ public class SettingsPane extends AnchorPane { final VBox problemParameters = new VBox(2); problemParameters.setPadding(new Insets(0, 0, 4, 0)); - if (jcgp.getProblem().getLocalParameters() != null) { - refreshParameters(jcgp.getProblem().getLocalParameters(), problemParameters); - } + refreshParameters(jcgp.getProblem().getLocalParameters(), problemParameters); final HBox testCaseControlContainer = new HBox(2); final Button showTestCaseButton = makeTestCaseButton(); - final Button loadTestCaseButton = makeLoadTestCaseButton(); + final Button loadProblemDataButton = makeLoadTestCaseButton(); HBox.setHgrow(showTestCaseButton, Priority.ALWAYS); showTestCaseButton.setMaxWidth(Double.MAX_VALUE); - HBox.setHgrow(loadTestCaseButton, Priority.ALWAYS); - loadTestCaseButton.setMaxWidth(Double.MAX_VALUE); + HBox.setHgrow(loadProblemDataButton, Priority.ALWAYS); + loadProblemDataButton.setMaxWidth(Double.MAX_VALUE); if (jcgp.getProblem() instanceof TestCaseProblem<?>) { - testCaseControlContainer.getChildren().addAll(showTestCaseButton, loadTestCaseButton); + testCaseControlContainer.getChildren().addAll(showTestCaseButton, loadProblemDataButton); remakeTestCaseTable(); + } else { + testCaseControlContainer.getChildren().add(loadProblemDataButton); } nodeFunctions = new VBox(2); @@ -215,16 +207,16 @@ public class SettingsPane extends AnchorPane { @Override public void handle(ActionEvent event) { jcgp.setProblem(problemCBox.getSelectionModel().getSelectedIndex()); - if (jcgp.getProblem().getLocalParameters() != null) { - refreshParameters(jcgp.getProblem().getLocalParameters(), problemParameters); - } + refreshParameters(jcgp.getProblem().getLocalParameters(), problemParameters); testCaseTable.close(); gui.setEvaluating(false); refreshFunctions(); testCaseControlContainer.getChildren().clear(); if (jcgp.getProblem() instanceof TestCaseProblem) { - testCaseControlContainer.getChildren().addAll(showTestCaseButton, loadTestCaseButton); + testCaseControlContainer.getChildren().addAll(showTestCaseButton, loadProblemDataButton); remakeTestCaseTable(); + } else { + testCaseControlContainer.getChildren().add(loadProblemDataButton); } gui.reset(); } @@ -236,17 +228,17 @@ public class SettingsPane extends AnchorPane { } private Button makeLoadTestCaseButton() { - Button b = new Button("Load test cases"); + Button b = new Button("Load problem data"); b.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { FileChooser fc = new FileChooser(); - fc.setTitle("Open test case file..."); + fc.setTitle("Open problem 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); + gui.getExperiment().loadProblemData(chrFile); remakeTestCaseTable(); gui.reDraw(); } @@ -369,10 +361,12 @@ public class SettingsPane extends AnchorPane { private void refreshParameters(Parameter<?>[] newParameters, VBox vb) { parameters.removeAll(vb.getChildren()); vb.getChildren().clear(); - for (int i = 0; i < newParameters.length; i++) { - GUIParameter<?> gp = GUIParameter.create(newParameters[i], this); - parameters.add(gp); - vb.getChildren().add(gp); + if (newParameters != null) { + for (int i = 0; i < newParameters.length; i++) { + GUIParameter<?> gp = GUIParameter.create(newParameters[i], this); + parameters.add(gp); + vb.getChildren().add(gp); + } } revalidateParameters(); } @@ -386,7 +380,7 @@ public class SettingsPane extends AnchorPane { CheckBox cb; final FunctionSet fs = gui.getExperiment().getResources().getFunctionSet(); for (int i = 0; i < fs.getTotalFunctionCount(); i++) { - cb = new CheckBox(fs.getFunction(i).getName()); + cb = new CheckBox(fs.getFunction(i).toString()); cb.setId(String.valueOf(i)); cb.setSelected(fs.isEnabled(fs.getFunction(i))); final int index = i; diff --git a/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java b/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java index e708c53..cc7113d 100644 --- a/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java @@ -62,7 +62,8 @@ public class GUIBooleanParameter extends GUIParameter<Boolean> { checkBox.setStyle(BASE_CHECKBOX_STYLE + INVALID_PARAMETER_STYLE); checkBox.setTooltip(tooltip); tooltip.setText(parameter.getStatus().getDetails()); - } else if (parameter.getStatus() == ParameterStatus.WARNING || parameter.getStatus() == ParameterStatus.WARNING_RESET) { + } else if (parameter.getStatus() == ParameterStatus.WARNING + || parameter.getStatus() == ParameterStatus.WARNING_RESET) { checkBox.setStyle(BASE_CHECKBOX_STYLE + WARNING_PARAMETER_STYLE); checkBox.setTooltip(tooltip); tooltip.setText(parameter.getStatus().getDetails()); diff --git a/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java b/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java index f177ffa..777e739 100644 --- a/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java @@ -36,6 +36,7 @@ public class GUIDoubleParameter extends GUIParameter<Number> { @Override protected Control makeControl() { + // we use a text field, and a formatting class to enforce decimals decimalFormat = new DecimalFormat(); decimalFormat.setMaximumFractionDigits(10); textField = new TextField(decimalFormat.format(parameter.get().doubleValue())); @@ -48,14 +49,16 @@ public class GUIDoubleParameter extends GUIParameter<Number> { @Override protected void setControlListeners() { /* pass the TextField value back to the parameter whenever it gets - * modified, provided it is not empty and the experiment isn't running */ + * modified, provided it is not empty, the experiment isn't running + * and it matches the double-precision regex filter */ textField.textProperty().addListener(new ChangeListener<String>() { @Override public void changed( ObservableValue<? extends String> observable, String oldValue, String newValue) { if (!settingsPane.isExperimentRunning()) { - if (newValue.matches("([0-9]*[.]*[0-9]*)")) { + //if (newValue.matches("([0-9]*[.]?[0-9]*)")) { + if (newValue.matches("^[-+]?[0-9]*\\.?[0-9]+$")) { if (!newValue.isEmpty()) { double value = Double.parseDouble(newValue); parameter.set(value); diff --git a/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java b/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java index e1b0b75..6e8b3f1 100644 --- a/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java @@ -33,6 +33,7 @@ public class GUIIntegerParameter extends GUIParameter<Number> { @Override protected Control makeControl() { + // this uses a text field textField = new TextField(String.valueOf(parameter.get())); textField.setStyle(VALID_PARAMETER_STYLE); textField.setAlignment(Pos.CENTER_RIGHT); @@ -44,7 +45,8 @@ public class GUIIntegerParameter extends GUIParameter<Number> { @Override protected void setControlListeners() { /* pass the TextField value back to the parameter whenever it gets - * modified, provided it is not empty and the experiment isn't running */ + * modified, provided it is not empty, the experiment isn't running + * and it matches the integer regex pattern */ textField.textProperty().addListener(new ChangeListener<String>() { @Override public void changed( @@ -98,7 +100,7 @@ public class GUIIntegerParameter extends GUIParameter<Number> { @Override public void refreshValue() { if (!textField.isFocused()) { - textField.setText(String.valueOf(parameter.get())); + textField.setText(parameter.get().toString()); } } } diff --git a/src/jcgp/gui/settings/parameters/GUIParameter.java b/src/jcgp/gui/settings/parameters/GUIParameter.java index 79762ff..b675fb5 100644 --- a/src/jcgp/gui/settings/parameters/GUIParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIParameter.java @@ -30,9 +30,8 @@ import jcgp.gui.settings.SettingsPane; * Monitor parameters have their Control disabled so that no changed can be made via the GUI. * Non-monitor parameters are updated automatically as well, but may be changed by the user * if the program is not evolving. - * <br><br> - * For more information on how parameters work in JCGP, see {@link Parameter}. * + * @see Parameter * @author Eduardo Pedroni * @param <T> the parameter data type */ @@ -174,7 +173,6 @@ public abstract class GUIParameter<T> extends HBox { */ public void validate() { parameter.validate(parameter.get()); - //refreshValue(); setValidityStyle(); } |