diff --git a/include/bounds.hpp b/include/bounds.hpp index 95cadd7..7495f6b 100644 --- a/include/bounds.hpp +++ b/include/bounds.hpp @@ -9,26 +9,27 @@ struct Population; namespace parameters { struct Parameters; + struct Settings; } namespace bounds { using Mask = Eigen::Array; - Mask is_out_of_bounds(const Vector& xi, const Vector& lb, const Vector& ub); - bool any_out_of_bounds(const Vector& xi, const Vector& lb, const Vector& ub); + Mask is_out_of_bounds(const Vector &xi, const Vector &lb, const Vector &ub); + bool any_out_of_bounds(const Vector &xi, const Vector &lb, const Vector &ub); struct BoundCorrection { virtual ~BoundCorrection() = default; - Vector lb, ub, db; + Vector db; Float diameter; size_t n_out_of_bounds = 0; bool has_bounds; - BoundCorrection(const Vector& lb, const Vector& ub) : lb(lb), ub(ub), db(ub - lb), - diameter((ub - lb).norm()), - has_bounds(true) + BoundCorrection(const Vector &lb, const Vector &ub) : db(ub - lb), + diameter((ub - lb).norm()), + has_bounds(true) { //! find a better way if (!std::isfinite(diameter)) @@ -38,13 +39,22 @@ namespace bounds } } - void correct(const Eigen::Index i, parameters::Parameters& p); + void correct(const Eigen::Index i, parameters::Parameters &p); - virtual Vector correct_x(const Vector& xi, const Mask& oob, const Float sigma) = 0; + virtual Vector correct_x( + const Vector &xi, + const Mask &oob, + const Float sigma, + const parameters::Settings &settings) = 0; - [[nodiscard]] Mask is_out_of_bounds(const Vector& xi) const; + [[nodiscard]] Mask is_out_of_bounds( + const Vector &xi, + const parameters::Settings &settings) const; - [[nodiscard]] Vector delta_out_of_bounds(const Vector& xi, const Mask& oob) const; + [[nodiscard]] Vector delta_out_of_bounds( + const Vector &xi, + const Mask &oob, + const parameters::Settings &settings) const; [[nodiscard]] bool any_out_of_bounds() const { @@ -56,7 +66,7 @@ namespace bounds { using BoundCorrection::BoundCorrection; - Vector correct_x(const Vector& xi, const Mask& oob, const Float sigma) override + Vector correct_x(const Vector &xi, const Mask &oob, const Float sigma, const parameters::Settings &settings) override { return xi; } @@ -73,14 +83,14 @@ namespace bounds COTN(Eigen::Ref lb, Eigen::Ref ub) : BoundCorrection(lb, ub), sampler(static_cast(lb.size()), rng::normal(0, 1.0 / 3.)) {} - Vector correct_x(const Vector& xi, const Mask& oob, const Float sigma) override; + Vector correct_x(const Vector &xi, const Mask &oob, const Float sigma, const parameters::Settings &settings) override; }; struct Mirror final : BoundCorrection { using BoundCorrection::BoundCorrection; - Vector correct_x(const Vector& xi, const Mask& oob, const Float sigma) override; + Vector correct_x(const Vector &xi, const Mask &oob, const Float sigma, const parameters::Settings &settings) override; }; struct UniformResample final : BoundCorrection @@ -89,24 +99,24 @@ namespace bounds UniformResample(Eigen::Ref lb, Eigen::Ref ub) : BoundCorrection(lb, ub), sampler(static_cast(lb.size())) {} - Vector correct_x(const Vector& xi, const Mask& oob, const Float sigma) override; + Vector correct_x(const Vector &xi, const Mask &oob, const Float sigma, const parameters::Settings &settings) override; }; struct Saturate final : BoundCorrection { using BoundCorrection::BoundCorrection; - Vector correct_x(const Vector& xi, const Mask& oob, const Float sigma) override; + Vector correct_x(const Vector &xi, const Mask &oob, const Float sigma, const parameters::Settings &settings) override; }; struct Toroidal final : BoundCorrection { using BoundCorrection::BoundCorrection; - Vector correct_x(const Vector& xi, const Mask& oob, const Float sigma) override; + Vector correct_x(const Vector &xi, const Mask &oob, const Float sigma, const parameters::Settings &settings) override; }; - inline std::shared_ptr get(const parameters::CorrectionMethod& m, const Vector& lb, const Vector& ub) + inline std::shared_ptr get(const parameters::CorrectionMethod &m, const Vector &lb, const Vector &ub) { using namespace parameters; switch (m) diff --git a/include/es.hpp b/include/es.hpp index fdc0c64..ecbd611 100644 --- a/include/es.hpp +++ b/include/es.hpp @@ -3,6 +3,7 @@ #include "sampling.hpp" #include "stats.hpp" #include "bounds.hpp" +#include "settings.hpp" namespace es { @@ -20,8 +21,15 @@ namespace es x(x0), f(f0), t(1), budget(budget), target(target), rejection_sampling(modules.bound_correction == parameters::CorrectionMethod::RESAMPLE), sampler(sampling::get(d, modules, 1)), - corrector(bounds::get(modules.bound_correction, Vector::Ones(d) * -5.0, Vector::Ones(d) * 5.0)) + corrector(bounds::get(modules.bound_correction, Vector::Ones(d) * -5.0, Vector::Ones(d) * 5.0)), + settings{d} { + settings.modules = modules; + settings.x0 = x0; + settings.budget = budget; + settings.target = target; + settings.lb = Vector::Ones(d) * -5.0; + settings.ub = Vector::Ones(d) * 5.0; } Vector sample(); @@ -40,6 +48,7 @@ namespace es std::shared_ptr sampler; std::shared_ptr corrector; + parameters::Settings settings; }; struct MuCommaLambdaES @@ -65,10 +74,18 @@ namespace es sampler(sampling::get(d, modules, lambda)), sigma_sampler(std::make_shared(d)), rejection_sampling(modules.bound_correction == parameters::CorrectionMethod::RESAMPLE), - corrector(bounds::get(modules.bound_correction, Vector::Ones(d) * -5.0, Vector::Ones(d) * 5.0)) + corrector(bounds::get(modules.bound_correction, Vector::Ones(d) * -5.0, Vector::Ones(d) * 5.0)), + settings(d) { // tau = 1.0 / sampler->expected_length(); // tau_i = 1.0 / std::sqrt(sampler->expected_length()); + + settings.modules = modules; + settings.x0 = x0; + settings.budget = budget; + settings.target = target; + settings.lb = Vector::Ones(d) * -5.0; + settings.ub = Vector::Ones(d) * 5.0; } Vector sample(const Vector si); @@ -100,5 +117,6 @@ namespace es bool rejection_sampling; std::shared_ptr corrector; + parameters::Settings settings; }; } \ No newline at end of file diff --git a/src/bounds.cpp b/src/bounds.cpp index 10e43ed..31dae20 100644 --- a/src/bounds.cpp +++ b/src/bounds.cpp @@ -9,8 +9,8 @@ static Float modulo2(const int x) namespace bounds { - - Mask is_out_of_bounds(const Vector &xi, const Vector &lb, const Vector &ub) + + Mask is_out_of_bounds(const Vector &xi, const Vector &lb, const Vector &ub) { return xi.array() < lb.array() || xi.array() > ub.array(); } @@ -20,69 +20,66 @@ namespace bounds return bounds::is_out_of_bounds(xi, lb, ub).any(); } - - Mask BoundCorrection::is_out_of_bounds(const Vector& xi) const + Mask BoundCorrection::is_out_of_bounds(const Vector &xi, const parameters::Settings &settings) const { - return bounds::is_out_of_bounds(xi, lb, ub); + return bounds::is_out_of_bounds(xi, settings.lb, settings.ub); } - Vector BoundCorrection::delta_out_of_bounds(const Vector& xi, const Mask& oob) const + Vector BoundCorrection::delta_out_of_bounds(const Vector &xi, const Mask &oob, const parameters::Settings &settings) const { - return (oob).select((xi - lb).cwiseQuotient(db), xi);; + return (oob).select((xi - settings.lb).cwiseQuotient(db), xi); + ; } - void BoundCorrection::correct(const Eigen::Index i, parameters::Parameters& p) + void BoundCorrection::correct(const Eigen::Index i, parameters::Parameters &p) { if (!has_bounds) return; - const auto oob = is_out_of_bounds(p.pop.X.col(i)); + const auto oob = is_out_of_bounds(p.pop.X.col(i), p.settings); if (oob.any()) { n_out_of_bounds++; if (p.settings.modules.bound_correction == parameters::CorrectionMethod::NONE) return; - p.pop.X.col(i) = correct_x(p.pop.X.col(i), oob, p.mutation->sigma); + p.pop.X.col(i) = correct_x(p.pop.X.col(i), oob, p.mutation->sigma, p.settings); p.pop.Y.col(i) = p.adaptation->invert_x(p.pop.X.col(i), p.pop.s(i)); p.pop.Z.col(i) = p.adaptation->invert_y(p.pop.Y.col(i)); } } - Vector COTN::correct_x(const Vector& xi, const Mask& oob, const Float sigma) + Vector COTN::correct_x(const Vector &xi, const Mask &oob, const Float sigma, const parameters::Settings &settings) { - const Vector y = delta_out_of_bounds(xi, oob); + const Vector y = delta_out_of_bounds(xi, oob, settings); return (oob).select( - lb.array() + db.array() * ((y.array() > 0).cast() - (sigma * sampler().array().abs())).abs(), y); + settings.lb.array() + db.array() * ((y.array() > 0).cast() - (sigma * sampler().array().abs())).abs(), y); } - - Vector Mirror::correct_x(const Vector& xi, const Mask& oob, const Float sigma) + Vector Mirror::correct_x(const Vector &xi, const Mask &oob, const Float sigma, const parameters::Settings &settings) { - const Vector y = delta_out_of_bounds(xi, oob); + const Vector y = delta_out_of_bounds(xi, oob, settings); return (oob).select( - lb.array() + db.array() * (y.array() - y.array().floor() - y.array().floor().unaryExpr(&modulo2)). - abs(), + settings.lb.array() + db.array() * (y.array() - y.array().floor() - y.array().floor().unaryExpr(&modulo2)).abs(), y); } - - Vector UniformResample::correct_x(const Vector& xi, const Mask& oob, const Float sigma) + Vector UniformResample::correct_x(const Vector &xi, const Mask &oob, const Float sigma, const parameters::Settings &settings) { - return (oob).select(lb + sampler().cwiseProduct(db), xi); + return (oob).select(settings.lb + sampler().cwiseProduct(db), xi); } - Vector Saturate::correct_x(const Vector& xi, const Mask& oob, const Float sigma) + Vector Saturate::correct_x(const Vector &xi, const Mask &oob, const Float sigma, const parameters::Settings &settings) { - const Vector y = delta_out_of_bounds(xi, oob); + const Vector y = delta_out_of_bounds(xi, oob, settings); return (oob).select( - lb.array() + db.array() * (y.array() > 0).cast(), y); + settings.lb.array() + db.array() * (y.array() > 0).cast(), y); } - Vector Toroidal::correct_x(const Vector& xi, const Mask& oob, const Float sigma) + Vector Toroidal::correct_x(const Vector &xi, const Mask &oob, const Float sigma, const parameters::Settings &settings) { - const Vector y = delta_out_of_bounds(xi, oob); + const Vector y = delta_out_of_bounds(xi, oob, settings); return (oob).select( - lb.array() + db.array() * (y.array() - y.array().floor()).abs(), y); + settings.lb.array() + db.array() * (y.array() - y.array().floor()).abs(), y); } } diff --git a/src/es.cpp b/src/es.cpp index bb35d11..086514f 100644 --- a/src/es.cpp +++ b/src/es.cpp @@ -13,11 +13,11 @@ namespace es const Vector z = (*sampler)(); x1 = x + sigma * z; - const auto mask = corrector->is_out_of_bounds(x1); + const auto mask = corrector->is_out_of_bounds(x, settings); if (mask.any()) - x1 = corrector->correct_x(x1, mask, sigma); + x1 = corrector->correct_x(x1, mask, sigma, settings); - } while (rejection_sampling && n_rej++ < 5*d && bounds::any_out_of_bounds(x1, corrector->lb, corrector->ub) ); + } while (rejection_sampling && n_rej++ < 5*d && bounds::any_out_of_bounds(x1, settings.lb, settings.ub) ); return x1; } @@ -49,11 +49,11 @@ namespace es const Vector z = (*sampler)(); x = m.array() + (si.array() * z.array()); - const auto mask = corrector->is_out_of_bounds(x); + const auto mask = corrector->is_out_of_bounds(x, settings); if (mask.any()) - x = corrector->correct_x(x, mask, si.mean()); + x = corrector->correct_x(x, mask, si.mean(), settings); - } while (rejection_sampling && n_rej++ < 5*d && bounds::any_out_of_bounds(x, corrector->lb, corrector->ub)); + } while (rejection_sampling && n_rej++ < 5*d && bounds::any_out_of_bounds(x, settings.lb, settings.ub)); return x; } diff --git a/src/interface.cpp b/src/interface.cpp index 5222575..9f82e5e 100644 --- a/src/interface.cpp +++ b/src/interface.cpp @@ -745,16 +745,15 @@ void define_bounds(py::module& main) using namespace bounds; py::class_>(m, "BoundCorrection") - .def_readwrite("lb", &BoundCorrection::lb) - .def_readwrite("ub", &BoundCorrection::ub) .def_readwrite("db", &BoundCorrection::db) .def_readwrite("diameter", &BoundCorrection::diameter) .def_readwrite("has_bounds", &BoundCorrection::has_bounds) .def_readonly("n_out_of_bounds", &BoundCorrection::n_out_of_bounds) .def("correct", &BoundCorrection::correct, - py::arg("population"), py::arg("m")) - .def("delta_out_of_bounds", &BoundCorrection::delta_out_of_bounds, py::arg("xi"), py::arg("oob")) - .def("is_out_of_bounds", &BoundCorrection::is_out_of_bounds, py::arg("xi")) + py::arg("index"), py::arg("parameters")) + .def("correct_x", &BoundCorrection::correct_x, py::arg("xi"), py::arg("oob"), py::arg("sigma"), py::arg("settings")) + .def("delta_out_of_bounds", &BoundCorrection::delta_out_of_bounds, py::arg("xi"), py::arg("oob"), py::arg("settings")) + .def("is_out_of_bounds", &BoundCorrection::is_out_of_bounds, py::arg("xi"), py::arg("settings")) ; py::class_>(m, "Resample") diff --git a/src/mutation.cpp b/src/mutation.cpp index 78cb0e8..c1e949e 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -5,8 +5,8 @@ namespace mutation { - Vector ThresholdConvergence::scale(const Vector& zi, const Float diameter, const size_t budget, - const size_t evaluations) + Vector ThresholdConvergence::scale(const Vector &zi, const Float diameter, const size_t budget, + const size_t evaluations) { const Float t = init_threshold * diameter * pow(static_cast(budget - evaluations) / static_cast(budget), decay_factor); @@ -15,12 +15,12 @@ namespace mutation return zi; } - bool SequentialSelection::break_conditions(const size_t i, const Float f, Float fopt, const parameters::Mirror& m) + bool SequentialSelection::break_conditions(const size_t i, const Float f, Float fopt, const parameters::Mirror &m) { return (f < fopt) and (i >= seq_cutoff) and (m != parameters::Mirror::PAIRWISE or i % 2 == 0); } - void Strategy::mutate(FunctionType& objective, const size_t n_offspring, parameters::Parameters& p) + void Strategy::mutate(FunctionType &objective, const size_t n_offspring, parameters::Parameters &p) { ss->sample(sigma, p.pop, p.weights.beta); p.bounds->n_out_of_bounds = 0; @@ -32,16 +32,17 @@ namespace mutation do { p.pop.t(i) = p.stats.t; - const auto& zi = (*p.sampler)(); - const auto& zi_scaled = p.mutation->tc->scale( - zi, p.bounds->diameter, p.settings.budget, p.stats.evaluations - ); + const auto &zi = (*p.sampler)(); + const auto &zi_scaled = p.mutation->tc->scale( + zi, p.bounds->diameter, p.settings.budget, p.stats.evaluations); p.pop.Z.col(i).noalias() = zi_scaled; p.pop.Y.col(i).noalias() = p.adaptation->compute_y(p.pop.Z.col(i)); p.pop.X.col(i).noalias() = p.pop.Y.col(i) * p.pop.s(i) + p.adaptation->m; p.bounds->correct(i, p); } while ( - (p.settings.modules.bound_correction == parameters::CorrectionMethod::RESAMPLE && n_rej++ < 5 * p.settings.dim && p.bounds->is_out_of_bounds(p.pop.X.col(i)).any()) || p.repelling->is_rejected(p.pop.X.col(i), p)); + (p.settings.modules.bound_correction == parameters::CorrectionMethod::RESAMPLE && + n_rej++ < 5 * p.settings.dim && p.bounds->is_out_of_bounds(p.pop.X.col(i), p.settings).any()) || + p.repelling->is_rejected(p.pop.X.col(i), p)); p.pop.f(i) = objective(p.pop.X.col(i)); p.stats.evaluations++; @@ -50,13 +51,12 @@ namespace mutation // TODO: We should renormalize the weights break; } - } } - void CSA::adapt(const parameters::Weights& w, std::shared_ptr adaptation, - Population& pop, - const Population& old_pop, const parameters::Stats& stats, const size_t lambda) + void CSA::adapt(const parameters::Weights &w, std::shared_ptr adaptation, + Population &pop, + const Population &old_pop, const parameters::Stats &stats, const size_t lambda) { Float l = (w.cs / w.damps) * ((adaptation->ps.norm() / w.expected_length_z) - 1); @@ -65,8 +65,7 @@ namespace mutation sigma *= std::exp(l); } - - void TPA::mutate(FunctionType& objective, const size_t n_offspring_, parameters::Parameters& p) + void TPA::mutate(FunctionType &objective, const size_t n_offspring_, parameters::Parameters &p) { Strategy::mutate(objective, n_offspring_, p); @@ -76,25 +75,25 @@ namespace mutation this->rank_tpa = f_neg < f_pos ? -a_tpa : a_tpa + b_tpa; } - void TPA::adapt(const parameters::Weights& w, std::shared_ptr adaptation, - Population& pop, - const Population& old_pop, const parameters::Stats& stats, const size_t lambda) + void TPA::adapt(const parameters::Weights &w, std::shared_ptr adaptation, + Population &pop, + const Population &old_pop, const parameters::Stats &stats, const size_t lambda) { s = ((1.0 - w.cs) * s) + (w.cs * this->rank_tpa); sigma *= std::exp(s); } //! Assumes the vector to be arready sorted - Float median(const Vector& x) + Float median(const Vector &x) { if (x.size() % 2 == 0) return (x(x.size() / 2) + x(x.size() / 2 - 1)) / 2.0; return x(x.size() / 2); } - void MSR::adapt(const parameters::Weights& w, std::shared_ptr adaptation, - Population& pop, - const Population& old_pop, const parameters::Stats& stats, const size_t lamb) + void MSR::adapt(const parameters::Weights &w, std::shared_ptr adaptation, + Population &pop, + const Population &old_pop, const parameters::Stats &stats, const size_t lamb) { const auto n = std::min(pop.n_finite(), old_pop.n_finite()); if (n != 0) @@ -108,12 +107,12 @@ namespace mutation } //! Returns the indices of the elements of query in database - Vector searchsorted(const Vector& query, const Vector& database) + Vector searchsorted(const Vector &query, const Vector &database) { Vector res(query.size()); auto i = 0; - for (const auto& xi : query) + for (const auto &xi : query) { auto it = std::find(std::begin(database), std::end(database), xi); res(i++) = static_cast(std::distance(std::begin(database), it)); @@ -121,9 +120,9 @@ namespace mutation return res; } - void PSR::adapt(const parameters::Weights& w, std::shared_ptr adaptation, - Population& pop, - const Population& old_pop, const parameters::Stats& stats, const size_t lambda) + void PSR::adapt(const parameters::Weights &w, std::shared_ptr adaptation, + Population &pop, + const Population &old_pop, const parameters::Stats &stats, const size_t lambda) { const auto n = std::min(pop.n_finite(), old_pop.n_finite()); if (n != 0) @@ -148,75 +147,73 @@ namespace mutation } } - void XNES::adapt(const parameters::Weights& w, std::shared_ptr adaptation, - Population& pop, - const Population& old_pop, const parameters::Stats& stats, const size_t lambda) + void XNES::adapt(const parameters::Weights &w, std::shared_ptr adaptation, + Population &pop, + const Population &old_pop, const parameters::Stats &stats, const size_t lambda) { if (const auto dynamic = std::dynamic_pointer_cast(adaptation)) { sigma *= std::exp(w.cs / 2.0 * dynamic->sigma_g); - return; + return; } - + const Float z = ((pop.Z).colwise().squaredNorm().array() - adaptation->dd).matrix() * w.clipped(); sigma *= std::exp((w.cs / std::sqrt(adaptation->dd)) * z); } - void MXNES::adapt(const parameters::Weights& w, std::shared_ptr adaptation, - Population& pop, - const Population& old_pop, const parameters::Stats& stats, const size_t lambda) + void MXNES::adapt(const parameters::Weights &w, std::shared_ptr adaptation, + Population &pop, + const Population &old_pop, const parameters::Stats &stats, const size_t lambda) { const Float delta = (w.mueff * adaptation->dz.squaredNorm() - adaptation->dd); sigma *= std::exp((w.cs / adaptation->dd) * delta); } - void LPXNES::adapt(const parameters::Weights& w, std::shared_ptr adaptation, - Population& pop, - const Population& old_pop, const parameters::Stats& stats, const size_t lambda) + void LPXNES::adapt(const parameters::Weights &w, std::shared_ptr adaptation, + Population &pop, + const Population &old_pop, const parameters::Stats &stats, const size_t lambda) { const Float rel_log = (pop.s.array() / sigma).log().matrix().dot(w.clipped()); sigma *= std::exp(w.cs * rel_log); } - void SR::adapt(const parameters::Weights& w, std::shared_ptr adaptation, - Population& pop, - const Population& old_pop, const parameters::Stats& stats, const size_t lambda) + void SR::adapt(const parameters::Weights &w, std::shared_ptr adaptation, + Population &pop, + const Population &old_pop, const parameters::Stats &stats, const size_t lambda) { sigma *= std::exp((1 / w.damps) * ((stats.success_ratio - tgt_success_ratio) / (1.0 - tgt_success_ratio))); } - - void SA::mutate(FunctionType& objective, const size_t n_offspring, parameters::Parameters& p) + void SA::mutate(FunctionType &objective, const size_t n_offspring, parameters::Parameters &p) { Strategy::mutate(objective, n_offspring, p); mean_sigma = std::exp(p.pop.s.array().log().mean()); } - void SA::adapt(const parameters::Weights& w, std::shared_ptr adaptation, - Population& pop, - const Population& old_pop, const parameters::Stats& stats, const size_t lambda) + void SA::adapt(const parameters::Weights &w, std::shared_ptr adaptation, + Population &pop, + const Population &old_pop, const parameters::Stats &stats, const size_t lambda) { - const auto& sigma_l = pop.s.topRows(w.positive.rows()); + const auto &sigma_l = pop.s.topRows(w.positive.rows()); sigma = std::exp((w.positive.array() * sigma_l.array().log()).sum()) / mean_sigma; } - - std::shared_ptr get(const parameters::Modules& m, const size_t mu, const Float d, const Float sigma) + std::shared_ptr get(const parameters::Modules &m, const size_t mu, const Float d, const Float sigma) { using namespace parameters; auto tc = m.threshold_convergence - ? std::make_shared() - : std::make_shared(); + ? std::make_shared() + : std::make_shared(); auto sq = m.sequential_selection - ? std::make_shared(m.mirrored, mu) - : std::make_shared(m.mirrored, mu); + ? std::make_shared(m.mirrored, mu) + : std::make_shared(m.mirrored, mu); auto ss = (m.sample_sigma or m.ssa == StepSizeAdaptation::LPXNES or m.ssa == StepSizeAdaptation::SA) - ? std::make_shared(d) - : std::make_shared(d); + ? std::make_shared(d) + : std::make_shared(d); switch (m.ssa) {