diff --git a/include/common.hpp b/include/common.hpp index 47d125c..1aa6e5e 100644 --- a/include/common.hpp +++ b/include/common.hpp @@ -35,16 +35,9 @@ using FunctionType = std::function; namespace constants { - extern Float max_dsigma; - extern Float min_dsigma; - extern Float tol_condition_cov; - extern Float stagnation_quantile; - extern Float sigma_threshold; extern size_t cache_max_doubles; extern size_t cache_min_samples; extern bool cache_samples; - extern Float lb_sigma; - extern Float ub_sigma; extern bool clip_sigma; } diff --git a/include/restart_criteria.hpp b/include/restart_criteria.hpp index eb6dea7..2d84462 100644 --- a/include/restart_criteria.hpp +++ b/include/restart_criteria.hpp @@ -17,6 +17,8 @@ namespace restart Criterion(const std::string& name): met(false), name(name) {} + virtual ~Criterion() = default; + void reset(const parameters::Parameters &p); virtual void update(const parameters::Parameters &p) = 0; @@ -69,9 +71,17 @@ namespace restart void on_reset(const parameters::Parameters &p) override; }; - struct SigmaOutOfBounds: Criterion + struct MaxSigma: Criterion + { + static inline Float tolerance = 1e4; + MaxSigma(): Criterion("MaxSigma"){} + void update(const parameters::Parameters &p) override; + }; + + struct MinSigma: Criterion { - SigmaOutOfBounds(): Criterion("SigmaOutOfBounds"){} + static inline Float tolerance = 1e-20; + MinSigma(): Criterion("MinSigma"){} void update(const parameters::Parameters &p) override; }; @@ -94,6 +104,7 @@ namespace restart struct TolX: Criterion { + static inline Float tolerance = 10e-12; Vector tolx_vector; TolX(): Criterion("TolX"){} void update(const parameters::Parameters &p) override; @@ -103,12 +114,14 @@ namespace restart struct MaxDSigma: Criterion { + static inline Float tolerance = std::pow(10., 20.); MaxDSigma(): Criterion("MaxDSigma"){} void update(const parameters::Parameters &p) override; }; struct MinDSigma: Criterion { + static inline Float tolerance = 1e-8; MinDSigma(): Criterion("MinDSigma"){} void update(const parameters::Parameters &p) override; }; @@ -116,24 +129,29 @@ namespace restart struct ConditionC: Criterion { + static inline Float tolerance = std::pow(10., 14.); ConditionC(): Criterion("ConditionC"){} void update(const parameters::Parameters &p) override; }; struct NoEffectAxis: Criterion { + static inline Float tolerance = 0.; NoEffectAxis(): Criterion("NoEffectAxis"){} void update(const parameters::Parameters &p) override; }; struct NoEffectCoord: Criterion { + static inline Float tolerance = 0.; NoEffectCoord(): Criterion("NoEffectCoord"){} void update(const parameters::Parameters &p) override; }; struct Stagnation: Criterion { + static inline Float tolerance = 0.3; + size_t n_stagnation; std::vector median_fitnesses; std::vector best_fitnesses; diff --git a/modcma/c_maes/cmaescpp/__init__.pyi b/modcma/c_maes/cmaescpp/__init__.pyi index b731cbe..c8ae86c 100644 --- a/modcma/c_maes/cmaescpp/__init__.pyi +++ b/modcma/c_maes/cmaescpp/__init__.pyi @@ -1,4 +1,4 @@ -from typing import Any, Callable, List, Optional, Union, overload, ClassVar +from typing import Any, Callable, List, overload, ClassVar import numpy from . import ( @@ -21,30 +21,10 @@ class constants: cache_max_doubles: ClassVar[int] = ... cache_min_samples: ClassVar[int] = ... cache_samples: ClassVar[bool] = ... - sigma_threshold: ClassVar[float] = ... - stagnation_quantile: ClassVar[float] = ... - tol_condition_cov: ClassVar[float] = ... - tol_min_sigma: ClassVar[float] = ... - tolup_sigma: ClassVar[float] = ... + clip_sigma: ClassVar[bool] = ... def __init__(self, *args, **kwargs) -> None: ... -class ModularCMAES: - @overload - def __init__(self, parameters: Parameters) -> None: ... - @overload - def __init__(self, dimension: int) -> None: ... - @overload - def __init__(self, settings: parameters.Settings) -> None: ... - def adapt(self, objective: Callable[[numpy.ndarray], float]) -> None: ... - def break_conditions(self) -> bool: ... - def mutate(self, arg0: Callable[[numpy.ndarray], float]) -> None: ... - def recombine(self) -> None: ... - def run(self, objective: Callable[[numpy.ndarray], float]) -> None: ... - def select(self) -> None: ... - def step(self, objective: Callable[[numpy.ndarray], float]) -> bool: ... - def __call__(self, objective: Callable[[numpy.ndarray], float]) -> None: ... - @property - def p(self) -> Parameters: ... + class Population: X: numpy.ndarray @@ -108,17 +88,21 @@ class Parameters: def start( self, objective: Callable[[numpy.ndarray[numpy.float64[m, 1]]], float] ) -> None: ... - -class constants: - cache_max_doubles: ClassVar[int] = ... - cache_min_samples: ClassVar[int] = ... - cache_samples: ClassVar[bool] = ... - clip_sigma: ClassVar[bool] = ... - lb_sigma: ClassVar[float] = ... - max_dsigma: ClassVar[float] = ... - min_dsigma: ClassVar[float] = ... - sigma_threshold: ClassVar[float] = ... - stagnation_quantile: ClassVar[float] = ... - tol_condition_cov: ClassVar[float] = ... - ub_sigma: ClassVar[float] = ... - def __init__(self, *args, **kwargs) -> None: ... + +class ModularCMAES: + @overload + def __init__(self, parameters: Parameters) -> None: ... + @overload + def __init__(self, dimension: int) -> None: ... + @overload + def __init__(self, settings: parameters.Settings) -> None: ... + def adapt(self) -> None: ... + def break_conditions(self) -> bool: ... + def mutate(self, arg0: Callable[[numpy.ndarray], float]) -> None: ... + def recombine(self) -> None: ... + def run(self, objective: Callable[[numpy.ndarray], float]) -> None: ... + def select(self) -> None: ... + def step(self, objective: Callable[[numpy.ndarray], float]) -> bool: ... + def __call__(self, objective: Callable[[numpy.ndarray], float]) -> None: ... + @property + def p(self) -> Parameters: ... diff --git a/modcma/c_maes/cmaescpp/restart.pyi b/modcma/c_maes/cmaescpp/restart.pyi index 42c90e6..17ddaf4 100644 --- a/modcma/c_maes/cmaescpp/restart.pyi +++ b/modcma/c_maes/cmaescpp/restart.pyi @@ -1,15 +1,35 @@ +from typing import ClassVar + import numpy class Strategy: def __init__(self, *args, **kwargs) -> None: ... def update(self, parameters) -> float: ... +class BIPOP(Strategy): + budget: int + budget_large: int + budget_small: int + lambda_init: int + lambda_large: int + lambda_small: int + mu_factor: float + def __init__(self, *args, **kwargs) -> None: ... + def large(self) -> bool: ... + @property + def used_budget(self) -> int: ... + +class IPOP(Strategy): + ipop_factor: float + def __init__(self, *args, **kwargs) -> None: ... + class Criterion: last_restart: int met: bool name: str def __init__(self, *args, **kwargs) -> None: ... def reset(self, parameters) -> None: ... + def on_reset(self, parameters) -> None: ... def update(self, parameters) -> None: ... class Criteria: @@ -20,20 +40,8 @@ class Criteria: @property def any(self) -> bool: ... -class BIPOP(Strategy): - budget: int - budget_large: int - budget_small: int - lambda_init: int - lambda_large: int - lambda_small: int - mu_factor: float - def __init__(self, *args, **kwargs) -> None: ... - def large(self) -> bool: ... - @property - def used_budget(self) -> int: ... - class ConditionC(Criterion): + tolerance: ClassVar[float] = ... def __init__(self) -> None: ... class ExceededMaxIter(Criterion): @@ -42,24 +50,24 @@ class ExceededMaxIter(Criterion): class FlatFitness(Criterion): flat_fitness_index: int - flat_fitnesses: numpy.ndarray[numpy.int32[m, 1]] + flat_fitnesses: numpy.ndarray[numpy.int32] max_flat_fitness: int def __init__(self) -> None: ... -class IPOP(Strategy): - ipop_factor: float - def __init__(self, *args, **kwargs) -> None: ... - class MaxDSigma(Criterion): + tolerance: ClassVar[float] = ... def __init__(self) -> None: ... class MinDSigma(Criterion): + tolerance: ClassVar[float] = ... def __init__(self) -> None: ... class NoEffectAxis(Criterion): + tolerance: ClassVar[float] = ... def __init__(self) -> None: ... class NoEffectCoord(Criterion): + tolerance: ClassVar[float] = ... def __init__(self) -> None: ... class NoImprovement(Criterion): @@ -67,17 +75,24 @@ class NoImprovement(Criterion): n_bin: int def __init__(self) -> None: ... -class SigmaOutOfBounds(Criterion): +class MinSigma(Criterion): + tolerance: ClassVar[float] = ... + def __init__(self) -> None: ... + +class MaxSigma(Criterion): + tolerance: ClassVar[float] = ... def __init__(self) -> None: ... class Stagnation(Criterion): best_fitnesses: list[float] median_fitnesses: list[float] n_stagnation: int + tolerance: ClassVar[float] = ... def __init__(self) -> None: ... class TolX(Criterion): - tolx_vector: numpy.ndarray[numpy.float64[m, 1]] + tolx_vector: numpy.ndarray[numpy.float64] + tolerance: ClassVar[float] = ... def __init__(self) -> None: ... class UnableToAdapt(Criterion): diff --git a/src/common.cpp b/src/common.cpp index 9498511..09478e8 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -11,16 +11,9 @@ std::ostream& operator<<(std::ostream& os, const std::vector& x) namespace constants { - Float max_dsigma = std::pow(10., 20.); - Float min_dsigma = 1e-8; - Float tol_condition_cov = pow(10., 14.); - Float stagnation_quantile = 0.3; - Float sigma_threshold = 1e-4; size_t cache_max_doubles = 2'000'000; size_t cache_min_samples = 128; bool cache_samples = false; - Float lb_sigma = 1e-20; - Float ub_sigma = 1e4; bool clip_sigma = false; } diff --git a/src/interface.cpp b/src/interface.cpp index 24789ec..9f138a3 100644 --- a/src/interface.cpp +++ b/src/interface.cpp @@ -890,36 +890,7 @@ class constants_w void define_constants(py::module &m) { py::class_(m, "constants") - .def_property_static( - "max_dsigma", - [](py::object) - { return constants::max_dsigma; }, - [](py::object, Float a) - { constants::max_dsigma = a; }) - .def_property_static( - "min_dsigma", - [](py::object) - { return constants::min_dsigma; }, - [](py::object, Float a) - { constants::min_dsigma = a; }) - .def_property_static( - "tol_condition_cov", - [](py::object) - { return constants::tol_condition_cov; }, - [](py::object, Float a) - { constants::tol_condition_cov = a; }) - .def_property_static( - "stagnation_quantile", - [](py::object) - { return constants::stagnation_quantile; }, - [](py::object, Float a) - { constants::stagnation_quantile = a; }) - .def_property_static( - "sigma_threshold", - [](py::object) - { return constants::sigma_threshold; }, - [](py::object, Float a) - { constants::sigma_threshold = a; }) + .def_property_static( "cache_max_doubles", [](py::object) @@ -944,34 +915,40 @@ void define_constants(py::module &m) { return constants::clip_sigma; }, [](py::object, bool a) { constants::clip_sigma = a; }) - .def_property_static( - "lb_sigma", - [](py::object) - { return constants::lb_sigma; }, - [](py::object, Float a) - { constants::lb_sigma = a; }) - - .def_property_static( - "ub_sigma", - [](py::object) - { return constants::ub_sigma; }, - [](py::object, Float a) - { constants::ub_sigma = a; }); + ; } +struct PyCriterion: restart::Criterion +{ + PyCriterion(const std::string& name): restart::Criterion(name) {} + + void update(const parameters::Parameters &p) override + { + PYBIND11_OVERRIDE_PURE(void, restart::Criterion, update, p); + } + + void on_reset(const parameters::Parameters &p) override + { + PYBIND11_OVERRIDE(void, restart::Criterion, on_reset, p); + } +}; + void define_restart_criteria(py::module &main) { auto m = main.def_submodule("restart"); using namespace restart; - py::class_>(m, "Criterion") - .def("reset", &Criterion::reset, py::arg("parameters")) + py::class_>(m, "Criterion") + .def(py::init(), py::arg("name")) + .def("on_reset", &Criterion::on_reset, py::arg("parameters")) .def("update", &Criterion::update, py::arg("parameters")) + .def("reset", &Criterion::reset, py::arg("parameters")) .def_readwrite("met", &Criterion::met) .def_readwrite("name", &Criterion::name) .def_readwrite("last_restart", &Criterion::last_restart) .def("__repr__", [](Criterion &self) - { return "<" + self.name + " met: " + std::to_string(self.met) + ">"; }); + { return "<" + self.name + " met: " + std::to_string(self.met) + ">"; }); + ; py::class_>(m, "ExceededMaxIter") .def(py::init<>()) @@ -982,8 +959,13 @@ void define_restart_criteria(py::module &main) .def_readwrite("n_bin", &NoImprovement::n_bin) .def_readwrite("best_fitnesses", &NoImprovement::best_fitnesses); - py::class_>(m, "SigmaOutOfBounds") - .def(py::init<>()); + py::class_>(m, "MaxSigma") + .def(py::init<>()) + .def_readwrite_static("tolerance", &MaxSigma::tolerance); + + py::class_>(m, "MinSigma") + .def(py::init<>()) + .def_readwrite_static("tolerance", &MinSigma::tolerance); py::class_>(m, "UnableToAdapt") .def(py::init<>()); @@ -996,34 +978,44 @@ void define_restart_criteria(py::module &main) py::class_>(m, "TolX") .def(py::init<>()) - .def_readwrite("tolx_vector", &TolX::tolx_vector); + .def_readwrite("tolx_vector", &TolX::tolx_vector) + .def_readwrite_static("tolerance", &TolX::tolerance) + ; py::class_>(m, "MaxDSigma") - .def(py::init<>()); + .def(py::init<>()) + .def_readwrite_static("tolerance", &MaxDSigma::tolerance); py::class_>(m, "MinDSigma") - .def(py::init<>()); + .def(py::init<>()) + .def_readwrite_static("tolerance", &MinDSigma::tolerance); py::class_>(m, "ConditionC") - .def(py::init<>()); + .def(py::init<>()) + .def_readwrite_static("tolerance", &ConditionC::tolerance); py::class_>(m, "NoEffectAxis") - .def(py::init<>()); + .def(py::init<>()) + .def_readwrite_static("tolerance", &NoEffectAxis::tolerance) + ; py::class_>(m, "NoEffectCoord") - .def(py::init<>()); + .def(py::init<>()) + .def_readwrite_static("tolerance", &NoEffectCoord::tolerance); py::class_>(m, "Stagnation") .def(py::init<>()) .def_readwrite("n_stagnation", &Stagnation::n_stagnation) .def_readwrite("median_fitnesses", &Stagnation::median_fitnesses) - .def_readwrite("best_fitnesses", &Stagnation::best_fitnesses); + .def_readwrite("best_fitnesses", &Stagnation::best_fitnesses) + .def_readwrite_static("tolerance", &Stagnation::tolerance); py::class_(m, "Criteria") .def_readwrite("items", &Criteria::items) .def("reset", &Criteria::reset, py::arg("parameters")) .def("update", &Criteria::update, py::arg("parameters")) .def_readonly("any", &Criteria::any); + } void define_restart_strategy(py::module &main) diff --git a/src/mutation.cpp b/src/mutation.cpp index f82a3f9..d647a02 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -220,7 +220,7 @@ namespace mutation default: case StepSizeAdaptation::CSA: cs = cs0.value_or((mueff + 2.0) / (d + mueff + 5.0)); - damps = 1.0 + (2.0 * std::max(0.0, sqrt((mueff - 1.0) / (d + 1)) - 1) + cs); + damps = 1.0 + (2.0 * std::max(Float{0.0}, sqrt((mueff - 1.0) / (d + 1)) - 1) + cs); return std::make_shared(tc, sq, ss, cs, damps, sigma, expected_z); } } diff --git a/src/parameters.cpp b/src/parameters.cpp index bb174e6..832308a 100644 --- a/src/parameters.cpp +++ b/src/parameters.cpp @@ -69,7 +69,7 @@ namespace parameters mutation->adapt(weights, adaptation, pop, old_pop, stats, lambda); if (constants::clip_sigma) - mutation->sigma = std::min(std::max(mutation->sigma, constants::lb_sigma), constants::ub_sigma); + mutation->sigma = std::min(std::max(mutation->sigma, restart::MinSigma::tolerance), restart::MaxSigma::tolerance); successfull_adaptation = adaptation->adapt_matrix(weights, settings.modules, pop, mu, settings, stats); diff --git a/src/restart_criteria.cpp b/src/restart_criteria.cpp index 2ad8715..a88cce0 100644 --- a/src/restart_criteria.cpp +++ b/src/restart_criteria.cpp @@ -68,9 +68,14 @@ namespace restart met = time_since_restart > n_bin and recent_improvement == 0; } - void SigmaOutOfBounds::update(const parameters::Parameters &p) + void MaxSigma::update(const parameters::Parameters& p) { - met = constants::lb_sigma > p.mutation->sigma or p.mutation->sigma > constants::ub_sigma; + met = p.mutation->sigma > tolerance; + } + + void MinSigma::update(const parameters::Parameters& p) + { + met = p.mutation->sigma < tolerance; } void UnableToAdapt::update(const parameters::Parameters &p) @@ -99,7 +104,7 @@ namespace restart if (const auto dynamic = std::dynamic_pointer_cast(p.adaptation)) { const Float d_sigma = p.mutation->sigma / p.settings.sigma0; - const Float tolx_condition = 10e-12 * p.settings.sigma0; + const Float tolx_condition = tolerance * p.settings.sigma0; tolx_vector.head(p.settings.dim) = dynamic->C.diagonal() * d_sigma; tolx_vector.tail(p.settings.dim) = dynamic->pc * d_sigma; met = (tolx_vector.array() < tolx_condition).all(); @@ -119,7 +124,7 @@ namespace restart { root_max_d = std::sqrt(dynamic->d.maxCoeff()); } - met = d_sigma > constants::max_dsigma * root_max_d; + met = d_sigma > (tolerance * root_max_d); } void MinDSigma::update(const parameters::Parameters &p) @@ -131,7 +136,7 @@ namespace restart { root_min_d = std::sqrt(dynamic->d.minCoeff()); } - met = d_sigma < constants::min_dsigma * root_min_d; + met = d_sigma < (tolerance * root_min_d); } void ConditionC::update(const parameters::Parameters &p) @@ -139,7 +144,7 @@ namespace restart if (const auto dynamic = std::dynamic_pointer_cast(p.adaptation)) { const Float condition_c = pow(dynamic->d.maxCoeff(), 2.0) / pow(dynamic->d.minCoeff(), 2); - met = condition_c > constants::tol_condition_cov; + met = condition_c > tolerance; } } @@ -149,7 +154,7 @@ namespace restart { const Eigen::Index t = p.stats.t % p.settings.dim; const auto effect_axis = 0.1 * p.mutation->sigma * std::sqrt(dynamic->d(t)) * dynamic->B.col(t); - met = (effect_axis.array() == 0).all(); + met = (effect_axis.array() < tolerance).all(); } } @@ -158,14 +163,14 @@ namespace restart if (const auto dynamic = std::dynamic_pointer_cast(p.adaptation)) { const auto effect_coord = 0.2 * p.mutation->sigma * dynamic->C.diagonal().cwiseSqrt(); - met = (effect_coord.array() == 0).all(); + met = (effect_coord.array() < tolerance).all(); } } void Stagnation::update(const parameters::Parameters &p) { const size_t time_since_restart = p.stats.t - last_restart; - const size_t pt = static_cast(constants::stagnation_quantile * time_since_restart); + const size_t pt = static_cast(tolerance * time_since_restart); median_fitnesses.push_back(median(p.pop.f)); best_fitnesses.push_back(p.pop.f(0)); @@ -192,7 +197,8 @@ namespace restart if (modules.restart_strategy >= parameters::RestartStrategyType::RESTART) { - criteria.push_back(std::make_shared()); + criteria.push_back(std::make_shared()); + criteria.push_back(std::make_shared()); criteria.push_back(std::make_shared()); criteria.push_back(std::make_shared()); criteria.push_back(std::make_shared());