aboutsummaryrefslogtreecommitdiffstats
path: root/src/jcgp/backend/modules
diff options
context:
space:
mode:
Diffstat (limited to 'src/jcgp/backend/modules')
-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
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
+
+ }
+}