Skip to content

Commit

Permalink
optimize non-dominated sorters and phase out eps param
Browse files Browse the repository at this point in the history
  • Loading branch information
foolnotion committed Oct 16, 2023
1 parent 4d13b3a commit 9f3ab08
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 33 deletions.
56 changes: 32 additions & 24 deletions source/operators/non_dominated_sorter/deductive_sort.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,48 @@
#include "operon/core/individual.hpp"

namespace Operon {

auto DeductiveSorter::Sort(Operon::Span<Operon::Individual const> pop, Operon::Scalar eps) const -> NondominatedSorterBase::Result
auto DeductiveSorter::Sort(Operon::Span<Operon::Individual const> pop, Operon::Scalar /*unused*/) const -> NondominatedSorterBase::Result
{
size_t n = 0; // total number of sorted solutions
std::vector<std::vector<size_t>> fronts;
std::vector<bool> dominated(pop.size(), false);
std::vector<bool> sorted(pop.size(), false);
auto dominatedOrSorted = [&](size_t i) { return sorted[i] || dominated[i]; };

std::size_t constexpr d = std::numeric_limits<uint64_t>::digits;
auto const s = static_cast<int>(pop.size());
auto const nb = s / d + (s % d != 0);

std::vector<uint64_t> dominated(nb);
std::vector<uint64_t> sorted(nb);

auto set = [](auto&& range, auto i) { range[i / d] |= (1UL << (d - i % d));}; // set bit i
auto reset = [](auto&& range, auto i) { range[i / d] &= ~(1UL << (i % d)); }; // unset bit i
auto get = [](auto&& range, auto i) -> bool { return range[i / d] & (1UL << (d - i % d)); };

auto dominatedOrSorted = [&](std::size_t i) { return get(sorted, i) || get(dominated, i); };

while (n < pop.size()) {
std::vector<size_t> front;

for (size_t i = 0; i < pop.size(); ++i) {
if (!dominatedOrSorted(i)) {
for (size_t j = i + 1; j < pop.size(); ++j) {
if (!dominatedOrSorted(j)) {
auto const& lhs = pop[i];
auto const& rhs = pop[j];
auto res = ParetoDominance{}(lhs.Fitness, rhs.Fitness, eps);
dominated[i] = (res == Dominance::Right);
dominated[j] = (res == Dominance::Left);

if (dominated[i]) {
break;
}
}
}

if (!dominated[i]) {
front.push_back(i);
sorted[i] = true;
}
if (dominatedOrSorted(i)) { continue; }

for (size_t j = i + 1; j < pop.size(); ++j) {
if (dominatedOrSorted(j)) { continue; }

auto const& lhs = pop[i];
auto const& rhs = pop[j];
auto res = ParetoDominance{}(lhs.Fitness, rhs.Fitness);
if (res == Dominance::Right) { set(dominated, i); }
if (res == Dominance::Left) { set(dominated, j); }

if (get(dominated, i)) { break; }
}

if (!get(dominated, i)) {
front.push_back(i);
set(sorted, i);
}
}

std::fill(dominated.begin(), dominated.end(), 0UL);
n += front.size();
fronts.push_back(front);
Expand Down
10 changes: 4 additions & 6 deletions source/operators/non_dominated_sorter/efficient_sort.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,22 @@
namespace Operon {

template<EfficientSortStrategy SearchStrategy>
inline auto EfficientSortImpl(Operon::Span<Operon::Individual const> pop, Operon::Scalar eps) -> NondominatedSorterBase::Result
inline auto EfficientSortImpl(Operon::Span<Operon::Individual const> pop, Operon::Scalar /*unused*/) -> NondominatedSorterBase::Result
{
// check if individual i is dominated by any individual in the front f
auto dominated = [&](auto const& f, size_t i) {
return std::any_of(f.rbegin(), f.rend(), [&](size_t j) {
return ParetoDominance{}(pop[j].Fitness, pop[i].Fitness, eps) == Dominance::Left;
return ParetoDominance{}(pop[j].Fitness, pop[i].Fitness) == Dominance::Left;
});
};

std::vector<std::vector<size_t>> fronts;
for (size_t i = 0; i < pop.size(); ++i) {
decltype(fronts)::iterator it;
if constexpr (SearchStrategy == EfficientSortStrategy::Binary) { // binary search
it = std::partition_point(fronts.begin(), fronts.end(),
[&](auto const& f) { return dominated(f, i); });
it = std::partition_point(fronts.begin(), fronts.end(), [&](auto const& f) { return dominated(f, i); });
} else { // sequential search
it = std::find_if(fronts.begin(), fronts.end(),
[&](auto const& f) { return !dominated(f, i); });
it = std::find_if(fronts.begin(), fronts.end(), [&](auto const& f) { return !dominated(f, i); });
}
if (it == fronts.end()) {
fronts.push_back({i});
Expand Down
5 changes: 2 additions & 3 deletions source/operators/non_dominated_sorter/hierarchical_sort.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace Operon {
auto
HierarchicalSorter::Sort(Operon::Span<Operon::Individual const> pop, Operon::Scalar eps) const -> NondominatedSorterBase::Result
HierarchicalSorter::Sort(Operon::Span<Operon::Individual const> pop, Operon::Scalar /*unused*/) const -> NondominatedSorterBase::Result
{
std::deque<size_t> q(pop.size());
std::iota(q.begin(), q.end(), 0UL);
Expand All @@ -26,7 +26,7 @@ namespace Operon {
auto nonDominatedCount = 0UL;
while (q.size() > nonDominatedCount) {
auto qj = q.front(); q.pop_front();
if (ParetoDominance{}(pop[q1].Fitness, pop[qj].Fitness, eps) == Dominance::None) {
if (ParetoDominance{}(pop[q1].Fitness, pop[qj].Fitness) == Dominance::None) {
q.push_back(qj);
++nonDominatedCount;
} else {
Expand All @@ -44,4 +44,3 @@ namespace Operon {
}

} // namespace Operon

0 comments on commit 9f3ab08

Please sign in to comment.