dignea  1.0.0
Diverse Instance Generator with Novelty Search and Evolutionary Algorithms
EIG.h
Go to the documentation of this file.
1 
12 #ifndef DIGNEA_EIG_H
13 #define DIGNEA_EIG_H
14 
15 #include <dignea/core/AbstractEA.h>
16 #include <dignea/core/Crossover.h>
17 #include <dignea/core/Mutation.h>
18 #include <dignea/core/Replacement.h>
19 #include <dignea/core/Selection.h>
21 #include <dignea/distances/Euclidean.h>
23 #include <dignea/generator/evaluations/EasyInstances.h>
24 #include <dignea/generator/evaluations/InstanceFitness.h>
25 #include <dignea/generator/evaluations/Weighted.h>
26 #include <dignea/metrics/AverageFitness.h>
27 #include <dignea/metrics/BestFitness.h>
30 #include <dignea/searches/NSFeatures.h>
31 #include <dignea/searches/NoveltySearch.h>
33 #include <dignea/utilities/exceptions/Mismatch.h>
34 #include <dignea/utilities/random/PseudoRandom.h>
35 
36 #include <numeric>
37 #include <vector>
38 
39 using namespace std;
40 
54 template <typename IP, typename IS, typename OP, typename OS>
55 class EIG : public AbstractEA<IS> {
56  public:
57  using Alg = AbstractEA<OS>;
67  EIG(const float &fitness = 0.6f, const float &novelty = 0.4f);
68 
69  virtual ~EIG() = default;
70 
71  void run() override;
72 
73  [[nodiscard]] json to_json() const override;
74 
77  [[nodiscard]] string getName() const override { return "EIG"; };
78 
80  [[nodiscard]] string getID() const override { return getName(); };
81 
82  virtual Front<IS> getResults() const override;
83 
86  const IP *getInstanceProblem() const { return instProb.get(); }
87 
90  void setInstanceProblem(unique_ptr<IP> problem);
91 
94  vector<Alg *> getPortfolio() const;
95 
104  void setPortfolio(vector<unique_ptr<Alg>> &configs);
105 
112  [[nodiscard]] float getMutationRate() const { return mutationRate; }
113 
117  void setMutationRate(float mRate) { this->mutationRate = mRate; }
118 
125  [[nodiscard]] float getCrossoverRate() const { return crossoverRate; }
126 
130  void setCrossoverRate(float cRate) { this->crossoverRate = cRate; }
131 
138  [[nodiscard]] int getRepetitions() const { return repetitions; }
139 
146  void setRepetitions(int reps) { this->repetitions = reps; }
147 
150  Mutation<IS> *getMutation() const { return mutation.get(); }
151 
154  void setMutation(unique_ptr<Mutation<IS>> mut) {
155  this->mutation = move(mut);
156  }
159  Crossover<IS> *getCrossover() const { return crossover.get(); }
160 
163  void setCrossover(unique_ptr<Crossover<IS>> cx) {
164  this->crossover = move(cx);
165  }
168  Selection<IS> *getSelection() const { return selection.get(); }
169 
172  void setSelection(unique_ptr<Selection<IS>> sel) {
173  this->selection = move(sel);
174  }
175 
178  void setReplacement(unique_ptr<Replacement<IS>> rep) {
179  this->repOperator = move(rep);
180  }
181 
187  [[nodiscard]] InstanceFitness *getEvaluation() const {
188  return instanceFitness.get();
189  }
192  void setInstanceFitness(unique_ptr<InstanceFitness> eval) {
193  this->instanceFitness = move(eval);
194  }
195 
198  inline int getGenerations() { return this->getMaxEvaluations(); };
199 
203  return noveltySearch.get();
204  }
207  void setNoveltySearch(unique_ptr<NoveltySearch<IS>> noveltySearch) {
208  this->noveltySearch = move(noveltySearch);
209  }
210 
214  void setWeightedFunction(const float &fW, const float &nW) {
215  this->weightedFitness = make_unique<Weighted<IS>>(fW, nW);
216  }
217 
218  protected:
219  void initProgress() override;
220 
221  void updateProgress() override;
222 
223  void finishProgress() override;
224 
225  bool isStoppingConditionReached() override;
226 
227  void createInitialPopulation() override;
228 
229  void evaluatePopulation(vector<IS> &individuals) override;
230 
231  [[maybe_unused]] void evaluateIndividual(IS &individual);
232 
233  virtual void evaluationPhase(vector<IS> &individuals);
234 
235  virtual void reproduction(IS &solution, IS &solution2);
236 
237  virtual void replacement(vector<IS> &individuals);
238 
239  virtual void updateEvolution(vector<IS> &solutions);
240 
241  protected:
242  unique_ptr<IP> instProb;
243  vector<unique_ptr<Alg>>
246  unique_ptr<Mutation<IS>> mutation;
247  unique_ptr<Crossover<IS>> crossover;
248  unique_ptr<Selection<IS>> selection;
249  unique_ptr<Replacement<IS>> repOperator;
250 
251  float mutationRate;
255  unique_ptr<NoveltySearch<IS>> noveltySearch;
256  unique_ptr<InstanceFitness> instanceFitness; // Evaluation formulation
257  unique_ptr<Weighted<IS>> weightedFitness;
258 
259  AverageFitness<OS> averageFitness;
261  BestFitness<OS>
264 };
265 
273 template <typename IP, typename IS, typename OP, typename OS>
274 EIG<IP, IS, OP, OS>::EIG(const float &fitness, const float &novelty)
275  : AbstractEA<IS>(),
276  mutationRate(0.05),
277  crossoverRate(0.8),
278  repetitions(10),
279  averageFitness(),
280  bestFitness() {
281  // Easy instances by default
282  instanceFitness = make_unique<EasyInstances>();
283  weightedFitness = make_unique<Weighted<IS>>(fitness, novelty);
284  noveltySearch = make_unique<NSFeatures<IS>>(make_unique<Euclidean<float>>(),
285  this->maxEvaluations, 3.0, 15);
286  this->evolutionInterval = 1;
287 }
288 
297 template <typename IP, typename IS, typename OP, typename OS>
299  if (instProb) {
300 #ifdef DEBUG
301  std::cout << "Running EIG" << std::endl;
302 #endif
303  // Defines FirstImprove replacement if other not set
304  if (!repOperator) {
305  this->repOperator = make_unique<FirstImprove<IS>>();
306  }
307  createInitialPopulation();
308  this->startTime = chrono::system_clock::now();
309  vector<IS> offspring(this->populationSize);
310  while (!isStoppingConditionReached()) {
311 #ifdef DEBUG
312  std::cout << "Performing Generation " << this->performedEvaluations
313  << std::endl;
314 #endif
315  for (int i = 0; i < this->populationSize; i++) {
316  IS firstChild = selection->select(this->population);
317  IS secondChild = selection->select(this->population);
318  reproduction(firstChild, secondChild);
319  offspring[i] = firstChild;
320  }
321  evaluationPhase(offspring);
322  replacement(offspring);
323  // Updates the fitness evolution
324  this->updateEvolution(this->population);
325  updateProgress();
326  }
327  this->endTime = chrono::system_clock::now();
328  finishProgress();
329  } else {
330  throw std::logic_error("AbstractDomain not set in EIG::run");
331  }
332 }
333 
344 template <typename IP, typename IS, typename OP, typename OS>
345 void EIG<IP, IS, OP, OS>::updateEvolution(vector<IS> &solutions) {
346  if (this->performedEvaluations % 10 == 0) {
347  this->evolution.update(this->performedEvaluations, solutions);
348  this->avgEvolution.update(this->performedEvaluations, solutions);
349  }
350 }
351 
369 template <typename IP, typename IS, typename OP, typename OS>
370 void EIG<IP, IS, OP, OS>::evaluationPhase(vector<IS> &individuals) {
371  // Evaluates with the Instance type and the EAs (only Fitness)
372  evaluatePopulation(individuals);
373  // Computes the diversity of each individual
374  individuals = noveltySearch->run(individuals, this->instProb.get());
375  // Computes the weighted sum of both fit + div
376 #ifdef DEBUG
377  auto [fRatio, nRatio] = this->weightedFitness->getFAndNRatios();
378  std::cout << fmt::format("{:=^120}", "") << std::endl;
379  std::cout << fmt::format("{:^20}({})\t{:^20}({})\t{:^20}",
380  "Performance Score ", fRatio, "Diversity Score ",
381  nRatio, "Fitness")
382  << std::endl;
383  std::cout << fmt::format("{:=^120}", "") << std::endl;
384 #endif
385  weightedFitness->computeFitness(individuals);
386 }
387 
397 template <typename IP, typename IS, typename OP, typename OS>
399  return noveltySearch->getResults();
400 }
401 
409 template <typename IP, typename IS, typename OP, typename OS>
411  this->performedEvaluations = 0;
412 }
413 
422 template <typename IP, typename IS, typename OP, typename OS>
424  this->performedEvaluations++;
425 }
426 
434 template <typename IP, typename IS, typename OP, typename OS>
436  std::chrono::duration<double> diff = this->endTime - this->startTime;
437  this->elapsedTime = diff.count();
438 }
439 
449 template <typename IP, typename IS, typename OP, typename OS>
451  return (this->performedEvaluations >= this->maxEvaluations);
452 }
453 
462 template <typename IP, typename IS, typename OP, typename OS>
464  this->population.reserve(this->populationSize);
465  this->population = instProb->createSolutions(this->populationSize);
466  evaluatePopulation(this->population);
467  // Computes the diversity of each individual
468  this->population =
469  noveltySearch->run(this->population, this->instProb.get());
470  // Computes the weighted sum of both fit + div
471  weightedFitness->computeFitness(this->population);
472  this->updateEvolution(this->population);
473  initProgress();
474 }
475 
486 template <typename IP, typename IS, typename OP, typename OS>
487 void EIG<IP, IS, OP, OS>::evaluatePopulation(vector<IS> &individuals) {
488  instProb->beforeEvaluation(individuals);
489  for (IS &individual : individuals) {
490  // Creates the optimisation problem for the EAs
491  shared_ptr<OP> optProblem = instProb->genOptProblem(individual);
492 
493  vector<float> avgBestFitEAs; // Avg. Best fitness from each alg.
494  avgBestFitEAs.reserve(algPortfolio.size());
495  vector<vector<float>> bestSolsEAs; // Best #reps solutions/algorithm
496 
497  for (unique_ptr<Alg> &config : algPortfolio) {
498  vector<float> bestF(repetitions, 0.0); // Resets the bestF
499  // Run the problem rep times
500  for (int rep = 0; rep < repetitions; rep++) {
501  config->setProblem(optProblem);
502  config->run();
503  // Get the results and compute the best founds
504  Front<OS> front = config->getResults();
505  OS bestSolution = bestFitness.getBestSolution(front);
506  bestF[rep] = bestSolution.getFitness();
507  }
508  // Saves avg. of the best found in #reps
509  float averageBestFitness = averageFitness.computeValue(bestF);
510  avgBestFitEAs.push_back(averageBestFitness);
511  // Saves all best #reps solutions for analysis
512  bestSolsEAs.push_back(bestF);
513  }
514 
515  // Saves the fitness founds to future analysis
516  individual.setConfigFitness(bestSolsEAs);
517  individual.setAvgConfigFitness(avgBestFitEAs);
518  // Set the biased fitness depending on the type of instance to generate
519  // (easy or hard)
520  individual.setBiasedFitness(
521  this->instanceFitness->compute(avgBestFitEAs));
522  }
523  // Problem dependent changes that must be executed after evaluate the
524  // instances
525  instProb->afterEvaluation(individuals);
526 }
527 
538 template <typename IP, typename IS, typename OP, typename OS>
539 [[maybe_unused]] void EIG<IP, IS, OP, OS>::evaluateIndividual(IS &individual) {
540  // Creates the optimisation problem for the EAs
541  shared_ptr<OP> optProblem = instProb->genOptProblem(individual);
542  vector<float> allFitness;
543  allFitness.reserve(algPortfolio.size());
544  for (unique_ptr<Alg> &config : algPortfolio) {
545  Front<OS> solutions(repetitions);
546  for (int rep = 0; rep < repetitions; rep++) {
547  config->setProblem(optProblem);
548  config->run();
549  Front<OS> front = config->getResults();
550  OS bestSolution = bestFitness.getBestSolution(front);
551  solutions.addSolution(bestSolution);
552  }
553  // Set the fitness as the avg. of the best found in #repetitions times
554  float averageBestFitness = averageFitness.computeValue(solutions);
555  allFitness.push_back(averageBestFitness);
556  }
557  individual.setBiasedFitness(instanceFitness->compute(allFitness));
558 }
559 
570 template <typename IP, typename IS, typename OP, typename OS>
571 void EIG<IP, IS, OP, OS>::reproduction(IS &solution1, IS &solution2) {
572  // Cruzamos los individuos con cierta probabilidad
573  if (PseudoRandom::randDouble() < this->crossoverRate) {
574  this->crossover->run(solution1, solution2);
575  }
576  this->mutation->run(solution1, this->mutationRate, this->instProb.get());
577 }
578 
589 template <typename IP, typename IS, typename OP, typename OS>
590 void EIG<IP, IS, OP, OS>::replacement(vector<IS> &individuals) {
591  if (individuals.size() != this->population.size()) {
592  std::string where = " replacement at EIG";
593  throw(Mismatch(where));
594  }
595  const float probIncl = this->populationSize / 100.0;
596  for (int i = 0; i < this->populationSize; i++) {
597  if (this->population[i].getDiversity() >=
598  this->noveltySearch->getThreshold() ||
599  ((PseudoRandom::randDouble() > probIncl) &&
600  (this->population[i].getBiasedFitness() > 0))) {
601  // Always check feasibility
602  this->noveltySearch->insertIntoArchive(this->population[i]);
603  }
604  }
605  // Novelty Search procedures to include individuals in the solution set
606  this->noveltySearch->cmpFinals(this->population);
607  // Performs the replacement operator
608  this->population =
609  this->repOperator->replace(this->population, individuals);
610 }
611 
621 template <typename IP, typename IS, typename OP, typename OS>
622 void EIG<IP, IS, OP, OS>::setPortfolio(vector<unique_ptr<Alg>> &configs) {
623  algPortfolio.clear();
624  algPortfolio.reserve(configs.size());
625  for (unsigned int i = 0; i < configs.size(); i++) {
626  algPortfolio.push_back(move(configs[i]));
627  }
628 }
629 
638 template <typename IP, typename IS, typename OP, typename OS>
639 void EIG<IP, IS, OP, OS>::setInstanceProblem(unique_ptr<IP> problem) {
640  this->instProb = move(problem);
641 }
642 
651 template <typename IP, typename IS, typename OP, typename OS>
652 vector<AbstractEA<OS> *> EIG<IP, IS, OP, OS>::getPortfolio() const {
653  vector<Alg *> configurations;
654  configurations.reserve(algPortfolio.size());
655  for (const unique_ptr<Alg> &config : algPortfolio) {
656  configurations.push_back(config.get());
657  }
658  return configurations;
659 }
660 
669 template <typename IP, typename IS, typename OP, typename OS>
671  json data;
672  Evolution copy = this->evolution;
673  AvgEvolution avgCopy = this->avgEvolution;
674  data["name"] = this->getName();
675  data["max_generations"] = this->getMaxEvaluations();
676  data["repetitions"] = this->repetitions;
677  data["pop_size"] = this->populationSize;
678  data["mutation"] = this->mutation->getName();
679  data["crossover"] = this->crossover->getName();
680  data["replacement"] = this->repOperator->getName();
681  data["mutation_rate"] = this->mutationRate;
682  data["crossover_rate"] = this->crossoverRate;
683  data["elapsed_time"] = this->elapsedTime;
684  data["evolution"] = copy.results();
685  data["avg_evolution"] = avgCopy.results();
686  data["novelty_search"] = this->noveltySearch->to_json();
687  auto [fRatio, nRatio] = this->weightedFitness->getFAndNRatios();
688  data["weighted_function"]["fitness_ratio"] = fRatio;
689  data["weighted_function"]["novelty_ratio"] = nRatio;
690  int i = 0;
691  bool target = true;
692  for (const unique_ptr<Alg> &config : this->algPortfolio) {
693  data["portfolio"][i] = config->to_json();
694  data["portfolio"][i]["isTarget"] = target;
695  i++;
696  target = false;
697  }
698 
699  return data;
700 }
701 
702 #endif
Class to define an Abstract Evolutionary Algorithm. This is the base skeleton for future extensions.
Definition: AbstractEA.h:42
int maxEvaluations
Definition: AbstractEA.h:259
Abstract Crossover interface.
Definition: Crossover.h:19
Instance Generation Algorithm. Known as Meta-Evolutionary Algorithm (EIG). This algorithm uses a Weig...
Definition: EIG.h:55
void setInstanceProblem(unique_ptr< IP > problem)
Set the Instance Generation Problem.
Definition: EIG.h:639
void setReplacement(unique_ptr< Replacement< IS >> rep)
Set the selection operator. Takes ownership of the pointer.
Definition: EIG.h:178
BestFitness< OS > bestFitness
Definition: EIG.h:262
void setInstanceFitness(unique_ptr< InstanceFitness > eval)
Set the evaluation approach for the instances.
Definition: EIG.h:192
int repetitions
Definition: EIG.h:253
float getMutationRate() const
Get the mutation rate used in EIG for mutation instances during the evolutionary process.
Definition: EIG.h:112
float mutationRate
Definition: EIG.h:251
AverageFitness< OS > averageFitness
Definition: EIG.h:259
void run() override
Interface to run the EIG for generating a set of instances for the optimisation problem defined (OP)
Definition: EIG.h:298
virtual void updateEvolution(vector< IS > &solutions)
Updates the evolution results of the algorithm for the given checkpoint.
Definition: EIG.h:345
void setCrossover(unique_ptr< Crossover< IS >> cx)
Set the crossover operator. Takes ownership of the pointer.
Definition: EIG.h:163
void setMutationRate(float mRate)
Set the mutation rate used in EIG for mutation instances during the evolutionary process.
Definition: EIG.h:117
virtual void reproduction(IS &solution, IS &solution2)
Executes the genetic operator over two instances to generate new individuals.
Definition: EIG.h:571
virtual void replacement(vector< IS > &individuals)
Replacement method for the population. In this case, the replacement is a simple generational approac...
Definition: EIG.h:590
float crossoverRate
Definition: EIG.h:252
void setSelection(unique_ptr< Selection< IS >> sel)
Set the selection operator. Takes ownership of the pointer.
Definition: EIG.h:172
void updateProgress() override
Updates the evolution by increasing the number of generations performed.
Definition: EIG.h:423
vector< unique_ptr< Alg > > algPortfolio
Definition: EIG.h:244
void evaluateIndividual(IS &individual)
Evaluates the given IS with all the algorithms in the porftolio.
Definition: EIG.h:539
float getCrossoverRate() const
Get the crossover rate used in EIG for applying the crossover operator to instances during the evolut...
Definition: EIG.h:125
unique_ptr< Mutation< IS > > mutation
Definition: EIG.h:246
int getRepetitions() const
Get the number of repetitions that each algorithm in the portfolio must perform over each instance at...
Definition: EIG.h:138
Selection< IS > * getSelection() const
Get the selection operator.
Definition: EIG.h:168
json to_json() const override
Creates the JSON representation of the EIG.
Definition: EIG.h:670
void finishProgress() override
Finishes the execution of the EIG.
Definition: EIG.h:435
unique_ptr< NoveltySearch< IS > > noveltySearch
Definition: EIG.h:255
void setPortfolio(vector< unique_ptr< Alg >> &configs)
Set the portfolio of algorithms to use in the evolutionary process. The order of the algorithms in th...
Definition: EIG.h:622
virtual void evaluationPhase(vector< IS > &individuals)
Evaluation phase of the EIG All the stages necessary to evaluated the population of individuals are p...
Definition: EIG.h:370
Mutation< IS > * getMutation() const
Get the mutation operator used to mutate instances in EIG.
Definition: EIG.h:150
void createInitialPopulation() override
Creates a initial population of Problem Instances of the OP type. Uses the IP method "createSolutions...
Definition: EIG.h:463
bool isStoppingConditionReached() override
Checks whether the number of performed generations has reached the maximum allowed.
Definition: EIG.h:450
void setRepetitions(int reps)
Set the number of repetitions that each algorithm in the portfolio must perform over each instance at...
Definition: EIG.h:146
InstanceFitness * getEvaluation() const
Get the evaluation approach for the instances.
Definition: EIG.h:187
void setCrossoverRate(float cRate)
Set the crossover rate used in EIG for applying the crossover operator to instances during the evolut...
Definition: EIG.h:130
void setNoveltySearch(unique_ptr< NoveltySearch< IS >> noveltySearch)
Set the Novelty Search approach to use.
Definition: EIG.h:207
unique_ptr< IP > instProb
Definition: EIG.h:242
unique_ptr< Crossover< IS > > crossover
Definition: EIG.h:247
void setMutation(unique_ptr< Mutation< IS >> mut)
Set the mutation operator. Takes ownership of the pointer.
Definition: EIG.h:154
void setWeightedFunction(const float &fW, const float &nW)
Set the weights to use in the WFF.
Definition: EIG.h:214
virtual Front< IS > getResults() const override
Returns a set of individuals as a result of the evolutionary method. The individual are instances for...
Definition: EIG.h:398
void evaluatePopulation(vector< IS > &individuals) override
Evaluates the population of individuals with all the algorithms in the portfolio.
Definition: EIG.h:487
string getID() const override
Get the ID. Similar to getName.
Definition: EIG.h:80
void initProgress() override
Initialises the number of generations performed to 0.
Definition: EIG.h:410
string getName() const override
Get the name of the algorithm.
Definition: EIG.h:77
const NoveltySearch< IS > * getNoveltySearch() const
Get the Novelty Search approach used in EIG.
Definition: EIG.h:202
Crossover< IS > * getCrossover() const
Get the crossover operator used in EIG.
Definition: EIG.h:159
vector< Alg * > getPortfolio() const
Get the portfolio of algorithms used in the evolutionary process.
Definition: EIG.h:652
EIG(const float &fitness=0.6f, const float &novelty=0.4f)
Construct a new EIG object using a fitness ratio of 0.6 and novelty of 0.4 by default....
Definition: EIG.h:274
const IP * getInstanceProblem() const
Get a raw pointer to the Instance Generation Problem.
Definition: EIG.h:86
unique_ptr< Selection< IS > > selection
Definition: EIG.h:248
int getGenerations()
Get the number of generations that EIG must perform.
Definition: EIG.h:198
Front class which stores the final results of an EA execution.
Definition: Front.h:26
void addSolution(const S &solution)
Includes a new solution in the front. This is only for single-objective solutions.
Definition: Front.h:144
Interface to define the fitness calculation of the instances evolved by EIG.
Definition: InstanceFitness.h:17
Abstract Mutation interface.
Definition: Mutation.h:21
Class to represent the Novelty Search Algorithm.
Definition: NoveltySearch.h:50
static double randDouble()
Generates a random double value between 0.0 and 1.0.
Definition: PseudoRandom.cpp:31
Replacement skeleton operator.
Definition: Replacement.h:16
Abstract Selection interface.
Definition: Selection.h:21