aboutsummaryrefslogtreecommitdiffstats
path: root/src/jcgp/backend/modules/mutator/ProbabilisticMutator.java
blob: e3c1d037dc85d315027157ef8ad9ef3d1bfbfbf5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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";
	}
}