aboutsummaryrefslogtreecommitdiffstats
path: root/src/jcgp/CGP.java
blob: 5111508c41b5f84330935e319fb31f38e89c59e3 (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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
package jcgp;

import java.util.Random;

import jcgp.ea.EvolutionaryAlgorithm;
import jcgp.ea.Mutator;
import jcgp.ea.StandardEA;
import jcgp.ea.StandardMutator;
import jcgp.fitness.FitnessFunction;
import jcgp.fitness.TestCase;
import jcgp.fitness.TruthTableEvaluator;
import jcgp.function.Addition;
import jcgp.function.Function;
import jcgp.function.FunctionSet;
import jcgp.function.Subtraction;
import jcgp.population.Chromosome;
import jcgp.population.Connection;
import jcgp.population.Input;
import jcgp.population.MutableElement;
import jcgp.population.Node;
import jcgp.population.Population;

public final class CGP {

	public static class Parameters {
		private static int rows, columns, inputs, outputs, mutationRate, generations, runs, populationSize, levelsBack, maxArity;

		/**
		 * @return the number of nodes
		 */
		public static int getNodeNumber() {
			return rows * columns;
		}

		/**
		 * @return the levelsBack
		 */
		public static int getLevelsBack() {
			return levelsBack;
		}

		/**
		 * @param levelsBack the levelsBack to set
		 */
		private static void setLevelsBack(int levelsBack) {
			Parameters.levelsBack = levelsBack;
		}

		/**
		 * @return the populationSize
		 */
		public static int getPopulationSize() {
			return populationSize;
		}

		/**
		 * @param populationSize the populationSize to set
		 */
		private static void setPopulationSize(int populationSize) {
			Parameters.populationSize = populationSize;
		}

		/**
		 * @return the rows
		 */
		public static int getRows() {
			return rows;
		}

		/**
		 * @return the columns
		 */
		public static int getColumns() {
			return columns;
		}

		/**
		 * @return the inputs
		 */
		public static int getInputs() {
			return inputs;
		}

		/**
		 * @return the outputs
		 */
		public static int getOutputs() {
			return outputs;
		}

		/**
		 * @return the mutationRate
		 */
		public static int getMutationRate() {
			return mutationRate;
		}

		/**
		 * @return the generations
		 */
		public static int getGenerations() {
			return generations;
		}

		/**
		 * @return the runs
		 */
		public static int getRuns() {
			return runs;
		}

		/**
		 * @param rows the rows to set
		 */
		private static void setRows(int rows) {
			Parameters.rows = rows;
		}

		/**
		 * @param columns the columns to set
		 */
		private static void setColumns(int columns) {
			Parameters.columns = columns;
		}

		/**
		 * @param inputs the inputs to set
		 */
		private static void setInputs(int inputs) {
			Parameters.inputs = inputs;
		}

		/**
		 * @param outputs the outputs to set
		 */
		private static void setOutputs(int outputs) {
			Parameters.outputs = outputs;
		}

		/**
		 * @param mutationRate the mutationRate to set
		 */
		private static void setMutationRate(int mutationRate) {
			Parameters.mutationRate = mutationRate;
		}

		/**
		 * @param generations the generations to set
		 */
		private static void setGenerations(int generations) {
			Parameters.generations = generations;
		}

		/**
		 * @param runs the runs to set
		 */
		private static void setRuns(int runs) {
			Parameters.runs = runs;
		}

		/**
		 * @return the maxArity
		 */
		public static int getMaxArity() {
			return maxArity;
		}

		/**
		 * @param maxArity the maxArity to set
		 */
		private static void setMaxArity(int maxArity) {
			Parameters.maxArity = maxArity;
		}
	}

	public abstract static class Utilities {

		public static int getRandomInt(int limit){
			return numberGenerator.nextInt(limit);
		}

		public static double getRandomDouble(int limit){
			return numberGenerator.nextDouble() * limit;
		}

		/**
		 * Returns a random allowed connection respecting levels back.
		 * This method may always pick inputs, as they can be picked
		 * regardless of the column.
		 * 
		 * @param chromosome the chromosome to pick from
		 * @param column the column to use as reference
		 * @return a random connection
		 */
		public static Connection getRandomConnection(Chromosome chromosome, int column){
			// work out the allowed range obeying levels back
			int allowedColumns = ((column >= Parameters.getLevelsBack()) ? Parameters.getLevelsBack() : column);
			int offset = column - allowedColumns;

			// choose input or node
			int connectionType = getRandomInt(Parameters.getInputs() + (Parameters.getRows() * allowedColumns));
			if (connectionType < Parameters.getInputs()) {
				// input
				return chromosome.getInput(getRandomInt(Parameters.getInputs()));
			} else {
				// node				
				return chromosome.getNode(getRandomInt(Parameters.getRows()), getRandomInt(allowedColumns) + offset);
			}
		}

		/**
		 * Returns a random allowed connection. 
		 * 
		 * This method may always pick inputs, as they can be picked
		 * regardless of the column.
		 * 
		 * @param chromosome the chromosome to pick from
		 * @param column the column to use as reference
		 * @param levelsBack whether or not to respect levels back
		 * @return a random connection
		 */
		public static Connection getRandomConnection(Chromosome chromosome, int column, boolean levelsBack){
			if (levelsBack) {
				return getRandomConnection(chromosome, column);
			} else {
				// choose input or node
				int connectionType = getRandomInt(Parameters.getInputs() + (Parameters.getRows() * column));
				if (connectionType < Parameters.getInputs()) {
					// input
					return chromosome.getInput(getRandomInt(Parameters.getInputs()));
				} else {
					// node				
					return chromosome.getNode(getRandomInt(Parameters.getRows()), getRandomInt(column));
				}
			}
		}

		/**
		 * @param chromosome the chromosome to choose from
		 * @return a random input
		 */
		public static Input getRandomInput(Chromosome chromosome){
			return chromosome.getInput(getRandomInt(Parameters.getInputs()));
		}

		/**
		 * Returns a random allowed node respecting levels back.
		 * 
		 * This method will NOT pick inputs.
		 * 
		 * @param chromosome the chromosome to pick from
		 * @param column the column to use as reference
		 * @return a random node
		 */
		public static Node getRandomNode(Chromosome chromosome, int column){
			// work out the allowed range obeying levels back
			int allowedColumns = ((column >= Parameters.getLevelsBack()) ? Parameters.getLevelsBack() : column);
			int offset = column - allowedColumns;
			
			// pick a random allowed column and row
			int randomColumn = (getRandomInt(allowedColumns) + offset);
			int randomRow = (getRandomInt(Parameters.getRows()));

			return chromosome.getNode(randomRow, randomColumn);
		}

		/**
		 * Returns a random allowed node.
		 * 
		 * This method will NOT pick inputs.
		 * 
		 * @param chromosome the chromosome to pick from
		 * @param column the column to use as reference
		 * @param levelsBack whether or not to respect levels back
		 * @return a random node
		 */
		public static Node getRandomNode(Chromosome chromosome, int column, boolean levelsBack){
			if (levelsBack) {
				return getRandomNode(chromosome, column);
			} else {
				// pick any random column before the given column
				int randomColumn = (getRandomInt(column));
				// pick a random rowgetColumns
				int randomRow = (getRandomInt(Parameters.getRows()));
				return chromosome.getNode(randomRow, randomColumn);
			}
		}

		/**
		 * This method picks a random mutable element from the given chromosome.
		 * 
		 * It will pick outputs or nodes fairly.
		 * 
		 * @param chromosome the chromosome to pick from
		 * @return a random mutable element
		 */
		public static MutableElement getRandomMutable(Chromosome chromosome){
			// choose output or node
			int connectionType = getRandomInt(Parameters.getOutputs() + Parameters.getNodeNumber());
			
			if (connectionType < Parameters.getOutputs()) {
				// outputs
				return chromosome.getOutput(getRandomInt(Parameters.getOutputs()));
			} else {
				// node				
				return chromosome.getNode(getRandomInt(Parameters.getRows()), getRandomInt(Parameters.getRows()));
			}
		}		
		
		/**
		 * pick from any column - use this for setting outputs
		 * 
		 * @param chromosome
		 * @return
		 */
		public static Node getRandomNode(Chromosome chromosome) {
			return chromosome.getNode(getRandomInt(Parameters.getRows()), getRandomInt(Parameters.getColumns()));
		}

		public static Function getRandomFunction() {
			return functionSet.getFunction(Utilities.getRandomInt(functionSet.getFunctionCount()));
		}

		public static Function getFunction(int index) {
			return functionSet.getFunction(index);
		}
	}

	public static class TruthTable {
		
		private static TestCase[] testCases;
		
		private static void setTestCases(TestCase ... testCases) {
			TruthTable.testCases = testCases;
		}
		
		public static TestCase getTestCase(int index) {
			return testCases[index];
		}
		
		public static int getTestCaseCount() {
			return testCases.length;
		}
	
	}
	
	// system-wide resources
	private static FunctionSet functionSet;
	private static Random numberGenerator;

	// CGP components
	private EvolutionaryAlgorithm ea;
	private Mutator mutator;
	private Population population;
	private FitnessFunction fitnessFunction;

	public CGP() {
		initialise();

		fitnessFunction.evaluate(population);

		ea.evolve(population, mutator);
	}

	/**
	 * 
	 */
	private void initialise() {
		// initialise random number generator
		numberGenerator = new Random(1234);
		
		// initialise function set
		functionSet = new FunctionSet(new Addition(), new Subtraction());

		// initialise parameters
		Parameters.setInputs(3);
		Parameters.setColumns(3);
		Parameters.setRows(3);
		Parameters.setOutputs(3);
		Parameters.setGenerations(10);
		Parameters.setMutationRate(1);
		Parameters.setRuns(5);
		Parameters.setPopulationSize(5);
		Parameters.setLevelsBack(1);

		// compute and set maximum arity
		Parameters.setMaxArity(functionSet.getMaxArity());

		// initialise EA
		ea = new StandardEA();
		mutator = new StandardMutator();

		// initialise fitness function and truth table
		TruthTable.setTestCases(new TestCase(new int[] {2, 5, 4}, new int[] {1, 10, 15}));
		fitnessFunction = new TruthTableEvaluator();
		
		// initialise population
		population = new Population();

	}
}