aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/jcgp/JCGP.java24
-rw-r--r--src/jcgp/backend/function/BitwiseLogic.java506
-rw-r--r--src/jcgp/backend/function/DigitalCircuitFunctions.java504
-rw-r--r--src/jcgp/backend/function/DoubleArithmetic.java589
-rw-r--r--src/jcgp/backend/function/Function.java29
-rw-r--r--src/jcgp/backend/function/FunctionSet.java88
-rw-r--r--src/jcgp/backend/function/SymbolicRegressionFunctions.java587
-rw-r--r--src/jcgp/backend/function/TravellingSalesmanFunctions.java396
-rw-r--r--src/jcgp/backend/modules/Module.java14
-rw-r--r--src/jcgp/backend/modules/es/EvolutionaryStrategy.java34
-rw-r--r--src/jcgp/backend/modules/es/MuPlusLambda.java67
-rw-r--r--src/jcgp/backend/modules/es/TournamentSelection.java99
-rw-r--r--src/jcgp/backend/modules/mutator/FixedPointMutator.java64
-rw-r--r--src/jcgp/backend/modules/mutator/Mutator.java27
-rw-r--r--src/jcgp/backend/modules/mutator/PercentPointMutator.java73
-rw-r--r--src/jcgp/backend/modules/mutator/PointMutator.java97
-rw-r--r--src/jcgp/backend/modules/mutator/ProbabilisticMutator.java131
-rw-r--r--src/jcgp/backend/modules/problem/DigitalCircuitProblem.java20
-rw-r--r--src/jcgp/backend/modules/problem/Problem.java28
-rw-r--r--src/jcgp/backend/modules/problem/SymbolicRegressionProblem.java28
-rw-r--r--src/jcgp/backend/modules/problem/TestCaseProblem.java8
-rw-r--r--src/jcgp/backend/modules/problem/TravellingSalesmanProblem.java43
-rw-r--r--src/jcgp/backend/parser/ChromosomeParser.java177
-rw-r--r--src/jcgp/backend/parser/FunctionParser.java51
-rw-r--r--src/jcgp/backend/parser/ParameterParser.java77
-rw-r--r--src/jcgp/backend/parser/TestCaseParser.java90
-rw-r--r--src/jcgp/backend/parsers/ChromosomeParser.java3
-rw-r--r--src/jcgp/backend/parsers/FunctionParser.java10
-rw-r--r--src/jcgp/backend/population/Chromosome.java20
-rw-r--r--src/jcgp/backend/population/Population.java35
-rw-r--r--src/jcgp/backend/resources/ModifiableResources.java177
-rw-r--r--src/jcgp/backend/resources/Resources.java181
-rw-r--r--src/jcgp/backend/tests/ChromosomeTests.java16
-rw-r--r--src/jcgp/backend/tests/NodeTests.java24
-rw-r--r--src/jcgp/backend/tests/OutputTests.java4
-rw-r--r--src/jcgp/backend/tests/PopulationTests.java6
-rw-r--r--src/jcgp/gui/console/GUIConsole.java1
-rw-r--r--src/jcgp/gui/population/FunctionSelector.java2
-rw-r--r--src/jcgp/gui/population/GUINode.java4
-rw-r--r--src/jcgp/gui/settings/SettingsPane.java56
-rw-r--r--src/jcgp/gui/settings/parameters/GUIBooleanParameter.java3
-rw-r--r--src/jcgp/gui/settings/parameters/GUIDoubleParameter.java7
-rw-r--r--src/jcgp/gui/settings/parameters/GUIIntegerParameter.java6
-rw-r--r--src/jcgp/gui/settings/parameters/GUIParameter.java4
-rw-r--r--test.chr2
45 files changed, 2487 insertions, 1925 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();
}
diff --git a/test.chr b/test.chr
index d015918..34664df 100644
--- a/test.chr
+++ b/test.chr
@@ -1 +1 @@
- 1 0 7 0 0 6 1 2 7 3 5 7 1 3 7 3 5 7 4 7 7 7 4 10 6 3 7 10 11 7 2 11 7 6 11 6 13 0 7 13 12 10 14 10 7 16 \ No newline at end of file
+ 0 0 7 0 0 6 0 0 15 0 0 6 0 0 18 2 0 6 2 0 8 1 3 14 5 0 13 2 4 11 4 2 14 4 2 0 3 6 2 2 5 1 3 1 6 6 5 3 5 5 9 3 3 13 4 2 19 3 4 18 5 5 11 7 6 5 4 7 12 0 5 4 3 5 7 3 \ No newline at end of file