From 67ace66f66ffaa00e1bd1495c0d406c801e59c5c Mon Sep 17 00:00:00 2001 From: Eduardo Pedroni Date: Mon, 7 Apr 2014 10:04:05 +0100 Subject: Refactored problem types --- src/jcgp/JCGP.java | 55 +++++----- .../backend/modules/fitness/DigitalCircuit.java | 17 ++++ .../backend/modules/fitness/FitnessFunction.java | 11 -- src/jcgp/backend/modules/fitness/Problem.java | 20 ++++ .../modules/fitness/SymbolicRegression.java | 17 ++++ .../backend/modules/fitness/TestCaseProblem.java | 112 +++++++++++++++++++++ .../fitness/testcase/TestCaseEvaluator.java | 87 ---------------- .../backend/resources/ModifiableResources.java | 5 +- src/jcgp/backend/resources/Resources.java | 20 +--- src/jcgp/gui/settings/SettingsPane.java | 45 ++++----- 10 files changed, 225 insertions(+), 164 deletions(-) create mode 100644 src/jcgp/backend/modules/fitness/DigitalCircuit.java delete mode 100644 src/jcgp/backend/modules/fitness/FitnessFunction.java create mode 100644 src/jcgp/backend/modules/fitness/Problem.java create mode 100644 src/jcgp/backend/modules/fitness/SymbolicRegression.java create mode 100644 src/jcgp/backend/modules/fitness/TestCaseProblem.java delete mode 100644 src/jcgp/backend/modules/fitness/testcase/TestCaseEvaluator.java diff --git a/src/jcgp/JCGP.java b/src/jcgp/JCGP.java index 70b01a8..a19e085 100644 --- a/src/jcgp/JCGP.java +++ b/src/jcgp/JCGP.java @@ -1,10 +1,14 @@ package jcgp; +import java.util.ArrayList; + import jcgp.backend.modules.ea.EvolutionaryAlgorithm; import jcgp.backend.modules.ea.MuPlusLambda; import jcgp.backend.modules.ea.TournamentSelection; -import jcgp.backend.modules.fitness.FitnessFunction; -import jcgp.backend.modules.fitness.testcase.TestCaseEvaluator.TestCase; +import jcgp.backend.modules.fitness.DigitalCircuit; +import jcgp.backend.modules.fitness.Problem; +import jcgp.backend.modules.fitness.SymbolicRegression; +import jcgp.backend.modules.fitness.TestCaseProblem.TestCase; import jcgp.backend.modules.mutator.Mutator; import jcgp.backend.modules.mutator.PointMutator; import jcgp.backend.population.Population; @@ -50,30 +54,34 @@ public class JCGP { new TournamentSelection()}; private EvolutionaryAlgorithm evolutionaryAlgorithm; - // fitness evaluators - private FitnessFunction[] fitnessFunctions = new FitnessFunction[] { - /*new TestCaseEvaluator()*/ }; - private FitnessFunction fitnessFunction; + // problem types + private Problem[] problems = new Problem[] { + new SymbolicRegression(), + new DigitalCircuit() }; + private Problem problem; /* * the population of chromosomes */ private Population population; - private boolean finished = false; public JCGP() { + setEvolutionaryAlgorithm(0); + setMutator(0); + setProblem(0); population = new Population(resources); - - evolutionaryAlgorithm = evolutionaryAlgorithms[0]; - mutator = mutators[0]; + ArrayList> tc = new ArrayList>(); + tc.add(new TestCase(new Integer[]{1, 2, 3}, new Integer[]{-4, 5, 6})); - //fitnessFunction = fitnessFunctions[0]; + ((SymbolicRegression) problem).setTestCases(tc); - //resources.setTestCases(new TestCase(new Integer[]{1, 2, 3}, new Integer[]{-4, 5, 6})); - + ArrayList> tcdc = new ArrayList>(); + tcdc.add(new TestCase(new Integer[]{1, 2, 3}, new Integer[]{-4, 5, 6})); + + ((DigitalCircuit) problems[1]).setTestCases(tc); } public ModifiableResources getResources() { @@ -119,16 +127,16 @@ public class JCGP { /** * @return the fitnessFunctions */ - public FitnessFunction[] getFitnessFunctions() { - return fitnessFunctions; + public Problem[] getProblems() { + return problems; } /** * @return the fitnessFunction */ - public FitnessFunction getFitnessFunction() { - return fitnessFunction; + public Problem getProblem() { + return problem; } @@ -149,21 +157,21 @@ public class JCGP { /** - * @param fitnessFunction the fitnessFunction to set + * @param problem the fitnessFunction to set */ - public void setFitnessFunction(int index) { - this.fitnessFunction = fitnessFunctions[index]; + public void setProblem(int index) { + this.problem = problems[index]; + resources.setFunctionSet(problem.getFunctionSet()); } public void nextGeneration() { if (!finished) { - fitnessFunction.evaluate(population, (Resources) resources); - + problem.evaluate(population, (Resources) resources); report(); if (resources.getInt("currentGen") < resources.getInt("generations")) { // we still have generations left to go - if (population.getChromosome(evolutionaryAlgorithm.getFittestChromosome()).getFitness() >= resources.getInt("maxFitness")) { + if (problem.isPerfectSolution(population.getChromosome(evolutionaryAlgorithm.getFittestChromosome()))) { // solution has been found, start next run resources.println("Solution found on generation " + resources.getInt("currentGen") + ", chromosome: " + evolutionaryAlgorithm.getFittestChromosome()); @@ -181,7 +189,6 @@ public class JCGP { } else { resources.set("currentGen", resources.getInt("currentGen") + 1); } - } else { // the run has ended, check if any more runs must be done resources.println("Solution not found, highest fitness achieved was " diff --git a/src/jcgp/backend/modules/fitness/DigitalCircuit.java b/src/jcgp/backend/modules/fitness/DigitalCircuit.java new file mode 100644 index 0000000..b01bdc5 --- /dev/null +++ b/src/jcgp/backend/modules/fitness/DigitalCircuit.java @@ -0,0 +1,17 @@ +package jcgp.backend.modules.fitness; + +import jcgp.backend.function.BitwiseLogic; + +public class DigitalCircuit extends TestCaseProblem { + + public DigitalCircuit() { + super(); + functionSet = new BitwiseLogic(); + } + + @Override + public String toString() { + return "Digital circuit"; + } + +} diff --git a/src/jcgp/backend/modules/fitness/FitnessFunction.java b/src/jcgp/backend/modules/fitness/FitnessFunction.java deleted file mode 100644 index d3b76cb..0000000 --- a/src/jcgp/backend/modules/fitness/FitnessFunction.java +++ /dev/null @@ -1,11 +0,0 @@ -package jcgp.backend.modules.fitness; - -import jcgp.backend.modules.Module; -import jcgp.backend.population.Population; -import jcgp.backend.resources.Resources; - -public interface FitnessFunction extends Module { - - public void evaluate(Population population, Resources resources); - -} diff --git a/src/jcgp/backend/modules/fitness/Problem.java b/src/jcgp/backend/modules/fitness/Problem.java new file mode 100644 index 0000000..1e70c13 --- /dev/null +++ b/src/jcgp/backend/modules/fitness/Problem.java @@ -0,0 +1,20 @@ +package jcgp.backend.modules.fitness; + +import jcgp.backend.function.FunctionSet; +import jcgp.backend.modules.Module; +import jcgp.backend.population.Chromosome; +import jcgp.backend.population.Population; +import jcgp.backend.resources.Resources; + +public abstract class Problem implements Module { + + protected FunctionSet functionSet; + + public abstract void evaluate(Population population, Resources resources); + + public FunctionSet getFunctionSet() { + return functionSet; + } + + public abstract boolean isPerfectSolution(Chromosome fittest); +} diff --git a/src/jcgp/backend/modules/fitness/SymbolicRegression.java b/src/jcgp/backend/modules/fitness/SymbolicRegression.java new file mode 100644 index 0000000..da2e69e --- /dev/null +++ b/src/jcgp/backend/modules/fitness/SymbolicRegression.java @@ -0,0 +1,17 @@ +package jcgp.backend.modules.fitness; + +import jcgp.backend.function.IntegerArithmetic; + +public class SymbolicRegression extends TestCaseProblem { + + public SymbolicRegression() { + super(); + functionSet = new IntegerArithmetic(); + } + + @Override + public String toString() { + return "Symbolic regression"; + } + +} diff --git a/src/jcgp/backend/modules/fitness/TestCaseProblem.java b/src/jcgp/backend/modules/fitness/TestCaseProblem.java new file mode 100644 index 0000000..7753e26 --- /dev/null +++ b/src/jcgp/backend/modules/fitness/TestCaseProblem.java @@ -0,0 +1,112 @@ +package jcgp.backend.modules.fitness; + +import java.util.ArrayList; +import java.util.List; + +import jcgp.backend.population.Chromosome; +import jcgp.backend.population.Population; +import jcgp.backend.resources.Resources; +import jcgp.backend.resources.parameters.IntegerParameter; +import jcgp.backend.resources.parameters.Parameter; + +/** + * + * This fitness function module implements a simple test case evaluator. + * + * A TestCase object is a + * + * + * @author Eduardo Pedroni + * + */ +public abstract class TestCaseProblem extends Problem { + + public static class TestCase { + + private T[] inputs; + private T[] outputs; + + public TestCase(T[] inputs, T[] outputs) { + this.inputs = inputs; + this.outputs = outputs; + } + + public T getInput(int index) { + return inputs[index]; + } + + public T getOutput(int index) { + return outputs[index]; + } + + public T[] getInputs() { + return inputs; + } + + public T[] getOutputs() { + return outputs; + } + } + + private ArrayList> testCases; + private IntegerParameter maxFitness; + + public TestCaseProblem() { + maxFitness = new IntegerParameter(0, "Max fitness", true, false) { + @Override + public void validate(int newValue) { + // blank + } + }; + testCases = new ArrayList>(); + } + + + @Override + public void evaluate(Population population, Resources resources) { + // for every chromosome in the population + for (int i = 0; i < resources.getInt("popSize"); i++) { + // assume an initial fitness of 0 + int fitness = 0; + // for each test case + for (int t = 0; t < testCases.size(); t++) { + population.getChromosome(i).setInputs(testCases.get(t).getInputs()); + // check each output + for (int o = 0; o < resources.getInt("outputs"); o++) { + if (population.getChromosome(i).getOutput(o).calculate() == testCases.get(t).getOutput(o)) { + fitness++; + } + } + } + // assign the resulting fitness to the respective individual + population.getChromosome(i).setFitness(fitness); + } + } + + @Override + public Parameter[] getLocalParameters() { + return new Parameter[]{maxFitness}; + } + + private int getMaxFitness() { + int fitness = 0; + + for (TestCase tc : testCases) { + fitness += tc.getOutputs().length; + } + + return fitness; + } + + public void setTestCases(List> testCases) { + this.testCases.clear(); + this.testCases.addAll(testCases); + maxFitness.set(getMaxFitness()); + } + + @Override + public boolean isPerfectSolution(Chromosome fittest) { + return fittest.getFitness() >= maxFitness.get(); + } +} + diff --git a/src/jcgp/backend/modules/fitness/testcase/TestCaseEvaluator.java b/src/jcgp/backend/modules/fitness/testcase/TestCaseEvaluator.java deleted file mode 100644 index 4b0654c..0000000 --- a/src/jcgp/backend/modules/fitness/testcase/TestCaseEvaluator.java +++ /dev/null @@ -1,87 +0,0 @@ -package jcgp.backend.modules.fitness.testcase; - -import java.util.ArrayList; -import java.util.Collections; - -import jcgp.backend.modules.fitness.FitnessFunction; -import jcgp.backend.population.Population; -import jcgp.backend.resources.Resources; - -/** - * - * This fitness function module implements a simple test case evaluator. - * - * A TestCase object is a - * - * - * @author Eduardo Pedroni - * - */ -public abstract class TestCaseEvaluator implements FitnessFunction { - - public static class TestCase { - - private T[] inputs; - private T[] outputs; - - public TestCase(T[] inputs, T[] outputs) { - this.inputs = inputs; - this.outputs = outputs; - } - - public T getInput(int index) { - return inputs[index]; - } - - public T getOutput(int index) { - return outputs[index]; - } - - public T[] getInputs() { - return inputs; - } - - public T[] getOutputs() { - return outputs; - } - } - - protected ArrayList> testCases; - - @Override - public void evaluate(Population population, Resources resources) { - // for every chromosome in the population - for (int i = 0; i < resources.getInt("popSize"); i++) { - // assume an initial fitness of 0 - int fitness = 0; - // for each test case - for (int t = 0; t < testCases.size(); t++) { - population.getChromosome(i).setInputs(testCases.get(t).getInputs()); - // check each output - for (int o = 0; o < resources.getInt("outputs"); o++) { - if (population.getChromosome(i).getOutput(o).calculate() == testCases.get(t).getOutput(o)) { - fitness++; - } - } - } - // assign the resulting fitness to the respective individual - population.getChromosome(i).setFitness(fitness); - } - } - - public int getMaxFitness() { - int fitness = 0; - - for (TestCase tc : testCases) { - fitness += tc.getOutputs().length; - } - - return fitness; - } - - public void setTestCases(TestCase... testCases) { - this.testCases = new ArrayList>(); - Collections.addAll(this.testCases, testCases); - } -} - diff --git a/src/jcgp/backend/resources/ModifiableResources.java b/src/jcgp/backend/resources/ModifiableResources.java index 3e6b55e..90c2f03 100644 --- a/src/jcgp/backend/resources/ModifiableResources.java +++ b/src/jcgp/backend/resources/ModifiableResources.java @@ -1,5 +1,6 @@ package jcgp.backend.resources; +import jcgp.backend.function.FunctionSet; import jcgp.backend.resources.parameters.BooleanParameter; import jcgp.backend.resources.parameters.DoubleParameter; import jcgp.backend.resources.parameters.IntegerParameter; @@ -30,8 +31,8 @@ public class ModifiableResources extends Resources { } } - public void setFunctionSet(int index) { - functionSet = functionSets[index]; + public void setFunctionSet(FunctionSet functionSet) { + this.functionSet = functionSet; set("arity", functionSet.getMaxArity()); } diff --git a/src/jcgp/backend/resources/Resources.java b/src/jcgp/backend/resources/Resources.java index c1c3e4c..d1f396a 100644 --- a/src/jcgp/backend/resources/Resources.java +++ b/src/jcgp/backend/resources/Resources.java @@ -5,11 +5,8 @@ import java.util.Random; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; -import jcgp.backend.function.BitwiseLogic; -import jcgp.backend.function.BooleanLogic; import jcgp.backend.function.Function; import jcgp.backend.function.FunctionSet; -import jcgp.backend.function.IntegerArithmetic; import jcgp.backend.resources.parameters.BooleanParameter; import jcgp.backend.resources.parameters.DoubleParameter; import jcgp.backend.resources.parameters.IntegerParameter; @@ -30,12 +27,7 @@ public class Resources { protected Random numberGenerator = new Random(); - // function sets - protected FunctionSet[] functionSets = new FunctionSet[] { - new IntegerArithmetic(), - new BitwiseLogic(), - new BooleanLogic() }; - protected FunctionSet functionSet = functionSets[0]; + protected FunctionSet functionSet; // GUI console protected Console console; @@ -202,7 +194,8 @@ public class Resources { } }); - parameters.put("arity", new IntegerParameter(functionSet.getMaxArity(), "Max arity", true, false) { + //parameters.put("arity", new IntegerParameter(functionSet.getMaxArity(), "Max arity", true, false) { + parameters.put("arity", new IntegerParameter(0, "Max arity", true, false) { @Override public void validate(int newValue) { // blank @@ -266,13 +259,6 @@ public class Resources { return functionSet.getAllowedFunction(index); } - /** - * @return the functionSets - */ - public FunctionSet[] getFunctionSets() { - return functionSets; - } - /** * @return the functionSet */ diff --git a/src/jcgp/gui/settings/SettingsPane.java b/src/jcgp/gui/settings/SettingsPane.java index 8c707d3..5b23bdd 100644 --- a/src/jcgp/gui/settings/SettingsPane.java +++ b/src/jcgp/gui/settings/SettingsPane.java @@ -14,12 +14,11 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; import javafx.scene.text.Font; -import javafx.scene.text.FontSmoothingType; import javafx.scene.text.Text; import jcgp.JCGP; import jcgp.backend.function.FunctionSet; import jcgp.backend.modules.ea.EvolutionaryAlgorithm; -import jcgp.backend.modules.fitness.FitnessFunction; +import jcgp.backend.modules.fitness.Problem; import jcgp.backend.modules.mutator.Mutator; import jcgp.backend.resources.parameters.Parameter; import jcgp.gui.GUI; @@ -28,7 +27,7 @@ import jcgp.gui.settings.parameters.GUIParameter; public class SettingsPane extends AnchorPane { private VBox mainContainer; - private VBox baseParameterPane, eaPane, mutatorPane, ffPane, nfPane; + private VBox baseParameterPane, eaPane, mutatorPane, problemPane; private Button runPause = new Button("Run"), step = new Button("Step"), reset = new Button("Reset"); @@ -95,7 +94,7 @@ public class SettingsPane extends AnchorPane { parameters.add(GUIParameter.create(cgp.getResources().getParameter("seed"), this)); parameters.add(GUIParameter.create(cgp.getResources().getParameter("report"), this)); - parameters.add(GUIParameter.create(cgp.getResources().getParameter("maxFitness"), this)); + //parameters.add(GUIParameter.create(cgp.getResources().getParameter("maxFitness"), this)); baseParameterPane.getChildren().addAll(parameters); mainContainer.getChildren().add(baseParameterPane); @@ -138,7 +137,6 @@ public class SettingsPane extends AnchorPane { mutatorPane = new VBox(2); Text header = new Text("Mutator"); - header.setFontSmoothingType(FontSmoothingType.LCD); header.setFont(Font.font("Arial", 14)); header.setUnderline(true); @@ -156,7 +154,7 @@ public class SettingsPane extends AnchorPane { mutatorCBox.setOnAction(new EventHandler() { @Override public void handle(ActionEvent event) { - cgp.setEvolutionaryAlgorithm(mutatorCBox.getSelectionModel().getSelectedIndex()); + cgp.setMutator(mutatorCBox.getSelectionModel().getSelectedIndex()); if (mutatorCBox.getSelectionModel().getSelectedItem().getLocalParameters() != null) { refreshParameters(mutatorCBox.getSelectionModel().getSelectedItem().getLocalParameters(), mutatorParameters); } @@ -168,39 +166,41 @@ public class SettingsPane extends AnchorPane { } private void initialiseProblemTypeParameters(final JCGP cgp, final GUI gui) { - ffPane= new VBox(2); + problemPane= new VBox(2); Text header = new Text("Problem type"); header.setFont(Font.font("Arial", 14)); header.setUnderline(true); - final ComboBox ffCBox = new ComboBox(); - ffCBox.getItems().addAll(cgp.getFitnessFunctions()); - ffCBox.getSelectionModel().select(cgp.getFitnessFunction()); + final ComboBox ffCBox = new ComboBox(); + ffCBox.getItems().addAll(cgp.getProblems()); + ffCBox.getSelectionModel().select(cgp.getProblem()); ffCBox.prefWidthProperty().bind(mainContainer.widthProperty()); final VBox ffParameters = new VBox(); ffParameters.setSpacing(2); - if (cgp.getFitnessFunction().getLocalParameters() != null) { - refreshParameters(cgp.getFitnessFunction().getLocalParameters(), ffParameters); + if (cgp.getProblem().getLocalParameters() != null) { + refreshParameters(cgp.getProblem().getLocalParameters(), ffParameters); } + final VBox nodeFunctions = new VBox(); + nodeFunctions.setSpacing(2); + refreshFunctions(cgp.getResources().getFunctionSet(), nodeFunctions, gui); + ffCBox.setOnAction(new EventHandler() { @Override public void handle(ActionEvent event) { - cgp.setEvolutionaryAlgorithm(ffCBox.getSelectionModel().getSelectedIndex()); + cgp.setProblem(ffCBox.getSelectionModel().getSelectedIndex()); if (ffCBox.getSelectionModel().getSelectedItem().getLocalParameters() != null) { - refreshParameters(ffCBox.getSelectionModel().getSelectedItem().getLocalParameters(), ffParameters); - } + refreshParameters(cgp.getProblem().getLocalParameters(), ffParameters); + refreshFunctions(cgp.getProblem().getFunctionSet(), nodeFunctions, gui); + } + gui.reset(); } }); - final VBox nfParameters = new VBox(); - nfParameters.setSpacing(2); - refreshFunctions(cgp.getResources().getFunctionSet(), nfParameters, gui); - - ffPane.getChildren().addAll(header, ffCBox, ffParameters, nfParameters); - mainContainer.getChildren().add(ffPane); + problemPane.getChildren().addAll(header, ffCBox, ffParameters, nodeFunctions); + mainContainer.getChildren().add(problemPane); } @@ -293,8 +293,7 @@ public class SettingsPane extends AnchorPane { baseParameterPane.setDisable(value); eaPane.setDisable(value); mutatorPane.setDisable(value); - ffPane.setDisable(value); - nfPane.setDisable(value); + problemPane.setDisable(value); step.setDisable(value); reset.setDisable(value); -- cgit v1.2.3