Mercurial > projects > aid
comparison trunk/aid/ga.d @ 0:4b2e8e8a633e
Repository setup.
author | revcompgeek |
---|---|
date | Mon, 03 Mar 2008 19:28:10 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4b2e8e8a633e |
---|---|
1 /*********************************** | |
2 * Provides a set of classes * | |
3 * for using Genetic Algorithms. * | |
4 * * | |
5 * Author: Matt P. * | |
6 * Version: 0.1 * | |
7 ***********************************/ | |
8 | |
9 module aid.ga; | |
10 | |
11 import tango.math.Random; | |
12 import tango.io.Stdout; | |
13 | |
14 void removeIndex(Gene[] array, uint index){ | |
15 //scope(failure) Stdout.format("E: removeIndex {}", index).newline; | |
16 if (index == 0) | |
17 array = array[1..$]; | |
18 else if (index == array.length) | |
19 array = array[0..$-1]; | |
20 else | |
21 array = array[0..index-1] ~ array[index+1..$]; | |
22 } | |
23 | |
24 class Gene { | |
25 char[] geneSequence; | |
26 private double fit = -1; // Save the fitness value so that it doesn't have to be calculated more than once. | |
27 private GeneticAlgorithm* ga; | |
28 | |
29 this(GeneticAlgorithm* a){ | |
30 ga = a; | |
31 } | |
32 | |
33 this(Gene g, GeneticAlgorithm* a){ | |
34 scope(failure) Stdout("E: Gene clone").newline; | |
35 geneSequence = g.geneSequence.dup; | |
36 this(a); | |
37 } | |
38 | |
39 double fitness(){ // Get property | |
40 if (fit == -1){ | |
41 fit = ga.calculateFitness(geneSequence); | |
42 } | |
43 return fit; | |
44 } | |
45 | |
46 Gene[] crossover(Gene other){ | |
47 Gene gene1 = new Gene(this,ga),gene2 = new Gene(other,ga); | |
48 auto r = Random.shared; | |
49 auto len = geneSequence.length; | |
50 char[] temp; | |
51 | |
52 //scope(failure) Stdout.format("E: crossover {}").newline; | |
53 | |
54 if (ga.crossoverType == 2){ | |
55 //Stdout("1").flush; | |
56 uint point1 = r.next(len-2)+1; | |
57 uint point2 = r.next(len-2)+1; | |
58 while (point1 == point2) | |
59 point2 = r.next(len); | |
60 if (point2 < point1){ | |
61 scope uint t = point1; | |
62 point1 = point2; | |
63 point2 = t; | |
64 } | |
65 //Stdout("2").newline; | |
66 scope(failure) Stdout.format("Ed: {},{}",point1,point2).newline; | |
67 //Stdout(point1)(", ")(point2).newline; | |
68 temp = gene1.geneSequence[point1..point2].dup; | |
69 //Stdout("4").flush; | |
70 gene1.geneSequence[point1..point2] = gene2.geneSequence[point1..point2].dup; | |
71 //Stdout("5").flush; | |
72 gene2.geneSequence[point1..point2] = temp; | |
73 //Stdout("6").flush; | |
74 } else if (ga.crossoverType == 1) { | |
75 uint point = r.next(len-2)+1; | |
76 temp = gene1.geneSequence[point..$].dup; | |
77 gene1.geneSequence[point..$] = gene2.geneSequence[point..$]; | |
78 gene2.geneSequence[point..$] = temp; | |
79 } else { // Uniform crossover | |
80 for (int i = 0; i < len / 2; i++){ | |
81 uint point = r.next(len); | |
82 | |
83 auto t = gene1.geneSequence[point]; | |
84 gene1.geneSequence[point] = gene2.geneSequence[point]; | |
85 gene2.geneSequence[point] = t; | |
86 } | |
87 } | |
88 return [gene1,gene2]; | |
89 } | |
90 | |
91 Gene mutate(){ | |
92 Gene g = new Gene(this,ga); | |
93 uint i = Random.shared.next(g.geneSequence.length); | |
94 g.geneSequence[i] = ga.getRandomChar(i); | |
95 return g; | |
96 } | |
97 } | |
98 | |
99 class Generation { | |
100 private Gene[] genes; | |
101 private uint population; | |
102 private GeneticAlgorithm* ga; | |
103 | |
104 public this(GeneticAlgorithm* g, bool generate = false){ | |
105 ga = g; | |
106 population = ga.startPopulation; | |
107 if (generate) generateGenes(); | |
108 } | |
109 | |
110 public this(Generation gen){ | |
111 genes = gen.genes; | |
112 ga = gen.ga; | |
113 } | |
114 | |
115 public Generation evolve(){ | |
116 Generation newGen = new Generation(ga); | |
117 | |
118 // Use tournament selection to create a new Generation | |
119 uint livingPop = cast(uint)(population * ga.survivalRate); | |
120 newGen.population = population; | |
121 newGen.genes.length = population; | |
122 for (auto i = 0; i < livingPop; i++){ | |
123 uint t = tournamentSelect(); | |
124 newGen.genes[i] = genes[t]; | |
125 //genes.removeIndex(t); | |
126 } | |
127 | |
128 // Cross over the left over genes | |
129 for (auto i = livingPop; i < population; i+=2){ | |
130 uint i1 = tournamentSelect(), i2 = tournamentSelect(); | |
131 while (i1 == i2) | |
132 i2 = tournamentSelect(); | |
133 Gene g1 = genes[i1], g2 = genes[i2]; | |
134 | |
135 Gene[] gs = g1.crossover(g2); | |
136 | |
137 newGen.genes[i..i+2] = gs[]; | |
138 if (ga.deleteOnCrossover){ | |
139 genes.removeIndex(i1); | |
140 genes.removeIndex(i2); | |
141 } | |
142 } | |
143 | |
144 // Mutate a small amount of genes | |
145 for (auto i = 0; i < population * ga.mutationRate; i++){ | |
146 uint index = Random.shared.next(newGen.genes.length); | |
147 newGen.genes[index] = newGen.genes[index].mutate(); | |
148 } | |
149 | |
150 return newGen; | |
151 } | |
152 | |
153 private void generateGenes(){ | |
154 scope(failure) Stdout("E: generateGenes").newline; | |
155 genes.length = population; | |
156 for (uint i = 0; i < population; i++){ | |
157 Gene t = new Gene(ga); | |
158 t.geneSequence = ga.getRandomGenotype(); | |
159 genes[i] = t; | |
160 } | |
161 } | |
162 | |
163 private uint tournamentSelect(){ | |
164 Gene[4] gens; | |
165 uint[4] indices; | |
166 scope(failure) Stdout("E: tournamentSelect").newline; | |
167 for (uint i = 0; i < 4; i++){ | |
168 indices[i] = Random.shared.next(genes.length); | |
169 gens[i] = genes[indices[i]]; | |
170 } | |
171 double max = gens[0].fitness; | |
172 uint index = indices[0]; | |
173 foreach (i,g; gens){ | |
174 if (g.fitness > max){ | |
175 max = g.fitness; | |
176 index = indices[i]; | |
177 } | |
178 } | |
179 return index; | |
180 } | |
181 } | |
182 | |
183 class GeneticAlgorithm { | |
184 public double delegate(char[]) calculateFitness; // Set these before you do anything with the library | |
185 public char[] delegate() getRandomGenotype; | |
186 public char delegate(uint index) getRandomChar; | |
187 public double survivalRate = 0.5; | |
188 public double mutationRate = 0.05; | |
189 public bool deleteOnCrossover = true; | |
190 public int crossoverType = 1; // 1 point crossover | |
191 public uint startPopulation = 1000; | |
192 public bool verbose = false; | |
193 public uint bailout = 1000; | |
194 | |
195 public double fitnessThreshold; | |
196 | |
197 public uint run(){ | |
198 //Stdout("t").flush; | |
199 Generation currGen = new Generation(&this,true); | |
200 //Stdout("test").flush; | |
201 bool stop = false; | |
202 uint generation = 1; | |
203 double average; | |
204 double max; | |
205 while (true){ | |
206 if (verbose) max = 0; | |
207 if (verbose) average = 0; | |
208 foreach (Gene g; currGen.genes){ | |
209 if (verbose) average += g.fitness; | |
210 | |
211 if (verbose) if (g.fitness > max) max = g.fitness; | |
212 if (g.fitness >= fitnessThreshold){ | |
213 stop = true; | |
214 } | |
215 } | |
216 if (verbose) average /= currGen.genes.length; | |
217 if (verbose) Stdout.formatln("Gen: {} Avg: {:.4} Max: {:.4}",generation, average, max); | |
218 if (stop || generation >= bailout) return generation; | |
219 currGen = currGen.evolve(); | |
220 generation++; | |
221 } | |
222 } | |
223 } | |
224 | |
225 | |
226 |