ECF 1.5
AlgNSGA2.cpp
1#include "ECF_base.h"
2#include "AlgNSGA2.h"
3#include "SelRandomOp.h"
4#include "SelWorstOp.h"
5#include <cmath>
6#include <cfloat>
7
8
9AlgNSGA2::AlgNSGA2()
10{
11 name_ = "NSGA2";
12 // create selection operators needed
13 // in this case, SelRandomOp and SelWorstOp
14 selRandomOp = static_cast<SelectionOperatorP> (new SelRandomOp);
15
16 selWorstOp = static_cast<SelectionOperatorP> (new SelWorstOp);
17 this->parentPop = new std::vector <IndividualP>();
18 this->fronts = boost::shared_ptr<std::vector <std::vector <IndividualP> > > (new std::vector <std::vector <IndividualP> >());
19
20}
21
22
23bool AlgNSGA2::initialize(StateP state)
24{
25 // initialize all operators
26 selRandomOp->initialize(state);
27 selWorstOp->initialize(state);
28
29 return true;
30}
31
32
33void AlgNSGA2::quickSort(std::vector <IndividualP> *group, int left, int right, std::string prop, int objective = -1)
34{
35 int i = left, j = right;
36
37 IndividualP tmp;
38 MOFitnessP pivotMO = boost::static_pointer_cast<MOFitness> (group->at((left + right) / 2)->fitness);
39 double pivot = pivotMO->getProperty(prop, objective);
40
41 /* partition */
42 while (i <= j) {
43 MOFitnessP moFitnessI = boost::static_pointer_cast<MOFitness> (group->at(i)->fitness);
44 while (moFitnessI->getProperty(prop, objective) < pivot) {
45 i++;
46 moFitnessI = boost::static_pointer_cast<MOFitness> (group->at(i)->fitness);
47 }
48 MOFitnessP moFitnessJ = boost::static_pointer_cast<MOFitness> (group->at(j)->fitness);
49 while (pivot < moFitnessJ->getProperty(prop, objective)) {
50 j--;
51 moFitnessJ = boost::static_pointer_cast<MOFitness> (group->at(j)->fitness);
52 }
53
54 if (i <= j) {
55 tmp = group->at(i);
56 group->at(i) = group->at(j);
57 group->at(j) = tmp;
58 i++;
59 j--;
60 }
61 };
62
63 /* recursion */
64 if (left < j)
65 quickSort(group, left, j, prop, objective);
66 if (i < right)
67 quickSort(group, i, right, prop, objective);
68}
69
70
71void AlgNSGA2::sortBasedOnProperty(std::vector <IndividualP>* deme, double* fMin, double* fMax, std::string prop, int objective = -1)
72{
73 int left = 0;
74 int right = deme->size()-1;
75 quickSort(deme, left, right, prop, objective);
76
77 MOFitnessP fitness = boost::static_pointer_cast<MOFitness> (deme->at(left)->fitness);
78 *fMin = fitness->getProperty(prop, objective);
79 fitness = boost::static_pointer_cast<MOFitness> (deme->at(right)->fitness);
80 *fMax = fitness->getProperty(prop, objective);
81}
82
83
84int AlgNSGA2::checkDominance(MOFitnessP fitness1, MOFitnessP fitness2)
85{
86 double eps = 1E-9;
87 uint size = fitness1->size();
88 if (size != fitness2->size()) {
89 // ne smije se dogoditi
90 }
91 int dominance = 0;
92 for (uint i = 0; i<size; i++) {
93 FitnessP f1 = fitness1->at(i);
94 FitnessP f2 = fitness2->at(i);
95 if (fabs(f1->getValue() - f2->getValue()) > DBL_EPSILON) {
96 if (dominance == 0) {
97 if (f1->isBetterThan(f2)) {
98 dominance = -1;
99 } else {
100 dominance = 1;
101 }
102 } else if (dominance == -1) {
103 if (f1->isBetterThan(f2)) {
104 // dominance ostaje -1
105 } else {
106 return 0;
107 }
108 } else {
109 if (f1->isBetterThan(f2)) {
110 return 0;
111 } else {
112 // dominance ostaje 1
113 }
114 }
115 }
116 }
117 return dominance;
118}
119
120
121// O(M * N^2),
122// M => broj funkcija ciljeva
123// N velicina populacije za sortiranje
124void AlgNSGA2::nonDomSorting(boost::shared_ptr<std::vector <IndividualP> > pool, int N, boost::shared_ptr<std::vector <std::vector <IndividualP> > > fronts)
125{
126 fronts->clear();
127 std::vector <IndividualP> Q = *(new std::vector <IndividualP>());
128 int collectedSoFar = 0;
129 int p = 1; // najnizi (najbolji) rank
130
131 // izracunati nc i Sp za svaku jedinku u populaciji
132 // i mora ici skroz do kraja (a ne do pool->size()-1) jer
133 // u slucaju da je cijela populacija u prvoj fronti, mora se i zadnji ubaciti u prvu frontu
134 for (uint i = 0; i < pool->size(); i++) {
135 IndividualP ind = pool->at(i);
136 MOFitnessP fitnessI = boost::static_pointer_cast<MOFitness> (ind->fitness);
137
138 if (i == 0) {
139 fitnessI->nc = 0;
140 fitnessI->Sp = new std::vector<IndividualP>();
141 fitnessI->rank = 0;
142 }
143
144 for (uint j = i+1; j < pool->size(); j++) {
145 IndividualP other = pool->at(j);
146 MOFitnessP fitnessJ = boost::static_pointer_cast<MOFitness> (other->fitness);
147
148 if (i == 0) {
149 fitnessJ->nc = 0;
150 fitnessJ->Sp = new std::vector<IndividualP>();
151 fitnessJ->rank = 0;
152 }
153
154 int dominance = checkDominance(fitnessI, fitnessJ);
155 if (dominance == -1) {
156 fitnessI->Sp->push_back(other);
157 fitnessJ->nc++;
158 } else if (dominance == 1) {
159 fitnessI->nc++;
160 fitnessJ->Sp->push_back(ind);
161 }
162 }
163
164 // inicijalno u skup Q ulaze pareto optimalna rjesenja
165 // sva rjesenja koja imaju 'domination count' (nc) jednak 0
166 if (fitnessI->nc == 0) {
167 fitnessI->rank = p;
168 Q.push_back(ind);
169 collectedSoFar++;
170 }
171 }
172
173
174 // odrediti rankove rjesenjima
175 //while (Q.size() != 0 && collectedSoFar < N) {
176 while (Q.size() != 0) {
177 fronts->push_back(Q);
178
179
180 p++;
181 // ako je skup Q prazan, zanci da smo svim rjesenjima dodjelili rank
182 // u skupu newQ skupljaju se rjesenja iduce fronte nedominacije
183 // znaci samo one rjesenja koja su dominirana iskljucivo rjesenjima iz skupa Q
184 std::vector <IndividualP> newQ;
185 for (uint i=0; i<Q.size(); i++) {
186 //pool->push_back(Q.at(i));
187 MOFitnessP fitnessI = boost::static_pointer_cast<MOFitness> (Q.at(i)->fitness);
188
189 for (uint j=0; j<fitnessI->Sp->size(); j++) {
190 // za svako rjesenje prolazimo po skupu rjesenja nad kojima ono dominira te azuriramo nc
191 IndividualP dominated = fitnessI->Sp->at(j);
192 MOFitnessP fitnessJ = boost::static_pointer_cast<MOFitness> (dominated->fitness);
193 fitnessJ->nc--;
194 if (fitnessJ->nc == 0) {
195 fitnessJ->rank = p;
196 newQ.push_back(dominated);
197 collectedSoFar++;
198 }
199 }
200 }
201 Q = newQ;
202 }
203
204}
205
206
207// O(M * N * logN)
208void AlgNSGA2::crowdedDistanceEst(StateP state, std::vector <IndividualP> *deme)
209{
210 MOFitnessP fitness = boost::static_pointer_cast<MOFitness> (deme->at(0)->fitness);
211 uint objCount = fitness->size();
212 for (uint i = 0; i<objCount; i++) {
213
214 double fMin;
215 double fMax;
216
217 // sortiramo cijelu populaciju rjesenja prema jednom cilju 'i'
218 // populacije je sortirana tako da rezultati funkcije cilja 'i' budu poredani od najmanjeg prema najvecem,
219 // bez obzira da li se u funkciji cilja 'i' radi o maksimizaciji ili minimizaciji
220 sortBasedOnProperty(deme, &fMin, &fMax, "objective", i);
221
222 // sad su rjesenja sortirana, na nultom indeksu nalazi se najbolje
223 // na posljednjem nalazi se najlošije
224 for (uint j = 0; j<deme->size(); j++) {
225 fitness = boost::static_pointer_cast<MOFitness> (deme->at(j)->fitness);
226
227
228 if (i == 0) {
229 // ako je ovo prva funkcija cilja koju razmatramo
230 // inicijaliziramo udaljenost na nulu, pocinjemo racunati crowding_distance za rjesenje 'j'
231 fitness->crowding_distance = 0;
232 }
233
234 double increment;
235
236 if (j == 0 || j == deme->size()-1) {
237 // ako je najbolje ili najlosije rjesenje, postaviti beskonacnu vrijednost u njega
238 // zapravo ne stavljam beskonacno nego maksimalnu vrijednost (fMax - fMin)
239 increment = fMax - fMin;
240
241 } else {
242 // ako se ne radi o najboljem ili najlosijem
243 // tada je crowding distance odreden napucenoscu prostora rjesenja
244
245 // sljedbenik (j+1) ima veci fitness od (j) s obzirom na funkciju cilja 'i'
246 MOFitnessP fitnessNeighbour = boost::static_pointer_cast<MOFitness> (deme->at(j+1)->fitness);
247 increment = fitnessNeighbour->getValueOfObjective(i);
248
249 // prethodnik (j-1) ima manji fitness od (j) s obzirom na funkciju cilja 'i'
250 fitnessNeighbour = boost::static_pointer_cast<MOFitness> (deme->at(j-1)->fitness);
251 increment -= fitnessNeighbour->getValueOfObjective(i);
252
253
254 }
255 // normalizirati udaljenost
256 increment /= fMax - fMin;
257 fitness->crowding_distance += increment;
258
259
260
261 }
262
263 }
264
265}
266
267
268// obavlja selekciju, krizanje, mutacije te stvara novu populaciju iste velicine
269// trenutno je steadyStateTournament sa turnirom velicine 3
270void AlgNSGA2::makeNewPop(StateP state, DemeP deme)
271{
272 for(uint iIter = 0; iIter < deme->size(); iIter++) {
273
274 ECF_LOG(state, 5, "Individuals in tournament: ");
275
276 std::vector<IndividualP> tournament = *(new std::vector<IndividualP>());
277 for (uint i = 0; i < 2; ++i) {
278 // select a random individual for the tournament
279 tournament.push_back(selRandomOp->select(*deme));
280 ECF_LOG(state, 5, uint2str(i) + ": " + tournament[i]->toString());
281 }
282
283 // select the worst from the tournament
284 IndividualP worst = selWorstOp->select(tournament);
285 ECF_LOG(state, 5, "The worst from the tournament: " + worst->toString());
286
287 // remove pointer to 'worst' individual from vector 'tournament'
288 removeFrom(worst, tournament);
289
290 IndividualP myMate = selRandomOp->select(*deme);
291
292 // crossover the first two (remaining) individuals in the tournament
293 mate(tournament[0], myMate, worst);
294
295 // perform mutation on new individual
296 mutate(worst);
297
298 // create new fitness
299 evaluate(worst);
300 ECF_LOG(state, 5, "New individual: " + worst->toString());
301 }
302}
303
304
305bool AlgNSGA2::advanceGeneration(StateP state, DemeP deme)
306{
307 static bool firstGen = true;
308
309 // ne prvi put
310 if(!firstGen)
311 this->makeNewPop(state, deme);
312 firstGen = false;
313
314 uint N = deme->size();
315 bool initialGeneration = parentPop->size() == 0;
316 for (uint id = 0; id<parentPop->size(); id++) {
317 deme->push_back((IndividualP) parentPop->at(id)->copy());
318 }
319
320 fronts->clear();
321 //int lastFront;
322 //nonDomSorting(deme, N, fronts, &lastFront);
323 nonDomSorting(deme, N, fronts);
324 //crowdedDistanceEst(state, deme, lastFront);
325 deme->clear();
326
327 uint i = 0;
328 uint size = 0;
329 while (size + fronts->at(i).size() <= N) {
330 crowdedDistanceEst(state, &(fronts->at(i)));
331 for (uint j = 0; j<fronts->at(i).size(); j++) {
332 deme->push_back(fronts->at(i).at(j));
333 }
334 size += fronts->at(i).size();
335 i++;
336 }
337
338 if (!initialGeneration) {
339 double fMin;
340 double fMax;
341 crowdedDistanceEst(state, &(fronts->at(i)));
342 sortBasedOnProperty(&(fronts->at(i)), &fMin, &fMax, "crowding_distance");
343
344 int howMany = N - deme->size();
345 int lastFrontSize = fronts->at(i).size();
346 for (int j = lastFrontSize-1; j >= (lastFrontSize - howMany); j--) {
347 deme->push_back(fronts->at(i).at(j));
348 }
349 }
350 // deme sad ponovo ima N jedinki i mozemo krenuti sa procesom selekcije, krizanja i mutacija
351
352/* uint genNo = state->getGenerationNo();
353 if (genNo == 499) {
354
355 // ispisi populaciju u log file
356 XMLNode xDeme;
357 deme->write(xDeme);
358 char *output = xDeme.createXMLString(true);
359 ECF_LOG(state, 1, "\nPareto Solutions: \n" + std::string(output));
360
361 // ispisati populaciju u zaseban file...
362 std::ofstream myfile;
363 myfile.open ("paretoFront.txt");
364 for (uint i = 0; i<deme->size(); i++) {
365 FloatingPointP gen = boost::static_pointer_cast<FloatingPoint::FloatingPoint> (deme->at(i)->getGenotype(0));
366 //MOFitnessP fitness = boost::static_pointer_cast<MOFitness> (deme->at(i)->fitness);
367 myfile << gen->realValue[0] << " " << gen->realValue[1] << "\n";
368 }
369 myfile.close();
370
371 }
372*/
373
374 // trenutnu populaciju spremimo u roditeljsku populaciju
375 parentPop->clear();
376 for (uint i = 0; i<deme->size(); i++) {
377 this->parentPop->push_back((IndividualP) deme->at(i)->copy());
378 }
379
380 // premjestio na pocetak generacije
381 //this->makeNewPop(state, deme);
382
383
384 return true;
385}
bool advanceGeneration(StateP state, DemeP deme)
Perform a single generation on a single deme.
Definition: AlgNSGA2.cpp:305
bool initialize(StateP state)
Initialize the algorithm, read parameters from the system, do a sanity check.
Definition: AlgNSGA2.cpp:23
uint mutate(const std::vector< IndividualP > &pool)
Helper function: send a vector of individuals to mutation.
Definition: Algorithm.h:169
std::string name_
algorithm name
Definition: Algorithm.h:23
bool mate(IndividualP p1, IndividualP p2, IndividualP child)
Helper function: crossover two individuals.
Definition: Algorithm.h:285
bool removeFrom(IndividualP victim, std::vector< IndividualP > &pool)
Helper function: remove victim from pool of individual pointers.
Definition: Algorithm.h:297
void evaluate(IndividualP ind)
Helper function: evaluate an individual.
Definition: Algorithm.h:157
Random individual selection operator.
Definition: SelRandomOp.h:12
Worst individual selection operator.
Definition: SelWorstOp.h:11