dignea  1.0.0
Diverse Instance Generator with Novelty Search and Evolutionary Algorithms
Sorter.h
Go to the documentation of this file.
1 
11 #ifndef DIGNEA_SORTBYOBJ_H
12 #define DIGNEA_SORTBYOBJ_H
13 
14 #include <dignea/core/Problem.h>
16 #include <dignea/utilities/exceptions/EmptyPopulation.h>
17 #include <dignea/utilities/exceptions/OutOfRange.h>
18 
19 #include <algorithm>
20 #include <iostream>
21 #include <ranges>
22 
23 using namespace std;
24 
35 template <typename S>
36 void sortByObj(vector<S> &population, const int &objNumber,
37  const Problem<S> *problem) {
38  if (population.empty()) {
39  std::string where = "sortByObj with obj = " + to_string(objNumber);
40  throw(EmptyPopulation(where));
41  }
42  if (objNumber >= population[0].getNumberOfObjs()) {
43  std::string where =
44  "sortByObj obj = " + to_string(objNumber) + " but only " +
45  to_string(population[0].getNumberOfObjs()) + " available";
46  throw(OutOfRange(where));
47  }
48  // Funcion lambda que vamos a emplear para ordenar la poblacion
49  // Recibe dos individuos como parametros y devuelve un booleano
50  // Tiene en cuenta el tipo de objetivo que estamos considerando: Min or Max.
51  // Ademas capturamos el contexto para poder acceder a objectiveNumber
52  auto comparator = [&](S &firstInd, S &secondInd) -> bool {
53  return (problem->getOptimizationDirection(objNumber) == Minimize)
54  ? (firstInd.getObjAt(objNumber) <
55  secondInd.getObjAt(objNumber))
56  : (firstInd.getObjAt(objNumber) >
57  secondInd.getObjAt(objNumber));
58  };
59  sort(population.begin(), population.end(), comparator);
60 }
61 
68 template <typename S>
69 void sortByFitness(vector<S> &population) {
70  if (population.empty()) {
71  std::string where = "sortByFitness";
72  throw(EmptyPopulation(where));
73  }
74  auto comparison = [&](S &firstInd, S &secondInd) -> bool {
75  return firstInd.getFitness() > secondInd.getFitness();
76  };
77  sort(population.begin(), population.end(), comparison);
78 }
79 
86 template <typename S>
87 void sortByCrowDistance(vector<S> &population) {
88  if (population.empty()) {
89  std::string where = "sortByFitness";
90  throw(EmptyPopulation(where));
91  }
92 
93  stable_sort(population.begin(), population.end(),
94  [](const S &firstInd, const S &secondInd) {
95  return firstInd.getCrowDistance() >
96  secondInd.getCrowDistance();
97  });
98 }
99 
107 template <typename S>
108 void crowOrder(vector<S> &population, const Problem<S> *problem) {
109  if (population.empty()) {
110  std::string where = "crowOrder";
111  throw(EmptyPopulation(where));
112  }
113  if (population[0].getNumberOfObjs() < 2) {
114  std::string where = "crowOrder less than two objectives ";
115  throw(OutOfRange(where));
116  }
117  if (population.size() == 1) {
118  return;
119  }
120  double difference = 0;
121  // Reset the distance before computing the crowding distance
122  for (S &solution : population) {
123  solution.setCrowDistance(0.0);
124  }
125  // We calculate the distance for each objective
126  for (int i = 0; i < problem->getNumberOfObjs(); i++) {
127  sortByObj(population, i, problem);
128  auto size = population.size() - 1;
129  population[0].setCrowDistance(std::numeric_limits<float>::max());
130  population[size].setCrowDistance(std::numeric_limits<float>::max());
131 
132  // Maximum and minimum values of the mth objective function
133  // This represents fmaxm fminm from the paper
134  difference = population[size].getObjAt(i) - population[0].getObjAt(i);
135  if (difference == 0) {
136  difference = 1; // For safe division
137  }
138  for (unsigned j = 1; j < size; j++) {
139  population[j].setCrowDistance(((population[j + 1].getObjAt(i) -
140  population[j - 1].getObjAt(i)) /
141  difference) +
142  population[j].getCrowDistance());
143  }
144  }
145  sortByCrowDistance(population);
146 }
147 
157 template <class S>
158 vector<vector<S>> fastNonDominatedSort(vector<S> &population,
159  const Problem<S> *problem) {
160  auto popSize = population.size();
161  map<int, vector<int>> idxFront;
162  map<int, vector<int>> dominates;
163  map<int, int> dominatedBy;
164  // Initialize the maps
165  for (unsigned i = 0; i < popSize; i++) {
166  dominates.insert({i, vector<int>()});
167  dominatedBy.insert({i, 0});
168  population[i].setRank(-1);
169  }
170 
171  int classified = 0;
172  // Calculates Front # 1
173  idxFront.insert({0, vector<int>()});
174  for (unsigned p = 0; p < popSize; p++) {
175  for (unsigned q = p + 1; q < popSize; q++) {
176  auto dominated =
177  dominanceTest(population[p], population[q], problem);
178  if (dominated == FIRST_DOMINATES) {
179  dominates[p].push_back(q);
180  dominatedBy[q]++;
181  } else if (dominated == SECOND_DOMINATES) {
182  dominates[q].push_back(p);
183  dominatedBy[p]++;
184  }
185  }
186  if (dominatedBy[p] == 0) {
187  population[p].setRank(0);
188  idxFront[0].push_back(p);
189  classified++;
190  }
191  }
192  int index = 0;
193  int rank = 1;
194  // Now we loop the idxFront
195  while (idxFront[index].size() != 0) {
196  vector<int> qIdxFront;
197  for (unsigned p = 0; p < idxFront[index].size(); p++) {
198  for (unsigned q = 0; q < dominates[p].size(); q++) {
199  dominatedBy[dominates[p][q]]--;
200  if (dominatedBy[dominates[p][q]] == 0) {
201  population[dominates[p][q]].setRank(rank);
202  qIdxFront.push_back(dominates[p][q]);
203  classified++;
204  }
205  }
206  }
207  index++;
208  rank++;
209  idxFront.insert({index, qIdxFront});
210  }
211  if (classified < (int)popSize) {
212  idxFront.insert({index, vector<int>()});
213  for (unsigned i = 0; i < popSize; i++) {
214  if (population[i].getRank() == -1) {
215  population[i].setRank(rank);
216  idxFront[index].push_back(i);
217  classified++;
218  }
219  }
220  }
221  // Just in case the last front was empty
222  if (idxFront[index].empty()) {
223  idxFront.erase(idxFront.find(index));
224  }
225  vector<vector<S>> fronts;
226  for (auto const &[key, value] : idxFront) {
227  vector<S> front;
228  std::for_each(value.cbegin(), value.cend(),
229  [&](const int &i) { front.push_back(population[i]); });
230 
231  fronts.push_back(front);
232  }
233  return fronts;
234 }
235 
236 #endif // DIGNEA_SORTBYOBJ_H
int dominanceTest(const S &ind1, const S &ind2, const Problem< S > *problem)
Test whether an individual dominates another. A individual dominates another if all their objectives ...
Definition: Comparator.h:140
void sortByCrowDistance(vector< S > &population)
Sorts the population by their crow distance value in descending order.
Definition: Sorter.h:87
void sortByObj(vector< S > &population, const int &objNumber, const Problem< S > *problem)
Sorts the population of individuals by the given objective according to the problem....
Definition: Sorter.h:36
vector< vector< S > > fastNonDominatedSort(vector< S > &population, const Problem< S > *problem)
Fast Non Dominated Sorting operator. Sorts the population in different fronts Used in NSGA-II.
Definition: Sorter.h:158
void sortByFitness(vector< S > &population)
Sorts the population by fitness in descending order.
Definition: Sorter.h:69
void crowOrder(vector< S > &population, const Problem< S > *problem)
Definition: Sorter.h:108
Class to represent a Problem in the tool. It includes the basic information for a problem a few metho...
Definition: Problem.h:29
int getNumberOfObjs() const
Get the number of objectives of the problem.
Definition: Problem.h:135
virtual int getOptimizationDirection(const int i) const =0
Returns the optimization direction for each objective in the problem. It returns Minimize or Maximize...