forked from jibsen/galib
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathex22.C
309 lines (247 loc) · 9.27 KB
/
ex22.C
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
/* ----------------------------------------------------------------------------
ex22.C
mbwall 5jan96
Copyright (c) 1995-1996 Massachusetts Institute of Technology
DESCRIPTION:
This example shows how to derive your own genetic algorithm class. This one
does a modified form of speciation that is useful for fitness-scaled speciation
with overlapping populations (Goldberg's speciation is designed for use with
non-overlapping populations.
The steady-state genetic algorithm built-in to GAlib is actually capable of
doing this already, but this example illustrates how you can modify a genetic
algorithm to do your own thing. For example, instead of using the "single
child crossover" you could use your own crossover algorithm instead.
---------------------------------------------------------------------------- */
#include <stdio.h>
#include <math.h>
#include <ga/ga.h>
#include <ga/std_stream.h>
#define cout STD_COUT
#define cerr STD_CERR
#define endl STD_ENDL
#define ofstream STD_OFSTREAM
// force instantiations for compilers that do not do auto instantiation
// for some compilers (e.g. metrowerks) this must come after any
// specializations or you will get 'multiply-defined errors when you compile.
#if !defined(GALIB_USE_AUTO_INST)
#include <ga/GA1DArrayGenome.C>
GALIB_INSTANTIATION_PREFIX GA1DArrayGenome<float>;
#endif
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#define OBJECTIVE Objective1
#define MIN_VALUE -100
#define MAX_VALUE 100
//#define OBJECTIVE Objective2
//#define MIN_VALUE -50
//#define MAX_VALUE 50
float Objective1(GAGenome&);
float Objective2(GAGenome&);
int Mutator(GAGenome&, float);
void Initializer(GAGenome&);
float Comparator(const GAGenome&, const GAGenome&);
int Crossover(const GAGenome&, const GAGenome&, GAGenome*);
// Here we define our own genetic algorithm class. This class is almost the
// same as the steady-state genetic algorithm, but we modify the step method
// (the one that does all the work) so that we do a slightly modified
// replacement. We're only going to do a two-parents-make-one-child mating,
// so we define our own crossover and use it rather than the standard one in
// GAlib.
typedef int (*SingleChildCrossover)(const GAGenome&,const GAGenome&,GAGenome*);
class SharedOverlapGA : public GASteadyStateGA {
public:
GADefineIdentity("SharedOverlapGA", 200);
SharedOverlapGA(const GAGenome& g) : GASteadyStateGA(g) {}
virtual ~SharedOverlapGA() {}
virtual void step();
SharedOverlapGA & operator++() { step(); return *this; }
void crossover(SingleChildCrossover func) {crossFunction = func;}
protected:
SingleChildCrossover crossFunction;
};
// This step method is similar to that of the regular steady-state genetic
// algorithm, but here we generate only one child in a crossover, and we
// do a slightly different type of replacement. Here we generate the new
// individuals, insert them into the population, force a scaling to occur,
// then remove the worst individuals. This is all done based on the scaled
// (fitness) scores, not the raw (objective) scores.
void
SharedOverlapGA::step()
{
int i;
GAGenome *mom, *dad;
for(i=0; i<tmpPop->size(); i++){ // takes care of odd population
mom = &(pop->select());
dad = &(pop->select());
stats.numsel += 2; // keep track of number of selections
if(GAFlipCoin(pCrossover()))
stats.numcro += crossFunction(*mom, *dad, &tmpPop->individual(i));
else if(GARandomBit())
tmpPop->individual(i).copy(*mom);
else
tmpPop->individual(i).copy(*dad);
stats.nummut += tmpPop->individual(i).mutate(pMutation());
}
for(i=0; i<tmpPop->size(); i++)
pop->add(tmpPop->individual(i));
pop->evaluate(); // get info about current pop for next time
pop->scale(); // remind the population to do its scaling
for(i=0; i<tmpPop->size(); i++)
pop->destroy(GAPopulation::WORST, GAPopulation::SCALED);
stats.update(*pop); // update the statistics by one generation
}
int
main(int argc, char** argv)
{
cout << "Example 22\n\n";
cout << "This example shows how to derive your own genetic algorithm\n";
cout << "class. Here we use a custom, single-child crossover and a\n";
cout << "modified replacement strategy with overlapping populations.\n\n";
cout.flush();
// See if we've been given a seed to use (for testing purposes). When you
// specify a random seed, the evolution will be exactly the same each time
// you use that seed number.
unsigned int seed = 0;
for(int ii=1; ii<argc; ii++) {
if(strcmp(argv[ii++],"seed") == 0) {
seed = atoi(argv[ii]);
}
}
ofstream outfile;
char file[] = "sinusoid.dat";
char ifile[] = "pop.initial.dat";
char ffile[] = "pop.final.dat";
int i;
GA1DArrayGenome<float> genome(1, OBJECTIVE);
genome.initializer(::Initializer);
genome.mutator(::Mutator);
genome.comparator(::Comparator);
GASharing share(Comparator);
SharedOverlapGA ga(genome);
ga.crossover(Crossover);
ga.scaling(share);
ga.populationSize(100);
ga.pReplacement(0.25);
ga.nGenerations(500);
ga.pMutation(0.01);
ga.pCrossover(1.0);
ga.scoreFilename("bog.dat"); // name of file for scores
ga.scoreFrequency(10); // keep the scores of every 10th generation
ga.flushFrequency(100); // specify how often to write the score to disk
ga.selectScores(GAStatistics::AllScores);
ga.parameters(argc, argv, gaTrue); // parse commands, complain if bogus args
cout << "initializing...\n"; cout.flush();
ga.initialize(seed);
// dump the initial population to file
outfile.open(ifile, (STD_IOS_OUT | STD_IOS_TRUNC));
for(i=0; i<ga.population().size(); i++){
genome = ga.population().individual(i);
outfile << genome.gene(0) << "\t" << genome.score() << "\n";
}
outfile.close();
// Evolve until the termination function says we're finished. Print out a
// little status indicator periodically to let us know what's going on. After
// the evolution we flush any remaining scores to file.
cout << "evolving"; cout.flush();
while(!ga.done()){
ga.step();
if(ga.generation() % 50 == 0){
cout << ".";
cout.flush();
}
}
cout << "\n\n";
ga.flushScores();
// dump the final population to file
outfile.open(ffile, (STD_IOS_OUT | STD_IOS_TRUNC));
for(i=0; i<ga.population().size(); i++){
genome = ga.population().individual(i);
outfile << genome.gene(0) << "\t" << genome.score() << "\n";
}
outfile.close();
// dump the function to file
cout << "dumping the function to file..." << endl;
outfile.open(file, (STD_IOS_OUT | STD_IOS_TRUNC));
if(outfile.fail()){
cerr << "Cannot open " << file << " for output.\n";
exit(1);
}
for(float x=MIN_VALUE; x<=MAX_VALUE; x+=1.0)
outfile << genome.gene(0,x) << "\t" << genome.score() << "\n";
outfile << "\n";
outfile.close();
cout << "initial population is in '" << ifile << "'\n";
cout << "final population is in '" << ffile << "'\n";
cout << "the function is in '" << file << "'\n";
cout << "parameters were:\n\n" << ga.parameters() << "\n";
return 0;
}
// Here are two different objective functions. Function 1 has multiple peaks
// with significant difference between peak heights - it is a modulated
// sinusoid. Function 2 has less difference between peaks - it is an
// approximation of a square plateau using a sum of sinusoids.
float
Objective1(GAGenome& g)
{
GA1DArrayGenome<float>& genome = (GA1DArrayGenome<float>&)g;
float v = genome.gene(0);
float y = 100.0 * exp(-fabs(v) / 50.0) * (1.0 - cos(v * M_PI * 2.0 / 25.0));
if(v < MIN_VALUE || v > MAX_VALUE) y = 0;
if(y < 0) y = 0;
return y+0.00001;
}
float
Objective2(GAGenome& g)
{
GA1DArrayGenome<float>& genome = (GA1DArrayGenome<float>&)g;
float v = genome.gene(0) / 100.0;
float y = 0.5 + 0.6 * sin(M_PI*v) + 0.2 * sin(3*M_PI*v) + 0.1 * sin(5*M_PI*v)
+ 0.02 * sin(7*M_PI*v) + 0.01 * sin(7*M_PI*v);
if(v < -0.23 || v > 1.23) y = 0;
if(y < 0) y = 0;
return y+0.00001;
}
void
Initializer(GAGenome& g)
{
GA1DArrayGenome<float>& genome = (GA1DArrayGenome<float>&)g;
genome.gene(0, GARandomFloat(-100.0, 100.0));
}
int
Mutator(GAGenome& g, float pmut)
{
GA1DArrayGenome<float>& genome = (GA1DArrayGenome<float>&)g;
int nmut = 0;
if(GAFlipCoin(pmut)){
genome.gene(0, genome.gene(0) +
GARandomFloat() * (GARandomFloat() - GARandomFloat()));
nmut = 1;
}
return nmut;
}
int
Crossover(const GAGenome& g1, const GAGenome& g2, GAGenome* c1)
{
GA1DArrayGenome<float>& mom = (GA1DArrayGenome<float>&)g1;
GA1DArrayGenome<float>& dad = (GA1DArrayGenome<float>&)g2;
GA1DArrayGenome<float>& child = (GA1DArrayGenome<float>&)*c1;
float distance = 0.0, midpoint = 0.0;
midpoint = (mom.gene(0) + dad.gene(0)) / 2;
distance = fabs(mom.gene(0) - dad.gene(0));
child.gene(0, midpoint + distance * (GARandomFloat() - GARandomFloat()));
return 1;
}
// You can change the factor to control how tightly the distance function
// considers the spacing of two genomes. Higher numbers will give you a
// tighter clustering at function peaks.
#define FACTOR 800
float
Comparator(const GAGenome& g1, const GAGenome& g2)
{
GA1DArrayGenome<float>& a = (GA1DArrayGenome<float>&)g1;
GA1DArrayGenome<float>& b = (GA1DArrayGenome<float>&)g2;
float val= exp( - (a.gene(0)-b.gene(0)) * (a.gene(0)-b.gene(0)) / FACTOR);
if(1-val < 0 || 1-val > 1) cerr << "val: " << val << "\n";
return 1-val;
}