diff options
Diffstat (limited to 'src/jcgp/backend/modules/mutator')
5 files changed, 334 insertions, 58 deletions
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"; +	} +}  | 
