diff --git a/mo/src/neighborhood/moRealVectorNeighbor.h b/mo/src/neighborhood/moRealVectorNeighbor.h new file mode 100644 index 000000000..2036460ba --- /dev/null +++ b/mo/src/neighborhood/moRealVectorNeighbor.h @@ -0,0 +1,100 @@ +/* + + Copyright (C) DOLPHIN Project-Team, INRIA Lille - Nord Europe, 2006-2010 + (C) OPAC Team, LIFL, 2002-2007 + + Eremey Valetov + + This software is governed by the CeCILL license under French law and + abiding by the rules of distribution of free software. You can use, + modify and/ or redistribute the software under the terms of the CeCILL + license as circulated by CEA, CNRS and INRIA at the following URL + "http://www.cecill.info". + + As a counterpart to the access to the source code and rights to copy, + modify and redistribute granted by the license, users are provided only + with a limited warranty and the software's author, the holder of the + economic rights, and the successive licensors have only limited liability. + + In this respect, the user's attention is drawn to the risks associated + with loading, using, modifying and/or developing or reproducing the + software by the user in light of its specific status of free software, + that may mean that it is complicated to manipulate, and that also + therefore means that it is reserved for developers and experienced + professionals having in-depth computer knowledge. Users are therefore + encouraged to load and test the software's suitability as regards their + requirements in conditions enabling the security of their systems and/or + data to be ensured and, more generally, to use and operate it in the + same conditions as regards security. + The fact that you are presently reading this means that you have had + knowledge of the CeCILL license and that you accept its terms. + + ParadisEO WebSite : http://paradiseo.gforge.inria.fr + Contact: paradiseo-help@lists.gforge.inria.fr +*/ + +#ifndef _moRealVectorNeighbor_h +#define _moRealVectorNeighbor_h + +#include +#include +#include +#include +#include + +/** + * Neighbor for real-valued vector solutions. + * Stores a delta vector; move() adds it to the solution, moveBack() subtracts it. + */ +template +class moRealVectorNeighbor : public moBackableNeighbor, public std::vector { +public: + typedef std::vector EOVT; + using EOVT::operator[]; + using EOVT::size; + + moRealVectorNeighbor(unsigned _size = 0, double _value = 0.0) + : moBackableNeighbor(), EOVT(_size, _value) {} + + moRealVectorNeighbor(const moRealVectorNeighbor& _n) + : moBackableNeighbor(_n), EOVT(_n) {} + + moRealVectorNeighbor& operator=(const moRealVectorNeighbor& _source) { + moBackableNeighbor::operator=(_source); + EOVT::operator=(_source); + return *this; + } + + bool equals(moRealVectorNeighbor& _neighbor) const { + if (size() != _neighbor.size()) + return false; + for (size_t i = 0; i < size(); i++) + if ((*this)[i] != _neighbor[i]) + return false; + return true; + } + + void move(EOT& _solution) override { + for (size_t i = 0; i < _solution.size(); i++) + _solution[i] += (*this)[i]; + _solution.invalidate(); + } + + void moveBack(EOT& _solution) override { + for (size_t i = 0; i < _solution.size(); i++) + _solution[i] -= (*this)[i]; + _solution.invalidate(); + } + + void printOn(std::ostream& _os) const override { + EO::printOn(_os); + _os << ' ' << size() << ' '; + std::copy(this->begin(), this->end(), std::ostream_iterator(_os, " ")); + } + + std::string className() const override { + return "moRealVectorNeighbor"; + } +}; + +#endif diff --git a/moeo/src/algo/moeoAlgo.h b/moeo/src/algo/moeoAlgo.h index 39b01f394..3458ec0c4 100644 --- a/moeo/src/algo/moeoAlgo.h +++ b/moeo/src/algo/moeoAlgo.h @@ -42,6 +42,12 @@ * Abstract class for multi-objective algorithms. */ class moeoAlgo - {}; +{ +public: + virtual ~moeoAlgo() = default; + + /** Whether this algorithm supports finalize() for post-integration updates. */ + virtual bool hasFinalize() const { return false; } +}; #endif /*MOEOALGO_H_*/ diff --git a/moeo/src/algo/moeoNSGAII.h b/moeo/src/algo/moeoNSGAII.h index 137592865..68e0cd200 100644 --- a/moeo/src/algo/moeoNSGAII.h +++ b/moeo/src/algo/moeoNSGAII.h @@ -148,6 +148,17 @@ public: while (continuator (_pop)); } + /** + * Recompute fitness and diversity assignments on the population. + * Useful after integrating immigrants from an island model. + * @param _pop the population to finalize + */ + void finalize(eoPop& _pop) override { + fitnessAssignment(_pop); + diversityAssignment(_pop); + } + + bool hasFinalize() const override { return true; } protected: diff --git a/moeo/src/algo/moeoPopAlgo.h b/moeo/src/algo/moeoPopAlgo.h index a3b948a3e..1caa40744 100644 --- a/moeo/src/algo/moeoPopAlgo.h +++ b/moeo/src/algo/moeoPopAlgo.h @@ -47,6 +47,14 @@ */ template < class MOEOT > class moeoPopAlgo : public moeoAlgo, public eoAlgo < MOEOT > - {}; +{ +public: + /** + * Recompute fitness/diversity after external population changes (e.g. immigrant integration). + * Default implementation is a no-op. Override in subclasses that need it. + * @param _pop the population to finalize + */ + virtual void finalize(eoPop&) {} +}; #endif /*MOEOPOPALGO_H_*/ diff --git a/moeo/src/algo/moeoUnifiedDominanceBasedLS_Real.h b/moeo/src/algo/moeoUnifiedDominanceBasedLS_Real.h new file mode 100644 index 000000000..419e255f2 --- /dev/null +++ b/moeo/src/algo/moeoUnifiedDominanceBasedLS_Real.h @@ -0,0 +1,149 @@ +/* +* +* Copyright (C) DOLPHIN Project-Team, INRIA Futurs, 2006-2008 +* (C) OPAC Team, LIFL, 2002-2008 +* +* Eremey Valetov +* +* This software is governed by the CeCILL license under French law and +* abiding by the rules of distribution of free software. You can use, +* modify and/ or redistribute the software under the terms of the CeCILL +* license as circulated by CEA, CNRS and INRIA at the following URL +* "http://www.cecill.info". +* +* As a counterpart to the access to the source code and rights to copy, +* modify and redistribute granted by the license, users are provided only +* with a limited warranty and the software's author, the holder of the +* economic rights, and the successive licensors have only limited liability. +* +* In this respect, the user's attention is drawn to the risks associated +* with loading, using, modifying and/or developing or reproducing the +* software by the user in light of its specific status of free software, +* that may mean that it is complicated to manipulate, and that also +* therefore means that it is reserved for developers and experienced +* professionals having in-depth computer knowledge. Users are therefore +* encouraged to load and test the software's suitability as regards their +* requirements in conditions enabling the security of their systems and/or +* data to be ensured and, more generally, to use and operate it in the +* same conditions as regards security. +* The fact that you are presently reading this means that you have had +* knowledge of the CeCILL license and that you accept its terms. +* +* ParadisEO WebSite : http://paradiseo.gforge.inria.fr +* Contact: paradiseo-help@lists.gforge.inria.fr +* +*/ +//----------------------------------------------------------------------------- + +#ifndef _MOEOUNIFIEDDOMINANCEBASEDLSREAL_H +#define _MOEOUNIFIEDDOMINANCEBASEDLSREAL_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _OPENMP +#include +#endif + +/** + * Real-valued dominance-based local search for multi-objective optimization. + * Uses an archive, flexible selection strategy, and neighborhood exploration + * with optional OpenMP parallelism for initial population evaluation. + */ +template < class Neighbor > +class moeoUnifiedDominanceBasedLSReal : public moeoPopLS < Neighbor > +{ +public: + typedef typename Neighbor::EOT MOEOT; + + moeoUnifiedDominanceBasedLSReal( + eoContinue < MOEOT > & _continuator, + eoEvalFunc < MOEOT > & _eval, + moeoArchive < MOEOT > & _archive, + moeoPopNeighborhoodExplorer < Neighbor > & _explorer, + moeoUnvisitedSelect < MOEOT > & _select) + : continuator(_continuator), eval(_eval), + archive(_archive), explorer(_explorer), select(_select) {} + + virtual void operator()(eoPop& _pop) override + { + size_t initialPopSize = _pop.size(); + int generation = 0; + + std::vector selectionVector; + std::vector initialFlags; + eoPop tmp_pop; + +#ifdef _OPENMP + #pragma omp parallel for +#endif + for (size_t i = 0; i < _pop.size(); i++) + eval(_pop[i]); + + archive(_pop); + + do { + tmp_pop.resize(0); + selectionVector = select(archive); + + initialFlags.clear(); + initialFlags.reserve(selectionVector.size()); + for (auto idx : selectionVector) + initialFlags.push_back(archive[idx].flag()); + + eo::log << eo::progress << "LS generation " << ++generation + << ", archive size = " << archive.size() << std::endl; + eo::log << eo::debug << "continuator = " << continuator << std::endl; + + explorer(archive, selectionVector, tmp_pop); + + // Verify explorer incremented visit counts correctly + bool allIncremented = true; + bool noneIncremented = true; + for (size_t i = 0; i < selectionVector.size(); i++) { + int newFlag = archive[selectionVector[i]].flag(); + if (newFlag == initialFlags[i] + 1) + noneIncremented = false; + else + allIncremented = false; + } + + if (!allIncremented && noneIncremented) { + eo::log << eo::warnings << "Explorer did not increment exploration counts" << std::endl; + for (auto idx : selectionVector) + archive[idx].flag(archive[idx].flag() + 1); + } else if (!allIncremented) { + eo::log << eo::warnings << "Exploration counts incremented inconsistently" << std::endl; + for (size_t i = 0; i < selectionVector.size(); i++) + archive[selectionVector[i]].flag(initialFlags[i] + 1); + } + + archive(tmp_pop); + archive(_pop); + + if (archive.size() <= initialPopSize) { + _pop = archive; + } else { + tmp_pop.resize(0); + for (size_t i = 0; i < initialPopSize; i++) + tmp_pop.push_back(archive[i]); + _pop = tmp_pop; + } + } while (continuator(archive)); + } + +protected: + eoContinue < MOEOT > & continuator; + eoEvalFunc& eval; + moeoArchive < MOEOT > & archive; + moeoPopNeighborhoodExplorer < Neighbor > & explorer; + moeoUnvisitedSelect < MOEOT > & select; +}; + +#endif /* _MOEOUNIFIEDDOMINANCEBASEDLSREAL_H */ diff --git a/moeo/src/explorer/moeoRealVectorNeighborhoodExplorer.h b/moeo/src/explorer/moeoRealVectorNeighborhoodExplorer.h new file mode 100644 index 000000000..2363055fb --- /dev/null +++ b/moeo/src/explorer/moeoRealVectorNeighborhoodExplorer.h @@ -0,0 +1,262 @@ +/* +* +* Copyright (C) DOLPHIN Project-Team, INRIA Futurs, 2006-2008 +* (C) OPAC Team, LIFL, 2002-2008 +* +* Eremey Valetov +* +* This software is governed by the CeCILL license under French law and +* abiding by the rules of distribution of free software. You can use, +* modify and/ or redistribute the software under the terms of the CeCILL +* license as circulated by CEA, CNRS and INRIA at the following URL +* "http://www.cecill.info". +* +* As a counterpart to the access to the source code and rights to copy, +* modify and redistribute granted by the license, users are provided only +* with a limited warranty and the software's author, the holder of the +* economic rights, and the successive licensors have only limited liability. +* +* In this respect, the user's attention is drawn to the risks associated +* with loading, using, modifying and/or developing or reproducing the +* software by the user in light of its specific status of free software, +* that may mean that it is complicated to manipulate, and that also +* therefore means that it is reserved for developers and experienced +* professionals having in-depth computer knowledge. Users are therefore +* encouraged to load and test the software's suitability as regards their +* requirements in conditions enabling the security of their systems and/or +* data to be ensured and, more generally, to use and operate it in the +* same conditions as regards security. +* The fact that you are presently reading this means that you have had +* knowledge of the CeCILL license and that you accept its terms. +* +* ParadisEO WebSite : http://paradiseo.gforge.inria.fr +* Contact: paradiseo-help@lists.gforge.inria.fr +* +*/ +//----------------------------------------------------------------------------- + +#ifndef MOEOREALVECTORNEIGHBORHOODEXPLORER_H +#define MOEOREALVECTORNEIGHBORHOODEXPLORER_H + +#include +#include +#include +#include +#include +#include + +/** + * Neighborhood explorer for real-valued vector solutions. + * Generates random directions in the search space, evaluates neighbors, + * and performs geometric scaling along improving directions. + * + * @tparam Neighbor neighbor type (must provide EOT typedef) + */ +template +class RealVectorNeighborhoodExplorer : public moeoPopNeighborhoodExplorer { +public: + typedef typename Neighbor::EOT MOEOT; + + /** + * @param _eval evaluation function + * @param _bounds bounds for each dimension + * @param _epsilon step size relative to range (scalar, applied to all dimensions) + * @param _ntrydirections number of random directions to try + * @param _breakdirectionlooponfirstsuccess stop direction loop on first improving direction + * @param _minntryscaling minimum scaling iterations before stopping + * @param _maxntryscaling maximum scaling iterations + * @param _scalingfactor multiplicative factor for scaling steps + */ + RealVectorNeighborhoodExplorer(eoEvalFunc& _eval, const eoRealVectorBounds& _bounds, + double _epsilon = 1e-5, unsigned int _ntrydirections = 10, + bool _breakdirectionlooponfirstsuccess = false, + unsigned int _minntryscaling = 2, unsigned int _maxntryscaling = 5, + unsigned int _scalingfactor = 4) + : bounds(_bounds), epsilons(std::vector(_bounds.size(), _epsilon)), + ntrydirections(_ntrydirections), breakdirectionlooponfirstsuccess(_breakdirectionlooponfirstsuccess), + minntryscaling(_minntryscaling), maxntryscaling(_maxntryscaling), scalingfactor(_scalingfactor), + evalFunc(_eval), minntryscaling_vectype(false) {} + + /** + * @param _eval evaluation function + * @param _bounds bounds for each dimension + * @param _epsilon step size relative to range (scalar) + * @param _ntrydirections number of random directions + * @param _breakdirectionlooponfirstsuccess stop on first success + * @param _mintryscaling_explored per-visit-count minimum scaling vector + * @param _maxntryscaling maximum scaling iterations + * @param _scalingfactor multiplicative factor + */ + RealVectorNeighborhoodExplorer(eoEvalFunc& _eval, const eoRealVectorBounds& _bounds, + double _epsilon, unsigned int _ntrydirections, + bool _breakdirectionlooponfirstsuccess, + std::vector _mintryscaling_explored, + unsigned int _maxntryscaling = 5, unsigned int _scalingfactor = 4) + : bounds(_bounds), epsilons(std::vector(_bounds.size(), _epsilon)), + ntrydirections(_ntrydirections), breakdirectionlooponfirstsuccess(_breakdirectionlooponfirstsuccess), + minntryscaling_explored(_mintryscaling_explored), maxntryscaling(_maxntryscaling), + scalingfactor(_scalingfactor), evalFunc(_eval), minntryscaling_vectype(true) {} + + /** + * @param _eval evaluation function + * @param _bounds bounds for each dimension + * @param _epsilons per-dimension step sizes + * @param _ntrydirections number of random directions + * @param _breakdirectionlooponfirstsuccess stop on first success + * @param _minntryscaling minimum scaling iterations + * @param _maxntryscaling maximum scaling iterations + * @param _scalingfactor multiplicative factor + */ + RealVectorNeighborhoodExplorer(eoEvalFunc& _eval, const eoRealVectorBounds& _bounds, + std::vector _epsilons, unsigned int _ntrydirections = 10, + bool _breakdirectionlooponfirstsuccess = false, + unsigned int _minntryscaling = 2, unsigned int _maxntryscaling = 5, + unsigned int _scalingfactor = 4) + : bounds(_bounds), epsilons(_epsilons), ntrydirections(_ntrydirections), + breakdirectionlooponfirstsuccess(_breakdirectionlooponfirstsuccess), + minntryscaling(_minntryscaling), maxntryscaling(_maxntryscaling), scalingfactor(_scalingfactor), + evalFunc(_eval), minntryscaling_vectype(false) { + if (epsilons.size() != bounds.size()) + throw std::invalid_argument("The number of epsilons must match the number of bounds."); + } + + /** + * @param _eval evaluation function + * @param _bounds bounds for each dimension + * @param _epsilons per-dimension step sizes + * @param _ntrydirections number of random directions + * @param _breakdirectionlooponfirstsuccess stop on first success + * @param _mintryscaling_explored per-visit-count minimum scaling vector + * @param _maxntryscaling maximum scaling iterations + * @param _scalingfactor multiplicative factor + */ + RealVectorNeighborhoodExplorer(eoEvalFunc& _eval, const eoRealVectorBounds& _bounds, + std::vector _epsilons, unsigned int _ntrydirections, + bool _breakdirectionlooponfirstsuccess, + std::vector _mintryscaling_explored, + unsigned int _maxntryscaling = 5, unsigned int _scalingfactor = 4) + : bounds(_bounds), epsilons(_epsilons), ntrydirections(_ntrydirections), + breakdirectionlooponfirstsuccess(_breakdirectionlooponfirstsuccess), + minntryscaling_explored(_mintryscaling_explored), maxntryscaling(_maxntryscaling), + scalingfactor(_scalingfactor), evalFunc(_eval), minntryscaling_vectype(true) { + if (epsilons.size() != bounds.size()) + throw std::invalid_argument("The number of epsilons must match the number of bounds."); + } + + void operator()(eoPop& _src, std::vector _select, eoPop& _dest) override { + for (unsigned int i = 0; i < _select.size(); i++) { + explore(_src[_select[i]], _dest); + _src[_select[i]].flag(_src[_select[i]].flag() + 1); + } + for (MOEOT& individual : _dest) + individual.flag(0); + } + +protected: + eoRealVectorBounds bounds; + std::vector epsilons; + unsigned int ntrydirections; + bool breakdirectionlooponfirstsuccess; + bool minntryscaling_vectype; + unsigned int minntryscaling; + std::vector minntryscaling_explored; + unsigned int maxntryscaling; + unsigned int scalingfactor; + eoEvalFunc& evalFunc; + + virtual void explore(MOEOT& _src, eoPop& _dest) { + std::random_device rd; + std::mt19937 generator(rd()); + std::uniform_real_distribution distribution(-1, 1); + + unsigned int minntryscaling_eff; + if (minntryscaling_vectype) { + if (!minntryscaling_explored.empty()) { + minntryscaling_eff = minntryscaling_explored[ + std::min(static_cast(_src.flag()), + minntryscaling_explored.size() - 1)]; + } else { + minntryscaling_eff = 1; + } + } else { + minntryscaling_eff = minntryscaling; + } + + for (unsigned int i = 0; i < ntrydirections; ++i) { + MOEOT modified_individual = _src; + std::vector random_direction(modified_individual.size()); + + double magnitude; + do { + magnitude = 0.0; + for (unsigned int j = 0; j < _src.size(); ++j) { + random_direction[j] = distribution(generator); + magnitude += random_direction[j] * random_direction[j]; + } + } while (magnitude > 1.0 || magnitude == 0); + + magnitude = std::sqrt(magnitude); + + for (unsigned int j = 0; j < modified_individual.size(); ++j) { + double range = bounds.maximum(j) - bounds.minimum(j); + random_direction[j] *= range * epsilons[j] / magnitude; + } + + for (unsigned int j = 0; j < modified_individual.size(); ++j) + modified_individual[j] += random_direction[j]; + modified_individual.invalidate(); + + if (!bounds.isInBounds(modified_individual)) + continue; + + moeoParetoObjectiveVectorComparator comparator; + evalFunc(modified_individual); + + if (comparator(_src.objectiveVector(), modified_individual.objectiveVector())) { + _dest.push_back(modified_individual); + + MOEOT previous_explored_individual = modified_individual; + bool continued_to_scale = false; + + for (unsigned int k = 0; k < maxntryscaling; ++k) { + for (unsigned int j = 0; j < modified_individual.size(); ++j) + random_direction[j] *= scalingfactor; + + MOEOT scaled_individual = _src; + for (unsigned int j = 0; j < scaled_individual.size(); ++j) + scaled_individual[j] += random_direction[j]; + scaled_individual.invalidate(); + + if (!bounds.isInBounds(scaled_individual)) + break; + + evalFunc(scaled_individual); + + if (k == maxntryscaling - 1) + break; + + if (comparator(_src.objectiveVector(), scaled_individual.objectiveVector())) { + _dest.push_back(scaled_individual); + } else if (k < minntryscaling_eff - 1) { + continued_to_scale = true; + } else { + break; + } + + if (!comparator(scaled_individual.objectiveVector(), previous_explored_individual.objectiveVector())) { + if (k < minntryscaling_eff - 1) + continued_to_scale = true; + else + break; + } + + previous_explored_individual = scaled_individual; + } + + if (breakdirectionlooponfirstsuccess) break; + } + } + } +}; + +#endif //MOEOREALVECTORNEIGHBORHOODEXPLORER_H diff --git a/moeo/src/moeo b/moeo/src/moeo index 4a7908913..5b8e47397 100644 --- a/moeo/src/moeo +++ b/moeo/src/moeo @@ -56,6 +56,7 @@ #include #include #include +#include //#include #include @@ -115,6 +116,7 @@ #include #include #include +#include #include #include @@ -206,6 +208,7 @@ #include #include #include +#include #include #include diff --git a/moeo/src/selection/moeoBestUnvisitedSelect.h b/moeo/src/selection/moeoBestUnvisitedSelect.h new file mode 100644 index 000000000..b38ee0629 --- /dev/null +++ b/moeo/src/selection/moeoBestUnvisitedSelect.h @@ -0,0 +1,127 @@ +/* +* +* Copyright (C) DOLPHIN Project-Team, INRIA Futurs, 2006-2008 +* (C) OPAC Team, LIFL, 2002-2008 +* +* Arnaud Liefooghe +* Jérémie Humeau +* Eremey Valetov +* +* This software is governed by the CeCILL license under French law and +* abiding by the rules of distribution of free software. You can use, +* modify and/ or redistribute the software under the terms of the CeCILL +* license as circulated by CEA, CNRS and INRIA at the following URL +* "http://www.cecill.info". +* +* As a counterpart to the access to the source code and rights to copy, +* modify and redistribute granted by the license, users are provided only +* with a limited warranty and the software's author, the holder of the +* economic rights, and the successive licensors have only limited liability. +* +* In this respect, the user's attention is drawn to the risks associated +* with loading, using, modifying and/or developing or reproducing the +* software by the user in light of its specific status of free software, +* that may mean that it is complicated to manipulate, and that also +* therefore means that it is reserved for developers and experienced +* professionals having in-depth computer knowledge. Users are therefore +* encouraged to load and test the software's suitability as regards their +* requirements in conditions enabling the security of their systems and/or +* data to be ensured and, more generally, to use and operate it in the +* same conditions as regards security. +* The fact that you are presently reading this means that you have had +* knowledge of the CeCILL license and that you accept its terms. +* +* ParadisEO WebSite : http://paradiseo.gforge.inria.fr +* Contact: paradiseo-help@lists.gforge.inria.fr +* +*/ +//----------------------------------------------------------------------------- + +#ifndef _MOEOBESTUNVISITEDSELECT_H +#define _MOEOBESTUNVISITEDSELECT_H + +#include +#include +#include +#include +#include + +/** + * Selects up to a given number of unvisited (or all) individuals from a population, + * optionally excluding those dominated by given objective vectors. The selected + * indices are returned in shuffled order. + */ +template < class MOEOT > +class moeoBestUnvisitedSelect : public moeoUnvisitedSelect < MOEOT > +{ +public: + /** + * @param _number maximum number of individuals to select + * @param _selectVisited if true, also consider visited individuals + */ + moeoBestUnvisitedSelect(unsigned int _number, bool _selectVisited = true) + : number(_number), selectVisited(_selectVisited) {} + + /** + * @param _number maximum number of individuals to select + * @param _excludeObjectives individuals dominated by any of these are excluded + * @param _selectVisited if true, also consider visited individuals + */ + moeoBestUnvisitedSelect(unsigned int _number, + const std::vector& _excludeObjectives, + bool _selectVisited = true) + : number(_number), excludeObjectives(_excludeObjectives), selectVisited(_selectVisited) {} + + std::vector operator()(eoPop& _src) override + { + std::vector res; + res.reserve(_src.size()); + + moeoParetoObjectiveVectorComparator comparator; + + for (unsigned int i = 0; i < _src.size(); i++) { + if (!selectVisited && _src[i].flag() != 0) + continue; + + bool isExcluded = false; + for (const auto& objVec : excludeObjectives) { + if (comparator(_src[i].objectiveVector(), objVec)) { + isExcluded = true; + eo::log << eo::debug << "Excluding individual " << i + << " based on exclusion objective vector" << std::endl; + break; + } + } + if (!isExcluded) + res.push_back(i); + } + + // Ensure at least one unvisited individual if possible + if (!selectVisited && res.empty()) { + for (unsigned int i = 0; i < _src.size(); i++) { + if (_src[i].flag() == 0) { + res.push_back(i); + break; + } + } + } + + // Last resort: pick the first individual + if (res.empty() && !_src.empty()) + res.push_back(0); + + std::random_device rd; + std::mt19937 g(rd()); + std::shuffle(res.begin(), res.end(), g); + + res.resize(std::min(number, static_cast(res.size()))); + return res; + } + +private: + unsigned int number; + std::vector excludeObjectives; + bool selectVisited; +}; + +#endif /*_MOEOBESTUNVISITEDSELECT_H*/