From 9f3ab08f81e22f97886a67484bd9ef76dd0b45b7 Mon Sep 17 00:00:00 2001 From: Bogdan Burlacu Date: Mon, 16 Oct 2023 12:10:07 +0200 Subject: [PATCH] optimize non-dominated sorters and phase out eps param --- .../non_dominated_sorter/deductive_sort.cpp | 56 +++++++++++-------- .../non_dominated_sorter/efficient_sort.cpp | 10 ++-- .../hierarchical_sort.cpp | 5 +- 3 files changed, 38 insertions(+), 33 deletions(-) diff --git a/source/operators/non_dominated_sorter/deductive_sort.cpp b/source/operators/non_dominated_sorter/deductive_sort.cpp index 70a0e9ac..20d4a431 100644 --- a/source/operators/non_dominated_sorter/deductive_sort.cpp +++ b/source/operators/non_dominated_sorter/deductive_sort.cpp @@ -5,40 +5,48 @@ #include "operon/core/individual.hpp" namespace Operon { - - auto DeductiveSorter::Sort(Operon::Span pop, Operon::Scalar eps) const -> NondominatedSorterBase::Result + auto DeductiveSorter::Sort(Operon::Span pop, Operon::Scalar /*unused*/) const -> NondominatedSorterBase::Result { size_t n = 0; // total number of sorted solutions std::vector> fronts; - std::vector dominated(pop.size(), false); - std::vector sorted(pop.size(), false); - auto dominatedOrSorted = [&](size_t i) { return sorted[i] || dominated[i]; }; + + std::size_t constexpr d = std::numeric_limits::digits; + auto const s = static_cast(pop.size()); + auto const nb = s / d + (s % d != 0); + + std::vector dominated(nb); + std::vector 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 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); diff --git a/source/operators/non_dominated_sorter/efficient_sort.cpp b/source/operators/non_dominated_sorter/efficient_sort.cpp index dbfc1a40..f4f4f758 100644 --- a/source/operators/non_dominated_sorter/efficient_sort.cpp +++ b/source/operators/non_dominated_sorter/efficient_sort.cpp @@ -7,12 +7,12 @@ namespace Operon { template - inline auto EfficientSortImpl(Operon::Span pop, Operon::Scalar eps) -> NondominatedSorterBase::Result + inline auto EfficientSortImpl(Operon::Span 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; }); }; @@ -20,11 +20,9 @@ namespace Operon { 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}); diff --git a/source/operators/non_dominated_sorter/hierarchical_sort.cpp b/source/operators/non_dominated_sorter/hierarchical_sort.cpp index acadd39f..63c50eef 100644 --- a/source/operators/non_dominated_sorter/hierarchical_sort.cpp +++ b/source/operators/non_dominated_sorter/hierarchical_sort.cpp @@ -8,7 +8,7 @@ namespace Operon { auto - HierarchicalSorter::Sort(Operon::Span pop, Operon::Scalar eps) const -> NondominatedSorterBase::Result + HierarchicalSorter::Sort(Operon::Span pop, Operon::Scalar /*unused*/) const -> NondominatedSorterBase::Result { std::deque q(pop.size()); std::iota(q.begin(), q.end(), 0UL); @@ -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 { @@ -44,4 +44,3 @@ namespace Operon { } } // namespace Operon -