diff options
author | Eduardo Pedroni <ep625@york.ac.uk> | 2014-04-25 19:38:16 +0100 |
---|---|---|
committer | Eduardo Pedroni <ep625@york.ac.uk> | 2014-04-25 19:38:16 +0100 |
commit | b0c0698e5503c2506217117bf144fde31e6f6601 (patch) | |
tree | 11a6e20fb565f1e75fb25852e757e4a37e4c313b /src/jcgp/backend/modules | |
parent | 9ac2848be66c39acdc291dc3b48b91178acc1a05 (diff) |
Commented lots of packages.
Diffstat (limited to 'src/jcgp/backend/modules')
14 files changed, 608 insertions, 125 deletions
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 + + } +} |