dignea  1.0.0
Diverse Instance Generator with Novelty Search and Evolutionary Algorithms
NoveltySearch.h
1 //
2 // Created by amarrero on 18/6/21.
3 //
4 #ifndef DIGNEA_NOVELTYSEARCH_H
5 #define DIGNEA_NOVELTYSEARCH_H
6 
7 #include <dignea/distances/Distance.h>
10 #include <dignea/utilities/KNN.h>
12 #include <dignea/utilities/exceptions/EmptyPopulation.h>
13 #include <fmt/core.h>
14 
15 #include <iostream>
16 #include <nlohmann/json.hpp>
17 #include <vector>
18 
19 #include "Search.h"
20 
21 using namespace std;
22 using json = nlohmann::json;
23 
24 typedef vector<float> Descriptor;
34 template <typename F>
35 Descriptor castDescriptor(const std::vector<F> &from) {
36  Descriptor castedVector;
37  castedVector.reserve(from.size());
38  std::transform(from.begin(), from.end(), std::back_inserter(castedVector),
39  [](F value) { return static_cast<float>(value); });
40  return castedVector;
41 }
42 
49 template <typename S>
50 class NoveltySearch : public Search<S> {
51  public:
52  NoveltySearch() = default;
53 
54  explicit NoveltySearch(unique_ptr<Distance<float>> dist,
55  const float &threshold = 2000,
56  const float &finalThresh = 0.0001,
57  const int &k = 15);
58 
59  virtual ~NoveltySearch() = default;
60 
61  Front<S> getResults() override;
62 
63  vector<S> run(vector<S> &population, const Problem<S> *problem) override;
64 
65  virtual void cmpFinals(vector<S> &population,
66  const Problem<S> *problem = nullptr);
67 
68  virtual void insertIntoArchive(const S &solution);
69 
70  inline const vector<S> &getArchive() const { return this->noveltyArchive; }
71 
72  virtual json to_json();
73 
74  float getThreshold() const { return this->threshold; }
75 
76  float getK() const { return this->k; }
77 
78  float getFinalThresh() const { return this->finalSThreshold; }
79 
80  protected:
81  virtual vector<Descriptor> beforeRun(const vector<S> &population);
82 
83  virtual vector<Descriptor> beforeCmpFinals(const vector<S> &population);
84 
85  virtual void insertFinal(const S &solution);
86 
87  protected:
88  unique_ptr<Distance<float>> distance;
89  vector<S> noveltyArchive;
90  vector<S> finalSs;
91  vector<Descriptor> finalSsDesc;
92 
93  float threshold;
94  float finalSThreshold;
95  int k;
96 };
97 
106 template <typename S>
108  const float &threshold,
109  const float &finalThresh, const int &k)
110  : distance(move(dist)),
111  threshold(threshold),
112  finalSThreshold(finalThresh),
113  k(k) {}
114 
123 template <typename S>
124 vector<Descriptor> NoveltySearch<S>::beforeRun(const vector<S> &population) {
125  vector<Descriptor> combinedPop;
126  combinedPop.reserve(population.size() + this->noveltyArchive.size());
127  for (const S &solution : population) {
128  vector vars = solution.getVariables();
129  combinedPop.push_back(castDescriptor(vars));
130  }
131  for (const S &solution : noveltyArchive) {
132  vector vars = solution.getVariables();
133  combinedPop.push_back(castDescriptor(vars));
134  }
135  // combinedPop.insert(combinedPop.end(), this->noveltyArchive.begin(),
136  // this->noveltyArchive.end(), [](const S &s) {
137  // vector vars = s.getVariables();
138  // return castVector(vars);
139  // });
140  return combinedPop;
141 }
142 
156 template <typename S>
157 vector<S> NoveltySearch<S>::run(vector<S> &population,
158  const Problem<S> *problem) {
159  vector<Descriptor> combinedPopulation = beforeRun(population);
160  // vector<S> combinedPop = population;
161  // combinedPop.insert(combinedPop.end(), noveltyArchive.begin(),
162  // noveltyArchive.end());
163  vector spars = sparseness(combinedPopulation, this->distance.get(), k);
164  auto momea = false;
165  if (population[0].getNumberOfObjs() == 2) {
166  // Running with MOEIG, we set diversity as objective 2
167  momea = true;
168  }
169  for (unsigned int i = 0; i < population.size(); i++) {
170  population[i].setDiversity(spars[i]);
171  if (momea) {
172  population[i].setObjAt(1, spars[i]);
173  }
174  }
175  return population;
176  // vector spars = sparseness(combinedPop, distance.get(), k);
177  // for (unsigned int i = 0; i < population.size(); i++) {
178  // population[i].setDiversity(spars[i]);
179  // }
180  // return population;
181 }
182 
183 template <typename S>
184 vector<Descriptor> NoveltySearch<S>::beforeCmpFinals(
185  const vector<S> &population) {
186  vector<Descriptor> descriptors;
187  descriptors.reserve(population.size());
188  for (const S &solution : population) {
189  vector vars = solution.getVariables();
190  descriptors.push_back(castDescriptor(vars));
191  }
192  return descriptors;
193 }
194 
204 template <typename S>
205 void NoveltySearch<S>::cmpFinals(vector<S> &population,
206  const Problem<S> *problem) {
207  if (population.size() == 0) {
208  std::string where = "NoveltySearch::cmpFinals";
209  throw EmptyPopulation(where);
210  }
211 #ifdef DEBUG
212  std::cout << fmt::format("{:=^120}", " Including inds. final solution set ")
213  << std::endl;
214  std::cout << fmt::format("{:=^120}", "") << std::endl;
215  std::cout << fmt::format("{:^30}\t{:^30}\t{:^20}({})\t{:^20}", "Sparseness",
216  "Biased Fitness", "Greater than threshold",
217  this->finalSThreshold, "Archive len()")
218  << std::endl;
219  std::cout << fmt::format("{:=^120}", "") << std::endl;
220 #endif
221 
222  if (population[0].getNumberOfObjs() == 1) {
223  // Includes fittest feasible individual if the archive is empty
224  if (finalSs.empty()) {
225  sortByFitness(population);
226  for (S &sol : population) {
227  if (sol.getFitness() > 0.0) {
228  this->insertFinal(sol);
229  break;
230  }
231  }
232 
233  } else {
234  // Includes feasible individuals with spars > threshold
235  // Here we require the individual to be feasible in the performance;
236  // i.e., target algorithm performs better than the others
237  // and to have a diversity value greater than the predefined
238  // threshold
239  vector descriptors = beforeCmpFinals(population);
240  for (unsigned int i = 0; i < descriptors.size(); i++) {
241  float spars = sparseness(descriptors[i], this->finalSsDesc,
242  distance.get(), 1);
243  if ((population[i].getBiasedFitness() > 0.0) &&
244  (spars > this->finalSThreshold)) {
245  this->insertFinal(population[i]);
246  }
247  }
248 
249 #ifdef DEBUG
250  std::cout << fmt::format(
251  "{:>20}\t{:>30}\t{:>20}\t{:>20}", spars,
252  solution.getBiasedFitness(),
253  (spars > this->finalSThreshold ? "Yes" : "No"),
254  finalSs.size())
255  << std::endl;
256 
257 #endif
258  }
259  } else {
260  if (!problem) {
261  string where =
262  "problem is nullptr in NoveltySearch::cmpFinals. When using "
263  "MOEIG cmpFinals must receive the problem as well";
264  throw(std::runtime_error(where));
265  }
266  if (finalSs.empty()) {
267  sortByObj(population, 0, problem);
268  for (S &sol : population) {
269  if (sol.getObjAt(0) > 0.0) {
270  this->insertFinal(sol);
271  break;
272  }
273  }
274  } else {
275  // Includes feasible individuals with spars > threshold
276  // Here we require the individual to be feasible in the performance;
277  // We get the descriptors of all the instances at once
278  vector descriptors = beforeCmpFinals(population);
279  for (unsigned int i = 0; i < descriptors.size(); i++) {
280  float spars = sparseness(descriptors[i], this->finalSsDesc,
281  distance.get(), 1);
282  if ((population[i].getObjAt(0) > 0.0) &&
283  (spars > this->finalSThreshold)) {
284  this->insertFinal(population[i]);
285  }
286  }
287  }
288  }
289 }
290 
298 template <typename S>
300  return Front<S>(this->finalSs);
301 }
302 
311 template <typename S>
312 void NoveltySearch<S>::insertIntoArchive(const S &solution) {
313  this->noveltyArchive.push_back(solution);
314 }
315 
324 template <typename S>
325 void NoveltySearch<S>::insertFinal(const S &solution) {
326  this->finalSs.push_back(solution);
327  this->finalSsDesc.push_back(castDescriptor(solution.getVariables()));
328 }
329 
330 template <typename S>
332  json data;
333  data["name"] = "Novelty Search";
334  data["k"] = this->k;
335  data["threshold"] = this->threshold;
336  return data;
337 }
338 
339 #endif // DIGNEA_NOVELTYSEARCH_H
vector< float > sparseness(const vector< Descriptor > &population, Distance< float > *metric, const int k)
Computes the sparseness of all individuals in the population vector Based on the Novelty Search from ...
Definition: KNN.cpp:81
nlohmann::json json
Definition: MinKnap.h:85
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
void sortByFitness(vector< S > &population)
Sorts the population by fitness in descending order.
Definition: Sorter.h:69
Front class which stores the final results of an EA execution.
Definition: Front.h:26
Class to represent the Novelty Search Algorithm.
Definition: NoveltySearch.h:50
virtual vector< Descriptor > beforeRun(const vector< S > &population)
Performs computational work necessary for running the NS This method creates a combined population us...
Definition: NoveltySearch.h:124
Front< S > getResults() override
Returns the results obtained by the Novelty Search in a Front object.
Definition: NoveltySearch.h:299
virtual void cmpFinals(vector< S > &population, const Problem< S > *problem=nullptr)
Compares the individuals in the population against the neighbors inside the archive of final Ss....
Definition: NoveltySearch.h:205
virtual void insertFinal(const S &solution)
Method to insert a new individual into the final set of solutions The default behaviour is to get all...
Definition: NoveltySearch.h:325
vector< S > run(vector< S > &population, const Problem< S > *problem) override
Novelty Search Algorithm It looks for novelty using the genotypes of the individuals in the populatio...
Definition: NoveltySearch.h:157
virtual void insertIntoArchive(const S &solution)
Method to insert a new individual into the noveltyArchive of novelty solutions.
Definition: NoveltySearch.h:312
Class to represent a Problem in the tool. It includes the basic information for a problem a few metho...
Definition: Problem.h:29
Search algorithm interface for dignea. Use this class as the skeleton to define local or other types ...
Definition: Search.h:23