diff --git a/README.md b/README.md
index 01bccee..fb2b482 100644
--- a/README.md
+++ b/README.md
@@ -8,27 +8,31 @@ This README provides a high level overview of the implemented modules, and provi
## Table of Contents
-1. [Installation](#installation)
-2. [Usage](#usage)
- - [Python only interface](#python-only)
- - [Ask-Tell Interface](#ask-tell)
- - [C++ Backend](#c-backend)
-3. [Modules](#modules)
- - [Matrix Adaptation](#matrix-adaptation)
- - [Active Update](#active-update)
- - [Elitism](#elitism)
- - [Orthogonal Sampling](#orthogonal-sampling)
- - [Sequential Selection](#sequential-selection)
- - [Threshold Convergence](#threshold-convergence)
- - [Sample Sigma](#sample-sigma)
- - [Base Sampler](#base-sampler)
- - [Recombination Weights](#recombination-weights)
- - [Mirrored Sampling](#mirrored-sampling)
- - [Step size adaptation](#step-size-adaptation)
- - [Restart Strategy](#restart-strategy)
- - [Bound Correction](#bound-correction)
-4. [Citation](#citation)
-5. [License](#license)
+- [ModularCMAES ](#modularcmaes---)
+ - [Table of Contents](#table-of-contents)
+ - [Installation ](#installation-)
+ - [Python Installation](#python-installation)
+ - [Installation from source](#installation-from-source)
+ - [Usage ](#usage-)
+ - [Python-only ](#python-only-)
+ - [Ask-Tell Interface ](#ask-tell-interface-)
+ - [C++ Backend ](#c-backend-)
+ - [Modules ](#modules-)
+ - [Matrix Adaptation ](#matrix-adaptation-)
+ - [Active Update ](#active-update-)
+ - [Elitism ](#elitism-)
+ - [Orthogonal Sampling ](#orthogonal-sampling-)
+ - [Sequential Selection ](#sequential-selection-)
+ - [Threshold Convergence ](#threshold-convergence-)
+ - [Sample Sigma ](#sample-sigma-)
+ - [Quasi-Gaussian Sampling ](#quasi-gaussian-sampling-)
+ - [Recombination Weights ](#recombination-weights-)
+ - [Mirrored Sampling ](#mirrored-sampling-)
+ - [Step size adaptation](#step-size-adaptation)
+ - [Restart Strategy](#restart-strategy)
+ - [Bound correction](#bound-correction)
+ - [Citation ](#citation-)
+ - [License ](#license-)
## Installation
@@ -184,7 +188,7 @@ while not cma.break_conditions():
cma.mutate(func)
cma.select()
cma.recombine()
- cma.adapt(func)
+ cma.adapt()
```
## Modules
diff --git a/include/c_maes.hpp b/include/c_maes.hpp
index cd91faa..8d9c933 100644
--- a/include/c_maes.hpp
+++ b/include/c_maes.hpp
@@ -12,6 +12,12 @@ struct ModularCMAES
void recombine() const;
+ void select() const;
+
+ void adapt() const;
+
+ void mutate(FunctionType &objective) const;
+
bool step(FunctionType& objective) const;
void operator()(FunctionType& objective) const;
diff --git a/include/common.hpp b/include/common.hpp
index 6000208..aae8627 100644
--- a/include/common.hpp
+++ b/include/common.hpp
@@ -22,7 +22,7 @@
#include
#include
-using Float = long double;
+using Float = double;
using Matrix = Eigen::Matrix;
using Vector = Eigen::Matrix;
using Array = Eigen::Array;
@@ -35,9 +35,9 @@ using FunctionType = std::function;
namespace constants
{
- extern Float tolup_sigma;
+ extern Float max_dsigma;
+ extern Float min_dsigma;
extern Float tol_condition_cov;
- extern Float tol_min_sigma;
extern Float stagnation_quantile;
extern Float sigma_threshold;
extern size_t cache_max_doubles;
diff --git a/include/parameters.hpp b/include/parameters.hpp
index 7d01c90..1f4d0a5 100644
--- a/include/parameters.hpp
+++ b/include/parameters.hpp
@@ -4,7 +4,8 @@
#include "mutation.hpp"
#include "population.hpp"
#include "matrix_adaptation.hpp"
-#include "restart.hpp"
+#include "restart_strategy.hpp"
+#include "restart_criteria.hpp"
#include "sampling.hpp"
#include "stats.hpp"
#include "selection.hpp"
@@ -16,6 +17,8 @@ namespace parameters
{
struct Parameters
{
+ bool successfull_adaptation;
+
size_t lambda;
size_t mu;
@@ -26,23 +29,26 @@ namespace parameters
Population pop;
Population old_pop;
+ restart::Criteria criteria;
std::shared_ptr sampler;
std::shared_ptr adaptation;
std::shared_ptr mutation;
std::shared_ptr selection;
- std::shared_ptr restart;
+ std::shared_ptr restart_strategy;
std::shared_ptr bounds;
std::shared_ptr repelling;
std::shared_ptr center_placement;
Parameters(const size_t dim);
+
Parameters(const Settings &settings);
- void adapt(FunctionType& objective);
+
+ void start(FunctionType& objective);
- void perform_restart(FunctionType& objective, const std::optional &sigma = std::nullopt);
+ void adapt();
- bool invalid_state() const;
+ void perform_restart(FunctionType& objective, const std::optional &sigma = std::nullopt);
};
}
diff --git a/include/restart.hpp b/include/restart.hpp
deleted file mode 100644
index 9c90771..0000000
--- a/include/restart.hpp
+++ /dev/null
@@ -1,174 +0,0 @@
-#pragma once
-
-#include "common.hpp"
-#include "modules.hpp"
-
-namespace parameters
-{
- struct Parameters;
-}
-
-namespace restart
-{
- class RestartCriteria
- {
- void update(const parameters::Parameters &p);
-
- public:
- Float sigma0;
- size_t last_restart;
- size_t max_iter;
- size_t max_flat_fitness;
- size_t n_bin;
- size_t n_stagnation;
- size_t flat_fitness_index;
-
- Eigen::Array flat_fitnesses;
- std::vector median_fitnesses;
- std::vector best_fitnesses;
-
- size_t time_since_restart;
- Float recent_improvement;
- size_t n_flat_fitness;
- Float d_sigma;
-
- Float tolx_condition;
- Vector tolx_vector;
-
- Float root_max_d;
- Float condition_c;
- Vector effect_coord;
- Vector effect_axis;
-
- bool any = false;
-
- RestartCriteria(const Float sigma0, const Float d, const Float lambda, const size_t t)
- : sigma0(sigma0),
- last_restart(t),
- max_iter(static_cast(100 + 50 * std::pow((d + 3), 2.0) / std::sqrt(lambda))),
- max_flat_fitness(static_cast(std::ceil(d / 3))),
- n_bin(10 + static_cast(std::ceil(30 * d / lambda))),
- n_stagnation(static_cast(std::min(static_cast(120 + (30 * d / lambda)), 20000))),
- flat_fitness_index(static_cast(std::round(.1 + lambda / 4))),
- flat_fitnesses(Eigen::Array::Constant(static_cast(d), 0)),
- median_fitnesses{},
- best_fitnesses{},
- time_since_restart(0),
- recent_improvement(0.),
- n_flat_fitness(0),
- d_sigma(0.),
- tolx_condition(0.),
- tolx_vector{static_cast(d * 2)},
- root_max_d(0.),
- condition_c(0.),
- effect_coord(static_cast(d)),
- effect_axis(static_cast(d))
- {
- median_fitnesses.reserve(max_iter);
- best_fitnesses.reserve(max_iter);
- }
-
- bool exceeded_max_iter() const;
-
- bool no_improvement() const;
-
- bool flat_fitness() const;
-
- bool tolx() const;
-
- bool tolupsigma() const;
-
- bool conditioncov() const;
-
- bool noeffectaxis() const;
-
- bool noeffectcoor() const;
-
- bool stagnation() const;
-
- bool min_sigma() const;
-
- bool operator()(const parameters::Parameters &p);
- };
-
- struct Strategy
- {
- RestartCriteria criteria;
-
- Strategy(const Float sigma0, const Float d, const Float lambda) : criteria{sigma0, d, lambda, 0} {}
-
- void evaluate(FunctionType& objective, parameters::Parameters &p);
-
- virtual void restart(FunctionType& objective, parameters::Parameters &) = 0;
- };
-
- struct None : Strategy
- {
- using Strategy::Strategy;
- void restart(FunctionType& objective, parameters::Parameters &p) override {}
- };
-
- struct Stop : Strategy
- {
- using Strategy::Strategy;
- void restart(FunctionType& objective, parameters::Parameters &p) override {}
- };
-
- struct Restart : Strategy
- {
- using Strategy::Strategy;
- void restart(FunctionType& objective, parameters::Parameters &) override;
- };
-
- struct IPOP : Strategy
- {
- Float ipop_factor = 2.0;
- using Strategy::Strategy;
- void restart(FunctionType& objective, parameters::Parameters &) override;
- };
-
- struct BIPOP : Strategy
- {
-
- size_t lambda_init;
- Float mu_factor;
- size_t budget;
-
- size_t lambda_large = 0;
- size_t lambda_small = 0;
- size_t budget_small = 0;
- size_t budget_large = 0;
- size_t used_budget = 0;
-
- BIPOP(
- const Float sigma0, const Float d, const Float lambda, const Float mu, const size_t budget) : Strategy(sigma0, d, lambda), lambda_init(static_cast(lambda)), mu_factor(mu / lambda), budget(budget)
- {
- }
-
- void restart(FunctionType& objective, parameters::Parameters &) override;
-
- bool large() const
- {
- return budget_large >= budget_small and budget_large > 0;
- }
- };
-
- inline std::shared_ptr get(const parameters::RestartStrategyType s, const Float sigma0, const Float d, const Float lambda, const Float mu, const size_t budget)
- {
- using namespace parameters;
- switch (s)
- {
- case RestartStrategyType::RESTART:
- return std::make_shared(sigma0, d, lambda);
- case RestartStrategyType::IPOP:
- return std::make_shared(sigma0, d, lambda);
- case RestartStrategyType::BIPOP:
- return std::make_shared(sigma0, d, lambda, mu, budget);
- case RestartStrategyType::STOP:
- return std::make_shared(sigma0, d, lambda);
- default:
- case RestartStrategyType::NONE:
- return std::make_shared(sigma0, d, lambda);
- }
- }
-}
\ No newline at end of file
diff --git a/include/restart_criteria.hpp b/include/restart_criteria.hpp
new file mode 100644
index 0000000..eb6dea7
--- /dev/null
+++ b/include/restart_criteria.hpp
@@ -0,0 +1,144 @@
+#pragma once
+
+#include "common.hpp"
+#include "modules.hpp"
+
+namespace parameters
+{
+ struct Parameters;
+}
+
+namespace restart
+{
+ struct Criterion {
+ bool met;
+ std::string name;
+ size_t last_restart;
+
+ Criterion(const std::string& name): met(false), name(name) {}
+
+ void reset(const parameters::Parameters &p);
+
+ virtual void update(const parameters::Parameters &p) = 0;
+
+ virtual void on_reset(const parameters::Parameters &p){};
+ };
+
+ using vCriteria = std::vector>;
+
+ struct Criteria {
+ Criteria(const vCriteria& c): items(c){}
+
+ void update(const parameters::Parameters &p)
+ {
+ any = false;
+ for (const auto& c: items)
+ {
+ c->update(p);
+ any = any or c->met;
+ }
+ }
+
+ void reset(const parameters::Parameters &p)
+ {
+ for (const auto& c: items)
+ c->reset(p);
+ }
+
+ vCriteria items;
+ bool any;
+
+ static Criteria get(const parameters::Modules modules);
+ };
+
+
+ struct ExceededMaxIter: Criterion
+ {
+ size_t max_iter;
+ ExceededMaxIter(): Criterion("ExceededMaxIter"){}
+ void update(const parameters::Parameters &p) override;
+ void on_reset(const parameters::Parameters &p) override;
+ };
+
+ struct NoImprovement: Criterion
+ {
+ size_t n_bin;
+ std::vector best_fitnesses;
+ NoImprovement(): Criterion("NoImprovement"){}
+ void update(const parameters::Parameters &p) override;
+ void on_reset(const parameters::Parameters &p) override;
+ };
+
+ struct SigmaOutOfBounds: Criterion
+ {
+ SigmaOutOfBounds(): Criterion("SigmaOutOfBounds"){}
+ void update(const parameters::Parameters &p) override;
+ };
+
+ struct UnableToAdapt: Criterion
+ {
+ UnableToAdapt(): Criterion("UnableToAdapt"){}
+ void update(const parameters::Parameters &p) override;
+ };
+
+ struct FlatFitness: Criterion
+ {
+ size_t max_flat_fitness;
+ size_t flat_fitness_index;
+ Eigen::Array flat_fitnesses;
+
+ FlatFitness(): Criterion("FlatFitness"){}
+ void update(const parameters::Parameters &p) override;
+ void on_reset(const parameters::Parameters &p) override;
+ };
+
+ struct TolX: Criterion
+ {
+ Vector tolx_vector;
+ TolX(): Criterion("TolX"){}
+ void update(const parameters::Parameters &p) override;
+ void on_reset(const parameters::Parameters &p) override;
+ };
+
+
+ struct MaxDSigma: Criterion
+ {
+ MaxDSigma(): Criterion("MaxDSigma"){}
+ void update(const parameters::Parameters &p) override;
+ };
+
+ struct MinDSigma: Criterion
+ {
+ MinDSigma(): Criterion("MinDSigma"){}
+ void update(const parameters::Parameters &p) override;
+ };
+
+
+ struct ConditionC: Criterion
+ {
+ ConditionC(): Criterion("ConditionC"){}
+ void update(const parameters::Parameters &p) override;
+ };
+
+ struct NoEffectAxis: Criterion
+ {
+ NoEffectAxis(): Criterion("NoEffectAxis"){}
+ void update(const parameters::Parameters &p) override;
+ };
+
+ struct NoEffectCoord: Criterion
+ {
+ NoEffectCoord(): Criterion("NoEffectCoord"){}
+ void update(const parameters::Parameters &p) override;
+ };
+
+ struct Stagnation: Criterion
+ {
+ size_t n_stagnation;
+ std::vector median_fitnesses;
+ std::vector best_fitnesses;
+ Stagnation(): Criterion("Stagnation"){}
+ void update(const parameters::Parameters &p) override;
+ void on_reset(const parameters::Parameters &p) override;
+ };
+}
\ No newline at end of file
diff --git a/include/restart_strategy.hpp b/include/restart_strategy.hpp
new file mode 100644
index 0000000..f024523
--- /dev/null
+++ b/include/restart_strategy.hpp
@@ -0,0 +1,77 @@
+#pragma once
+
+#include "common.hpp"
+#include "modules.hpp"
+#include "restart_criteria.hpp"
+
+namespace parameters
+{
+ struct Parameters;
+}
+
+namespace restart
+{
+ struct Strategy
+ {
+ virtual Float update(parameters::Parameters &p);
+ };
+
+ struct IPOP : Strategy
+ {
+ Float ipop_factor = 2.0;
+ Float update(parameters::Parameters &) override;
+ };
+
+ struct BIPOP : Strategy
+ {
+ size_t lambda_init;
+ Float mu_factor;
+ size_t budget;
+
+ size_t lambda_large = 0;
+ size_t lambda_small = 0;
+ size_t budget_small = 0;
+ size_t budget_large = 0;
+ size_t used_budget = 0;
+
+ BIPOP(
+ const Float lambda, const Float mu, const size_t budget) :
+ Strategy(),
+ lambda_init(static_cast(lambda)),
+ mu_factor(mu / lambda),
+ budget(budget)
+ {
+ }
+
+ Float update(parameters::Parameters &) override;
+
+ bool large() const
+ {
+ return budget_large >= budget_small and budget_large > 0;
+ }
+ };
+
+ namespace strategy {
+ inline std::shared_ptr get(
+ const parameters::Modules modules,
+ const Float lambda,
+ const Float mu,
+ const size_t budget
+ )
+ {
+ using namespace parameters;
+ switch (modules.restart_strategy)
+ {
+ case RestartStrategyType::IPOP:
+ return std::make_shared();
+ case RestartStrategyType::BIPOP:
+ return std::make_shared(lambda, mu, budget);
+ default:
+ case RestartStrategyType::STOP:
+ case RestartStrategyType::NONE:
+ case RestartStrategyType::RESTART:
+ return std::make_shared();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/include/stats.hpp b/include/stats.hpp
index 461b981..9aa108d 100644
--- a/include/stats.hpp
+++ b/include/stats.hpp
@@ -31,7 +31,6 @@ namespace parameters
has_improved = true;
}
success_ratio = (1 - cs) * success_ratio + (cs * has_improved);
-
}
};
}
diff --git a/modcma/c_maes/cmaescpp/__init__.pyi b/modcma/c_maes/cmaescpp/__init__.pyi
index 3cbe737..b731cbe 100644
--- a/modcma/c_maes/cmaescpp/__init__.pyi
+++ b/modcma/c_maes/cmaescpp/__init__.pyi
@@ -1,14 +1,21 @@
from typing import Any, Callable, List, Optional, Union, overload, ClassVar
import numpy
-from . import matrix_adaptation, sampling, parameters, mutation, restart, repelling, center
+from . import (
+ matrix_adaptation,
+ sampling,
+ parameters,
+ mutation,
+ restart,
+ repelling,
+ center,
+)
class Solution:
x: numpy.ndarray
y: float
t: int
e: int
-
class constants:
cache_max_doubles: ClassVar[int] = ...
@@ -21,7 +28,6 @@ class constants:
tolup_sigma: ClassVar[float] = ...
def __init__(self, *args, **kwargs) -> None: ...
-
class ModularCMAES:
@overload
def __init__(self, parameters: Parameters) -> None: ...
@@ -40,35 +46,6 @@ class ModularCMAES:
@property
def p(self) -> Parameters: ...
-class Parameters:
- adaptation: Union[
- matrix_adaptation.MatrixAdaptation,
- matrix_adaptation.CovarianceAdaptation,
- matrix_adaptation.NoAdaptation,
- matrix_adaptation.SeperableAdaptation
- ]
- bounds: Any
- center_placement: center.Placement
- lamb: int
- mu: int
- mutation: mutation.Strategy
- old_pop: Population
- pop: Population
- repelling: repelling.Repelling
- restart: restart.Strategy
- sampler: sampling.Sampler
- selection: Any
- settings: parameters.Settings
- stats: parameters.Stats
- weights: parameters.Weights
-
- @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 perform_restart(self, objective: Callable[[numpy.ndarray], float], sigma: Optional[float] = ...) -> None: ...
-
class Population:
X: numpy.ndarray
Y: numpy.ndarray
@@ -91,6 +68,57 @@ class Population:
def keep_only(self, idx: List[int]) -> None: ...
def resize_cols(self, size: int) -> None: ...
def sort(self) -> None: ...
- def __add__(self, other: Population) -> Population: ...
+ def __add__(self, other: "Population") -> "Population": ...
@property
def n_finite(self) -> int: ...
+
+class Parameters:
+ adaptation: (
+ matrix_adaptation.MatrixAdaptation
+ | matrix_adaptation.CovarianceAdaptation
+ | matrix_adaptation.SeperableAdaptation
+ | matrix_adaptation.OnePlusOneAdaptation
+ | matrix_adaptation.NoAdaptation
+ )
+ bounds: Any
+ center_placement: center.Placement
+ criteria: restart.Criteria
+ lamb: int
+ mu: int
+ mutation: mutation.Strategy
+ old_pop: Population
+ pop: Population
+ repelling: repelling.Repelling
+ restart_strategy: restart.Strategy
+ sampler: sampling.Sampler
+ selection: Any
+ settings: parameters.Settings
+ stats: parameters.Stats
+ weights: parameters.Weights
+ @overload
+ def __init__(self, dimension: int) -> None: ...
+ @overload
+ def __init__(self, settings: parameters.Settings) -> None: ...
+ def adapt(self) -> None: ...
+ def perform_restart(
+ self,
+ objective: Callable[[numpy.ndarray[numpy.float64[m, 1]]], float],
+ sigma: float | None = ...,
+ ) -> None: ...
+ 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: ...
diff --git a/modcma/c_maes/cmaescpp/es.pyi b/modcma/c_maes/cmaescpp/es.pyi
new file mode 100644
index 0000000..f2be5f5
--- /dev/null
+++ b/modcma/c_maes/cmaescpp/es.pyi
@@ -0,0 +1,49 @@
+import modcma.c_maes.cmaescpp.bounds
+import modcma.c_maes.cmaescpp.parameters
+import modcma.c_maes.cmaescpp.sampling
+import numpy
+from typing import Callable
+
+class MuCommaLambdaES:
+ S: numpy.ndarray[numpy.float64[m, n]]
+ X: numpy.ndarray[numpy.float64[m, n]]
+ budget: int
+ corrector: modcma.c_maes.cmaescpp.bounds.BoundCorrection
+ d: int
+ e: int
+ f: numpy.ndarray
+ f_min: float
+ lamb: int
+ m: numpy.ndarray
+ mu: int
+ mu_inv: float
+ rejection_sampling: bool
+ sampler: modcma.c_maes.cmaescpp.sampling.Sampler
+ sigma: numpy.ndarray
+ sigma_sampler: modcma.c_maes.cmaescpp.sampling.Sampler
+ t: int
+ target: float
+ tau: float
+ tau_i: float
+ x_min: numpy.ndarray
+ def __init__(self, d: int, x0: numpy.ndarray, sigma0: float = ..., budget: int = ..., target: float = ..., modules: modcma.c_maes.cmaescpp.parameters.Modules = ...) -> None: ...
+ def sample(self, arg0: numpy.ndarray) -> numpy.ndarray: ...
+ def step(self, arg0: Callable[[numpy.ndarray], float]) -> None: ...
+ def __call__(self, arg0: Callable[[numpy.ndarray], float]) -> None: ...
+
+class OnePlusOneES:
+ budget: int
+ corrector: modcma.c_maes.cmaescpp.bounds.BoundCorrection
+ d: int
+ decay: float
+ f: float
+ rejection_sampling: bool
+ sampler: modcma.c_maes.cmaescpp.sampling.Sampler
+ sigma: float
+ t: int
+ target: float
+ x: numpy.ndarray
+ def __init__(self, d: int, x0: numpy.ndarray, f0: float, sigma0: float = ..., budget: int = ..., target: float = ..., modules: modcma.c_maes.cmaescpp.parameters.Modules = ...) -> None: ...
+ def sample(self) -> numpy.ndarray: ...
+ def step(self, arg0: Callable[[numpy.ndarray], float]) -> None: ...
+ def __call__(self, arg0: Callable[[numpy.ndarray], float]) -> None: ...
diff --git a/modcma/c_maes/cmaescpp/matrix_adaptation.pyi b/modcma/c_maes/cmaescpp/matrix_adaptation.pyi
index 742a5c9..40afdb4 100644
--- a/modcma/c_maes/cmaescpp/matrix_adaptation.pyi
+++ b/modcma/c_maes/cmaescpp/matrix_adaptation.pyi
@@ -1,38 +1,21 @@
import modcma.c_maes.cmaescpp
-import modcma.c_maes.cmaescpp.mutation
import numpy
class Adaptation:
- chiN: float
dd: float
dm: numpy.ndarray
+ expected_length_z: float
inv_C: numpy.ndarray
m: numpy.ndarray
m_old: numpy.ndarray
ps: numpy.ndarray
-
def __init__(self, *args, **kwargs) -> None: ...
- def adapt_evolution_paths(
- self,
- pop: modcma.c_maes.cmaescpp.Population,
- weights,
- mutation: modcma.c_maes.cmaescpp.mutation.Strategy,
- stats,
- mu: int,
- lamb: int,
- ) -> None: ...
- def adapt_matrix(
- self,
- weights,
- modules,
- population: modcma.c_maes.cmaescpp.Population,
- mu: int,
- settings,
- ) -> bool: ...
- def restart(self, settings) -> None: ...
+ def adapt_evolution_paths(self, pop: modcma.c_maes.cmaescpp.Population, weights, mutation: modcma.c_maes.cmaescpp.mutation.Strategy, stats, mu: int, lamb: int) -> None: ...
+ def adapt_matrix(self, weights, modules, population: modcma.c_maes.cmaescpp.Population, mu: int, settings, stats) -> bool: ...
def compute_y(self, zi: numpy.ndarray) -> numpy.ndarray: ...
def invert_x(self, xi: numpy.ndarray, sigma: float) -> numpy.ndarray: ...
def invert_y(self, yi: numpy.ndarray) -> numpy.ndarray: ...
+ def restart(self, settings) -> None: ...
class CovarianceAdaptation(Adaptation):
B: numpy.ndarray
@@ -41,19 +24,20 @@ class CovarianceAdaptation(Adaptation):
hs: bool
inv_root_C: numpy.ndarray
pc: numpy.ndarray
- def __init__(self, dimension: int, x0: numpy.ndarray) -> None: ...
- def adapt_covariance_matrix(
- self, weights, modules, population: modcma.c_maes.cmaescpp.Population, mu: int
- ) -> None: ...
+ def __init__(self, dimension: int, x0: numpy.ndarray, expected_length_z: float) -> None: ...
+ def adapt_covariance_matrix(self, weights, modules, population: modcma.c_maes.cmaescpp.Population, mu: int) -> None: ...
def perform_eigendecomposition(self, stats) -> bool: ...
class MatrixAdaptation(Adaptation):
M: numpy.ndarray
M_inv: numpy.ndarray
- def __init__(self, dimension: int, x0: numpy.ndarray) -> None: ...
-
-class SeperableAdaptation(CovarianceAdaptation):
- def __init__(self, dimension: int, x0: numpy.ndarray) -> None: ...
+ def __init__(self, dimension: int, x0: numpy.ndarray, expected_length_z: float) -> None: ...
class NoAdaptation(Adaptation):
- def __init__(self, dimension: int, x0: numpy.ndarray) -> None: ...
+ def __init__(self, dimension: int, x0: numpy.ndarray, expected_length_z: float) -> None: ...
+
+class OnePlusOneAdaptation(CovarianceAdaptation):
+ def __init__(self, dimension: int, x0: numpy.ndarray, expected_length_z: float) -> None: ...
+
+class SeperableAdaptation(CovarianceAdaptation):
+ def __init__(self, dimension: int, x0: numpy.ndarray, expected_length_z: float) -> None: ...
diff --git a/modcma/c_maes/cmaescpp/parameters.pyi b/modcma/c_maes/cmaescpp/parameters.pyi
index 004a3fd..a538a9f 100644
--- a/modcma/c_maes/cmaescpp/parameters.pyi
+++ b/modcma/c_maes/cmaescpp/parameters.pyi
@@ -71,7 +71,9 @@ class Stats:
current_best: Solution
evaluations: int
global_best: Solution
+ has_improved: bool
solutions: list[Solution]
+ success_ratio: float
t: int
def __init__(self) -> None: ...
diff --git a/modcma/c_maes/cmaescpp/restart.pyi b/modcma/c_maes/cmaescpp/restart.pyi
index 375913e..42c90e6 100644
--- a/modcma/c_maes/cmaescpp/restart.pyi
+++ b/modcma/c_maes/cmaescpp/restart.pyi
@@ -1,5 +1,24 @@
import numpy
-from typing import Callable
+
+class Strategy:
+ def __init__(self, *args, **kwargs) -> None: ...
+ def update(self, parameters) -> float: ...
+
+class Criterion:
+ last_restart: int
+ met: bool
+ name: str
+ def __init__(self, *args, **kwargs) -> None: ...
+ def reset(self, parameters) -> None: ...
+ def update(self, parameters) -> None: ...
+
+class Criteria:
+ items: list[Criterion]
+ def __init__(self, *args, **kwargs) -> None: ...
+ def reset(self, parameters) -> None: ...
+ def update(self, parameters) -> None: ...
+ @property
+ def any(self) -> bool: ...
class BIPOP(Strategy):
budget: int
@@ -9,83 +28,57 @@ class BIPOP(Strategy):
lambda_large: int
lambda_small: int
mu_factor: float
- def __init__(self, sigma: float, dimension: float, lamb: float, mu: float, budget: int) -> None: ...
+ def __init__(self, *args, **kwargs) -> None: ...
def large(self) -> bool: ...
- def restart(self, objective: Callable[[numpy.ndarray], float], parameters) -> None: ...
@property
def used_budget(self) -> int: ...
+class ConditionC(Criterion):
+ def __init__(self) -> None: ...
+
+class ExceededMaxIter(Criterion):
+ max_iter: int
+ def __init__(self) -> None: ...
+
+class FlatFitness(Criterion):
+ flat_fitness_index: int
+ flat_fitnesses: numpy.ndarray[numpy.int32[m, 1]]
+ max_flat_fitness: int
+ def __init__(self) -> None: ...
+
class IPOP(Strategy):
ipop_factor: float
- def __init__(self, sigma: float, dimension: float, lamb: float) -> None: ...
- def restart(self, objective: Callable[[numpy.ndarray], float], parameters) -> None: ...
-
-class NoRestart(Strategy):
- def __init__(self, sigma: float, dimension: float, lamb: float) -> None: ...
- def restart(self, objective: Callable[[numpy.ndarray], float], parameters) -> None: ...
-
-class Restart(Strategy):
- def __init__(self, sigma: float, dimension: float, lamb: float) -> None: ...
- def restart(self, objective: Callable[[numpy.ndarray], float], parameters) -> None: ...
-
-class RestartCriteria:
- def __init__(self, sigma: float, dimension: float, lamb: float, time: int) -> None: ...
- def conditioncov(self) -> bool: ...
- def exceeded_max_iter(self) -> bool: ...
- def flat_fitness(self) -> bool: ...
- def no_improvement(self) -> bool: ...
- def noeffectaxis(self) -> bool: ...
- def noeffectcoor(self) -> bool: ...
- def stagnation(self) -> bool: ...
- def tolupsigma(self) -> bool: ...
- def tolx(self) -> bool: ...
- def __call__(self, parameters) -> bool: ...
- @property
- def any(self) -> bool: ...
- @property
- def best_fitnesses(self) -> list[float]: ...
- @property
- def condition_c(self) -> float: ...
- @property
- def d_sigma(self) -> float: ...
- @property
- def effect_axis(self) -> numpy.ndarray: ...
- @property
- def effect_coord(self) -> numpy.ndarray: ...
- @property
- def flat_fitness_index(self) -> int: ...
- @property
- def flat_fitnesses(self) -> numpy.ndarray[numpy.int32[m, 1]]: ...
- @property
- def last_restart(self) -> int: ...
- @property
- def max_iter(self) -> int: ...
- @property
- def median_fitnesses(self) -> list[float]: ...
- @property
- def n_bin(self) -> int: ...
- @property
- def n_flat_fitness(self) -> int: ...
- @property
- def n_stagnation(self) -> int: ...
- @property
- def recent_improvement(self) -> float: ...
- @property
- def root_max_d(self) -> float: ...
- @property
- def sigma0(self) -> float: ...
- @property
- def time_since_restart(self) -> int: ...
- @property
- def tolx_condition(self) -> float: ...
- @property
- def tolx_vector(self) -> numpy.ndarray: ...
+ def __init__(self, *args, **kwargs) -> None: ...
-class Stop(Strategy):
- def __init__(self, sigma: float, dimension: float, lamb: float) -> None: ...
- def restart(self, objective: Callable[[numpy.ndarray], float], parameters) -> None: ...
+class MaxDSigma(Criterion):
+ def __init__(self) -> None: ...
-class Strategy:
- criteria: RestartCriteria
- def __init__(self, *args, **kwargs) -> None: ...
- def evaluate(self, objective: Callable[[numpy.ndarray], float], parameters) -> None: ...
+class MinDSigma(Criterion):
+ def __init__(self) -> None: ...
+
+class NoEffectAxis(Criterion):
+ def __init__(self) -> None: ...
+
+class NoEffectCoord(Criterion):
+ def __init__(self) -> None: ...
+
+class NoImprovement(Criterion):
+ best_fitnesses: list[float]
+ n_bin: int
+ def __init__(self) -> None: ...
+
+class SigmaOutOfBounds(Criterion):
+ def __init__(self) -> None: ...
+
+class Stagnation(Criterion):
+ best_fitnesses: list[float]
+ median_fitnesses: list[float]
+ n_stagnation: int
+ def __init__(self) -> None: ...
+
+class TolX(Criterion):
+ tolx_vector: numpy.ndarray[numpy.float64[m, 1]]
+ def __init__(self) -> None: ...
+
+class UnableToAdapt(Criterion):
+ def __init__(self) -> None: ...
diff --git a/setup.py b/setup.py
index f45ccde..c87dfce 100644
--- a/setup.py
+++ b/setup.py
@@ -11,7 +11,7 @@
with open("README.md", "r", encoding="Latin-1") as fh:
long_description = fh.read()
-__version__ = "1.0.10"
+__version__ = "1.0.11"
ext = Pybind11Extension(
"modcma.c_maes.cmaescpp",
diff --git a/src/c_maes.cpp b/src/c_maes.cpp
index e7bad1a..8244ead 100644
--- a/src/c_maes.cpp
+++ b/src/c_maes.cpp
@@ -7,16 +7,31 @@ void ModularCMAES::recombine() const
positive);
}
-bool ModularCMAES::step(FunctionType& objective) const
+void ModularCMAES::mutate(FunctionType &objective) const
{
+ p->start(objective);
p->mutation->mutate(objective, p->lambda, *p);
+}
+
+void ModularCMAES::select() const
+{
p->selection->select(*p);
+}
+void ModularCMAES::adapt() const
+{
+ p->adapt();
+}
+
+bool ModularCMAES::step(FunctionType& objective) const
+{
+ mutate(objective);
+ select();
recombine();
- p->adapt(objective);
- if (p->stats.t % (p->settings.dim * 2) == 0 and p->settings.verbose)
- std::cout << p->stats << " (mu, lambda, sigma): " << p->mu
- << ", " << p->lambda << ", " << p->mutation->sigma << '\n';
+ adapt();
+ // if (p->stats.t % (p->settings.dim * 2) == 0 and p->settings.verbose)
+ // std::cout << p->stats << " (mu, lambda, sigma): " << p->mu
+ // << ", " << p->lambda << ", " << p->mutation->sigma << '\n';
return !break_conditions();
}
@@ -24,8 +39,8 @@ void ModularCMAES::operator()(FunctionType& objective) const
{
while (step(objective));
- if (p->settings.verbose)
- std::cout << p->stats << '\n';
+ // if (p->settings.verbose)
+ // std::cout << p->stats << '\n';
}
bool ModularCMAES::break_conditions() const
@@ -34,6 +49,6 @@ bool ModularCMAES::break_conditions() const
const auto budget_used_up = p->stats.evaluations >= p->settings.budget;
const auto exceed_gens = p->settings.max_generations and p->stats.t >= p->settings.max_generations;
const auto restart_strategy_criteria = p->settings.modules.restart_strategy == parameters::RestartStrategyType::STOP
- and p->restart->criteria.any;
+ and p->criteria.any;
return exceed_gens or target_reached or budget_used_up or restart_strategy_criteria;
}
diff --git a/src/common.cpp b/src/common.cpp
index 117bde3..9498511 100644
--- a/src/common.cpp
+++ b/src/common.cpp
@@ -11,9 +11,9 @@ std::ostream& operator<<(std::ostream& os, const std::vector& x)
namespace constants
{
- Float tolup_sigma = std::pow(10., 20.);
+ Float max_dsigma = std::pow(10., 20.);
+ Float min_dsigma = 1e-8;
Float tol_condition_cov = pow(10., 14.);
- Float tol_min_sigma = 1e-8;
Float stagnation_quantile = 0.3;
Float sigma_threshold = 1e-4;
size_t cache_max_doubles = 2'000'000;
diff --git a/src/interface.cpp b/src/interface.cpp
index 2d01fc4..24789ec 100644
--- a/src/interface.cpp
+++ b/src/interface.cpp
@@ -636,7 +636,8 @@ void define_parameters(py::module &main)
py::class_>(main, "Parameters")
.def(py::init(), py::arg("dimension"))
.def(py::init(), py::arg("settings"))
- .def("adapt", &Parameters::adapt, py::arg("objective"))
+ .def("adapt", &Parameters::adapt)
+ .def("start", &Parameters::start, py::arg("objective"))
.def("perform_restart", &Parameters::perform_restart, py::arg("objective"),
py::arg("sigma") = std::nullopt)
.def_readwrite("settings", &Parameters::settings)
@@ -665,6 +666,7 @@ void define_parameters(py::module &main)
{
self.adaptation = adaptation;
})
+ .def_readwrite("criteria", &Parameters::criteria)
.def_readwrite("stats", &Parameters::stats)
.def_readwrite("weights", &Parameters::weights)
.def_readwrite("pop", &Parameters::pop)
@@ -672,7 +674,7 @@ void define_parameters(py::module &main)
.def_readwrite("sampler", &Parameters::sampler)
.def_readwrite("mutation", &Parameters::mutation)
.def_readwrite("selection", &Parameters::selection)
- .def_readwrite("restart", &Parameters::restart)
+ .def_readwrite("restart_strategy", &Parameters::restart_strategy)
.def_readwrite("repelling", &Parameters::repelling)
.def_readwrite("bounds", &Parameters::bounds)
.def_readwrite("center_placement", &Parameters::center_placement);
@@ -889,23 +891,23 @@ void define_constants(py::module &m)
{
py::class_(m, "constants")
.def_property_static(
- "tolup_sigma",
+ "max_dsigma",
[](py::object)
- { return constants::tolup_sigma; },
+ { return constants::max_dsigma; },
[](py::object, Float a)
- { constants::tolup_sigma = a; })
+ { constants::max_dsigma = a; })
.def_property_static(
- "tol_condition_cov",
+ "min_dsigma",
[](py::object)
- { return constants::tol_condition_cov; },
+ { return constants::min_dsigma; },
[](py::object, Float a)
- { constants::tol_condition_cov = a; })
+ { constants::min_dsigma = a; })
.def_property_static(
- "tol_min_sigma",
+ "tol_condition_cov",
[](py::object)
- { return constants::tol_min_sigma; },
+ { return constants::tol_condition_cov; },
[](py::object, Float a)
- { constants::tol_min_sigma = a; })
+ { constants::tol_condition_cov = a; })
.def_property_static(
"stagnation_quantile",
[](py::object)
@@ -954,87 +956,93 @@ void define_constants(py::module &m)
[](py::object)
{ return constants::ub_sigma; },
[](py::object, Float a)
- { constants::ub_sigma = a; })
- ;
+ { constants::ub_sigma = a; });
}
-void define_restart(py::module &main)
+void define_restart_criteria(py::module &main)
{
auto m = main.def_submodule("restart");
using namespace restart;
- py::class_(m, "RestartCriteria")
- .def(py::init(), py::arg("sigma"), py::arg("dimension"), py::arg("lamb"), py::arg("time"))
- .def("exceeded_max_iter", &RestartCriteria::exceeded_max_iter)
- .def("no_improvement", &RestartCriteria::no_improvement)
- .def("flat_fitness", &RestartCriteria::flat_fitness)
- .def("tolx", &RestartCriteria::tolx)
- .def("tolupsigma", &RestartCriteria::tolupsigma)
- .def("conditioncov", &RestartCriteria::conditioncov)
- .def("noeffectaxis", &RestartCriteria::noeffectaxis)
- .def("noeffectcoor", &RestartCriteria::noeffectcoor)
- .def("stagnation", &RestartCriteria::stagnation)
- .def_readonly("sigma0", &RestartCriteria::sigma0)
- .def_readonly("last_restart", &RestartCriteria::last_restart)
- .def_readonly("max_iter", &RestartCriteria::max_iter)
- .def_readonly("n_bin", &RestartCriteria::n_bin)
- .def_readonly("n_stagnation", &RestartCriteria::n_stagnation)
- .def_readonly("flat_fitness_index", &RestartCriteria::flat_fitness_index)
- .def_readonly("flat_fitnesses", &RestartCriteria::flat_fitnesses)
- .def_readonly("median_fitnesses", &RestartCriteria::median_fitnesses)
- .def_readonly("best_fitnesses", &RestartCriteria::best_fitnesses)
- .def_readonly("time_since_restart", &RestartCriteria::time_since_restart)
- .def_readonly("recent_improvement", &RestartCriteria::recent_improvement)
- .def_readonly("n_flat_fitness", &RestartCriteria::n_flat_fitness)
- .def_readonly("d_sigma", &RestartCriteria::d_sigma)
- .def_readonly("tolx_condition", &RestartCriteria::tolx_condition)
- .def_readonly("tolx_vector", &RestartCriteria::tolx_vector)
- .def_readonly("root_max_d", &RestartCriteria::root_max_d)
- .def_readonly("condition_c", &RestartCriteria::condition_c)
- .def_readonly("effect_coord", &RestartCriteria::effect_coord)
- .def_readonly("effect_axis", &RestartCriteria::effect_axis)
- .def_readonly("any", &RestartCriteria::any)
- .def("__call__", &RestartCriteria::operator(), py::arg("parameters"))
- .def("__repr__", [](const RestartCriteria &res)
- {
- std::stringstream ss;
- ss << std::boolalpha;
- ss << "";
- return ss.str(); });
+ py::class_>(m, "Criterion")
+ .def("reset", &Criterion::reset, py::arg("parameters"))
+ .def("update", &Criterion::update, 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) + ">"; });
- py::class_>(m, "Strategy")
- .def("evaluate", &Strategy::evaluate, py::arg("objective"), py::arg("parameters"))
- .def_readwrite("criteria", &Strategy::criteria);
+ py::class_>(m, "ExceededMaxIter")
+ .def(py::init<>())
+ .def_readwrite("max_iter", &ExceededMaxIter::max_iter);
+
+ py::class_>(m, "NoImprovement")
+ .def(py::init<>())
+ .def_readwrite("n_bin", &NoImprovement::n_bin)
+ .def_readwrite("best_fitnesses", &NoImprovement::best_fitnesses);
+
+ py::class_>(m, "SigmaOutOfBounds")
+ .def(py::init<>());
+
+ py::class_>(m, "UnableToAdapt")
+ .def(py::init<>());
+
+ py::class_>(m, "FlatFitness")
+ .def(py::init<>())
+ .def_readwrite("max_flat_fitness", &FlatFitness::max_flat_fitness)
+ .def_readwrite("flat_fitness_index", &FlatFitness::flat_fitness_index)
+ .def_readwrite("flat_fitnesses", &FlatFitness::flat_fitnesses);
- py::class_>(m, "NoRestart")
- .def(py::init(), py::arg("sigma"), py::arg("dimension"), py::arg("lamb"))
- .def("restart", &None::restart, py::arg("objective"), py::arg("parameters"));
+ py::class_>(m, "TolX")
+ .def(py::init<>())
+ .def_readwrite("tolx_vector", &TolX::tolx_vector);
+
+ py::class_>(m, "MaxDSigma")
+ .def(py::init<>());
- py::class_>(m, "Stop")
- .def(py::init(), py::arg("sigma"), py::arg("dimension"), py::arg("lamb"))
- .def("restart", &Stop::restart, py::arg("objective"), py::arg("parameters"));
+ py::class_>(m, "MinDSigma")
+ .def(py::init<>());
- py::class_>(m, "Restart")
- .def(py::init(), py::arg("sigma"), py::arg("dimension"), py::arg("lamb"))
- .def("restart", &Restart::restart, py::arg("objective"), py::arg("parameters"));
+ py::class_>(m, "ConditionC")
+ .def(py::init<>());
+
+ py::class_>(m, "NoEffectAxis")
+ .def(py::init<>());
+
+ py::class_>(m, "NoEffectCoord")
+ .def(py::init<>());
+
+ 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);
+
+ 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)
+{
+ auto m = main.def_submodule("restart");
+ using namespace restart;
+
+ py::class_>(m, "Strategy")
+ // .def("evaluate", &Strategy::evaluate, py::arg("objective"), py::arg("parameters"))
+ // .def_readwrite("criteria", &Strategy::criteria)
+ .def("update", &Strategy::update, py::arg("parameters"));
+ ;
py::class_>(m, "IPOP")
- .def(py::init(), py::arg("sigma"), py::arg("dimension"), py::arg("lamb"))
- .def("restart", &IPOP::restart, py::arg("objective"), py::arg("parameters"))
+ // .def(py::init(), py::arg("sigma"), py::arg("dimension"), py::arg("lamb"))
.def_readwrite("ipop_factor", &IPOP::ipop_factor);
py::class_>(m, "BIPOP")
- .def(py::init(), py::arg("sigma"), py::arg("dimension"), py::arg("lamb"), py::arg("mu"), py::arg("budget"))
- .def("restart", &BIPOP::restart, py::arg("objective"), py::arg("parameters"))
+ // .def(py::init(), py::arg("sigma"), py::arg("dimension"), py::arg("lamb"), py::arg("mu"), py::arg("budget"))
.def("large", &BIPOP::large)
.def_readwrite("mu_factor", &BIPOP::mu_factor)
.def_readwrite("lambda_init", &BIPOP::lambda_init)
@@ -1053,16 +1061,9 @@ void define_cmaes(py::module &m)
.def(py::init(), py::arg("dimension"))
.def(py::init(), py::arg("settings"))
.def("recombine", &ModularCMAES::recombine)
- .def(
- "mutate", [](ModularCMAES &self, FunctionType &f)
- { self.p->mutation->mutate(f, self.p->lambda, *self.p); },
- py::arg("objective"))
- .def("select", [](ModularCMAES &self)
- { self.p->selection->select(*self.p); })
- .def(
- "adapt", [](ModularCMAES &self, FunctionType &f)
- { self.p->adapt(f); },
- py::arg("objective"))
+ .def("mutate", &ModularCMAES::mutate, py::arg("objective"))
+ .def("select", &ModularCMAES::select)
+ .def("adapt", &ModularCMAES::adapt)
.def("step", &ModularCMAES::step, py::arg("objective"))
.def("__call__", &ModularCMAES::operator(), py::arg("objective"))
.def("run", &ModularCMAES::operator(), py::arg("objective"))
@@ -1160,7 +1161,8 @@ PYBIND11_MODULE(cmaescpp, m)
define_population(m);
define_samplers(m);
define_mutation(m);
- define_restart(m);
+ define_restart_criteria(m);
+ define_restart_strategy(m);
define_matrix_adaptation(m);
define_center_placement(m);
define_repelling(m);
diff --git a/src/matrix_adaptation.cpp b/src/matrix_adaptation.cpp
index 5396e49..ef90444 100644
--- a/src/matrix_adaptation.cpp
+++ b/src/matrix_adaptation.cpp
@@ -43,14 +43,10 @@ namespace matrix_adaptation
leftCols(mu).transpose());
}
-
- //std::cout << C << std::endl;
C = old_c + rank_one + rank_mu;
C = C.triangularView().toDenseMatrix() +
C.triangularView().toDenseMatrix().transpose();
-
- //std::cout << C << std::endl;
}
bool CovarianceAdaptation::perform_eigendecomposition(const Settings& settings)
diff --git a/src/mutation.cpp b/src/mutation.cpp
index e45053f..f82a3f9 100644
--- a/src/mutation.cpp
+++ b/src/mutation.cpp
@@ -33,22 +33,18 @@ namespace mutation
ss->sample(sigma, p.pop);
p.bounds->n_out_of_bounds = 0;
p.repelling->prepare_sampling(p);
-
for (Eigen::Index i = 0; i < static_cast(n_offspring); ++i)
{
size_t n_rej = 0;
do
{
p.pop.Z.col(i) = p.mutation->tc->scale((*p.sampler)(), p.bounds->diameter, p.settings.budget, p.stats.evaluations);
-
p.pop.Y.col(i) = p.adaptation->compute_y(p.pop.Z.col(i));
-
p.pop.X.col(i) = 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.pop.f(i) = objective(p.pop.X.col(i));
p.stats.evaluations++;
if (sq->break_conditions(i, p.pop.f(i), p.stats.global_best.y, p.settings.modules.mirrored))
@@ -183,6 +179,7 @@ namespace mutation
const Float expected_z)
{
using namespace parameters;
+
auto tc = m.threshold_convergence
? std::make_shared()
: std::make_shared();
diff --git a/src/parameters.cpp b/src/parameters.cpp
index 2340d6e..bb174e6 100644
--- a/src/parameters.cpp
+++ b/src/parameters.cpp
@@ -2,12 +2,15 @@
namespace parameters
{
- Parameters::Parameters(const Settings &settings) : lambda(settings.lambda0),
+ Parameters::Parameters(const Settings &settings) : successfull_adaptation(true),
+ lambda(settings.lambda0),
mu(settings.mu0),
settings(settings),
+ stats{},
weights(settings.dim, settings.mu0, settings.lambda0, settings),
pop(settings.dim, settings.lambda0),
old_pop(settings.dim, settings.lambda0),
+ criteria(restart::Criteria::get(settings.modules)),
sampler(sampling::get(settings.dim, settings.modules, settings.lambda0)),
adaptation(matrix_adaptation::get(settings.modules, settings.dim,
settings.x0.value_or(Vector::Zero(settings.dim)),
@@ -19,10 +22,8 @@ namespace parameters
settings.cs,
sampler->expected_length())),
selection(std::make_shared(settings.modules)),
- restart(restart::get(
- settings.modules.restart_strategy,
- settings.sigma0,
- static_cast(settings.dim),
+ restart_strategy(restart::strategy::get(
+ settings.modules,
static_cast(settings.lambda0),
static_cast(settings.mu0),
settings.budget)),
@@ -30,6 +31,7 @@ namespace parameters
repelling(repelling::get(settings.modules)),
center_placement(center::get(settings.modules.center_placement))
{
+ criteria.reset(*this);
}
Parameters::Parameters(const size_t dim) : Parameters(Settings(dim, {}))
@@ -39,9 +41,8 @@ namespace parameters
void Parameters::perform_restart(FunctionType &objective, const std::optional &sigma)
{
stats.solutions.push_back(stats.current_best);
-
stats.evaluations++;
- stats.centers.emplace_back(adaptation->m, objective(adaptation->m), stats.t, stats.evaluations);
+ stats.centers.emplace_back(adaptation->m, objective(adaptation->m), stats.t - 1, stats.evaluations);
stats.update_best(stats.centers.back().x, stats.centers.back().y);
stats.has_improved = false;
repelling->update_archive(objective, *this);
@@ -58,37 +59,32 @@ namespace parameters
settings.cs, sampler->expected_length());
adaptation->restart(settings);
(*center_placement)(*this);
- restart->criteria = restart::RestartCriteria(sigma.value_or(settings.sigma0), settings.dim, lambda, stats.t);
+ criteria.reset(*this);
stats.current_best = {};
}
- bool Parameters::invalid_state() const
+ void Parameters::adapt()
{
+ adaptation->adapt_evolution_paths(pop, weights, mutation, stats, mu, lambda);
+ 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);
-
- const bool sigma_out_of_bounds = constants::lb_sigma > mutation->sigma or mutation->sigma > constants::ub_sigma;
- if (sigma_out_of_bounds && settings.verbose)
- {
- std::cout << "sigma out of bounds: " << mutation->sigma << " restarting\n";
- }
- return sigma_out_of_bounds;
+ successfull_adaptation = adaptation->adapt_matrix(weights, settings.modules, pop, mu, settings, stats);
+
+ criteria.update(*this);
+ stats.t++;
}
- void Parameters::adapt(FunctionType &objective)
+ void Parameters::start(FunctionType &objective)
{
- adaptation->adapt_evolution_paths(pop, weights, mutation, stats, mu, lambda);
- mutation->adapt(weights, adaptation, pop, old_pop, stats, lambda);
-
- auto successfull_adaptation = adaptation->adapt_matrix(weights, settings.modules, pop, mu, settings, stats);
-
- if (!successfull_adaptation or invalid_state())
- perform_restart(objective);
-
old_pop = pop;
- restart->evaluate(objective, *this);
- stats.t++;
+ if (criteria.any)
+ {
+ const auto sig = restart_strategy->update(*this);
+ perform_restart(objective, sig);
+ }
}
}
@@ -99,6 +95,5 @@ std::ostream &operator<<(std::ostream &os, const parameters::Stats &s)
<< " t=" << s.t
<< " e=" << s.evaluations
<< " best=" << s.global_best
- << " improved=" << std::boolalpha << s.has_improved
- ;
+ << " improved=" << std::boolalpha << s.has_improved;
}
diff --git a/src/restart.cpp b/src/restart.cpp
deleted file mode 100644
index 77c27a7..0000000
--- a/src/restart.cpp
+++ /dev/null
@@ -1,220 +0,0 @@
-#include "restart.hpp"
-#include "parameters.hpp"
-#include "matrix_adaptation.hpp"
-
-#include
-
-namespace restart
-{
- //! max - min, for the last n elements of a vector
- Float ptp_tail(const std::vector &v, const size_t n)
- {
- const auto na = std::min(v.size(), n);
- if (na == 1)
- {
- return v[0];
- }
-
- const Float min = *std::min_element(v.end() - na, v.end());
- const Float max = *std::max_element(v.end() - na, v.end());
- return max - min;
- }
-
- // TODO: this is duplicate code
- 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);
- }
-
- Float median(const std::vector &v, const size_t from, const size_t to)
- {
- const size_t n = to - from;
- if (n % 2 == 0)
- return (v[from + (n / 2)] + v[from + (n / 2) - 1]) / 2.0;
- return v[from + (n / 2)];
- }
-
- void RestartCriteria::update(const parameters::Parameters &p)
- {
- flat_fitnesses(p.stats.t % p.settings.dim) = p.pop.f(0) == p.pop.f(flat_fitness_index);
- median_fitnesses.push_back(median(p.pop.f));
- best_fitnesses.push_back(p.pop.f(0));
-
- time_since_restart = p.stats.t - last_restart;
- recent_improvement = ptp_tail(best_fitnesses, n_bin);
- n_flat_fitness = static_cast(flat_fitnesses.sum());
-
- d_sigma = p.mutation->sigma / sigma0;
- tolx_condition = 10e-12 * sigma0;
-
- if (p.settings.modules.matrix_adaptation == parameters::MatrixAdaptationType::COVARIANCE ||
- p.settings.modules.matrix_adaptation == parameters::MatrixAdaptationType::SEPERABLE)
- {
- using namespace matrix_adaptation;
- const std::shared_ptr dynamic = std::dynamic_pointer_cast(
- p.adaptation);
-
- tolx_vector.head(p.settings.dim) = dynamic->C.diagonal() * d_sigma;
- tolx_vector.tail(p.settings.dim) = dynamic->pc * d_sigma;
-
- root_max_d = std::sqrt(dynamic->d.maxCoeff());
- condition_c = pow(dynamic->d.maxCoeff(), 2.0) / pow(dynamic->d.minCoeff(), 2);
-
- effect_coord = 0.2 * p.mutation->sigma * dynamic->C.diagonal().cwiseSqrt();
-
- const Eigen::Index t = p.stats.t % p.settings.dim;
- effect_axis = 0.1 * p.mutation->sigma * std::sqrt(dynamic->d(t)) * dynamic->B.col(t);
- }
- }
-
- bool RestartCriteria::exceeded_max_iter() const
- {
- return max_iter < time_since_restart;
- }
-
- bool RestartCriteria::no_improvement() const
- {
- return time_since_restart > n_bin and recent_improvement == 0;
- }
-
- bool RestartCriteria::flat_fitness() const
- {
- return time_since_restart > static_cast(flat_fitnesses.size()) and n_flat_fitness > max_flat_fitness;
- }
-
- bool RestartCriteria::tolx() const
- {
- return (tolx_vector.array() < tolx_condition).all();
- }
-
- bool RestartCriteria::tolupsigma() const
- {
- return d_sigma > constants::tolup_sigma * root_max_d;
- }
-
- bool RestartCriteria::conditioncov() const
- {
- return condition_c > constants::tol_condition_cov;
- }
-
- bool RestartCriteria::noeffectaxis() const
- {
- return (effect_axis.array() == 0).all();
- }
-
- bool RestartCriteria::noeffectcoor() const
- {
- return (effect_coord.array() == 0).all();
- }
-
- bool RestartCriteria::min_sigma() const
- {
- return d_sigma < constants::tol_min_sigma;
- }
-
- bool RestartCriteria::stagnation() const
- {
- const size_t pt = static_cast(constants::stagnation_quantile * time_since_restart);
- return time_since_restart > n_stagnation and ((median(best_fitnesses, pt, time_since_restart) >= median(
- best_fitnesses, 0, pt)) and
- (median(median_fitnesses, pt, time_since_restart) >= median(median_fitnesses, 0, pt)));
- }
-
- bool RestartCriteria::operator()(const parameters::Parameters &p)
- {
- update(p);
- any = exceeded_max_iter() or no_improvement() or stagnation() or min_sigma();
- if (p.settings.lambda0 > 1)
- {
- any = any or flat_fitness();
- }
- if (p.settings.modules.matrix_adaptation == parameters::MatrixAdaptationType::COVARIANCE)
- {
- any = any or tolx() or tolupsigma() or conditioncov() or noeffectaxis() or noeffectcoor();
- }
-
- if (any)
- {
- if (p.settings.verbose)
- {
- std::cout << "restart criteria: " << p.stats.t << " (";
- std::cout << time_since_restart << std::boolalpha;
- std::cout << ") flat_fitness: " << flat_fitness();
- std::cout << " exeeded_max_iter: " << exceeded_max_iter();
- std::cout << " no_improvement: " << no_improvement();
- std::cout << " tolx: " << tolx();
- std::cout << " tolupsigma: " << tolupsigma();
- std::cout << " conditioncov: " << conditioncov();
- std::cout << " noeffectaxis: " << noeffectaxis();
- std::cout << " noeffectcoor: " << noeffectcoor();
- std::cout << " min_sigma: " << min_sigma();
- std::cout << " stagnation: " << stagnation() << '\n';
- }
- return true;
- }
- return false;
- }
-
- void Strategy::evaluate(FunctionType &objective, parameters::Parameters &p)
- {
- if (criteria(p))
- {
- restart(objective, p);
- }
- }
-
- void Restart::restart(FunctionType &objective, parameters::Parameters &p)
- {
- p.perform_restart(objective);
- }
-
- void IPOP::restart(FunctionType &objective, parameters::Parameters &p)
- {
- const size_t max_lambda = static_cast(std::pow(p.settings.dim * p.lambda, 2));
- if (p.mu < max_lambda)
- {
- p.mu *= static_cast(ipop_factor);
- p.lambda *= static_cast(ipop_factor);
- }
- p.perform_restart(objective);
- }
-
- void BIPOP::restart(FunctionType &objective, parameters::Parameters &p)
- {
- static std::uniform_real_distribution<> dist;
-
- const auto last_used_budget = p.stats.evaluations - used_budget;
- used_budget += last_used_budget;
- const auto remaining_budget = budget - used_budget;
-
- if (!lambda_large)
- {
- lambda_large = lambda_init * 2;
- budget_small = remaining_budget / 2;
- budget_large = remaining_budget - budget_small;
- }
- else if (large())
- {
- budget_large -= last_used_budget;
- lambda_large *= 2;
- }
- else
- {
- budget_small -= last_used_budget;
- }
-
- lambda_small = static_cast(std::floor(
- static_cast(lambda_init) * std::pow(.5 / static_cast(lambda_large) / lambda_init,
- std::pow(dist(rng::GENERATOR), 2))));
-
- if (lambda_small % 2 != 0)
- lambda_small++;
-
- p.lambda = std::max(size_t{2}, large() ? lambda_large : lambda_small);
- p.mu = std::max(Float{1.0}, p.lambda * mu_factor);
- p.perform_restart(objective,
- large() ? p.settings.sigma0 : p.settings.sigma0 * std::pow(10, -2 * dist(rng::GENERATOR)));
- }
-}
diff --git a/src/restart_criteria.cpp b/src/restart_criteria.cpp
new file mode 100644
index 0000000..2ad8715
--- /dev/null
+++ b/src/restart_criteria.cpp
@@ -0,0 +1,214 @@
+#include
+#include "restart_criteria.hpp"
+#include "parameters.hpp"
+
+namespace
+{
+ //! max - min, for the last n elements of a vector
+ Float ptp_tail(const std::vector &v, const size_t n)
+ {
+ const auto na = std::min(v.size(), n);
+ if (na == 1)
+ {
+ return v[0];
+ }
+
+ const Float min = *std::min_element(v.end() - na, v.end());
+ const Float max = *std::max_element(v.end() - na, v.end());
+ return max - min;
+ }
+
+ // TODO: this is duplicate code
+ 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);
+ }
+
+ Float median(const std::vector &v, const size_t from, const size_t to)
+ {
+ const size_t n = to - from;
+ if (n % 2 == 0)
+ return (v[from + (n / 2)] + v[from + (n / 2) - 1]) / 2.0;
+ return v[from + (n / 2)];
+ }
+}
+
+namespace restart
+{
+ void Criterion::reset(const parameters::Parameters &p)
+ {
+ last_restart = p.stats.t;
+ met = false;
+ on_reset(p);
+ }
+
+ void ExceededMaxIter::on_reset(const parameters::Parameters &p)
+ {
+ max_iter = static_cast(100 + 50 * std::pow((static_cast(p.settings.dim) + 3), 2.0) / std::sqrt(static_cast(p.lambda)));
+ }
+
+ void ExceededMaxIter::update(const parameters::Parameters &p)
+ {
+ const auto time_since_restart = p.stats.t - last_restart;
+ met = max_iter < time_since_restart;
+ }
+
+ void NoImprovement::on_reset(const parameters::Parameters &p)
+ {
+ n_bin = 10 + static_cast(std::ceil(30 * static_cast(p.settings.dim) / static_cast(p.lambda)));
+ }
+
+ void NoImprovement::update(const parameters::Parameters &p)
+ {
+ const size_t time_since_restart = p.stats.t - last_restart;
+ best_fitnesses.push_back(p.pop.f(0));
+ const auto recent_improvement = ptp_tail(best_fitnesses, n_bin);
+ met = time_since_restart > n_bin and recent_improvement == 0;
+ }
+
+ void SigmaOutOfBounds::update(const parameters::Parameters &p)
+ {
+ met = constants::lb_sigma > p.mutation->sigma or p.mutation->sigma > constants::ub_sigma;
+ }
+
+ void UnableToAdapt::update(const parameters::Parameters &p)
+ {
+ met = !p.successfull_adaptation or !std::isfinite(p.mutation->sigma);
+ }
+
+ void FlatFitness::update(const parameters::Parameters &p)
+ {
+ const size_t time_since_restart = p.stats.t - last_restart;
+ flat_fitnesses(p.stats.t % p.settings.dim) = p.pop.f(0) == p.pop.f(flat_fitness_index);
+ const size_t n_flat_fitness = static_cast(flat_fitnesses.sum());
+ met = time_since_restart > static_cast(flat_fitnesses.size()) and n_flat_fitness > max_flat_fitness;
+ }
+
+ void FlatFitness::on_reset(const parameters::Parameters &p)
+ {
+ flat_fitnesses = Eigen::Array::Constant(p.settings.dim, 0);
+ max_flat_fitness = static_cast(std::ceil(static_cast(p.settings.dim) / 3));
+ flat_fitness_index = static_cast(std::round(.1 + static_cast(p.lambda) / 4));
+ }
+
+ void TolX::update(const parameters::Parameters &p)
+ {
+ // TODO: This should be another sigma0, the one that has been used to 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;
+ 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();
+ }
+ }
+
+ void TolX::on_reset(const parameters::Parameters &p)
+ {
+ tolx_vector = Vector::Ones(p.settings.dim * 2);
+ }
+
+ void MaxDSigma::update(const parameters::Parameters &p)
+ {
+ const Float d_sigma = p.mutation->sigma / p.settings.sigma0;
+ Float root_max_d = 1.0;
+ if (const auto dynamic = std::dynamic_pointer_cast(p.adaptation))
+ {
+ root_max_d = std::sqrt(dynamic->d.maxCoeff());
+ }
+ met = d_sigma > constants::max_dsigma * root_max_d;
+ }
+
+ void MinDSigma::update(const parameters::Parameters &p)
+ {
+ const Float d_sigma = p.mutation->sigma / p.settings.sigma0;
+
+ Float root_min_d = 1.0;
+ if (const auto dynamic = std::dynamic_pointer_cast(p.adaptation))
+ {
+ root_min_d = std::sqrt(dynamic->d.minCoeff());
+ }
+ met = d_sigma < constants::min_dsigma * root_min_d;
+ }
+
+ void ConditionC::update(const parameters::Parameters &p)
+ {
+ 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;
+ }
+ }
+
+ void NoEffectAxis::update(const parameters::Parameters &p)
+ {
+ if (const auto dynamic = std::dynamic_pointer_cast(p.adaptation))
+ {
+ 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();
+ }
+ }
+
+ void NoEffectCoord::update(const parameters::Parameters &p)
+ {
+ 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();
+ }
+ }
+
+ 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);
+ median_fitnesses.push_back(median(p.pop.f));
+ best_fitnesses.push_back(p.pop.f(0));
+
+ const bool best_better = median(best_fitnesses, pt, time_since_restart) >= median(best_fitnesses, 0, pt);
+ const bool median_better = median(median_fitnesses, pt, time_since_restart) >= median(median_fitnesses, 0, pt);
+
+ met = time_since_restart > n_stagnation and (best_better and median_better);
+ }
+
+ void Stagnation::on_reset(const parameters::Parameters &p)
+ {
+ const auto d = static_cast(p.settings.dim);
+ const auto lambda = static_cast(p.lambda);
+ n_stagnation = (static_cast(std::min(static_cast(120 + (30 * d / lambda)), 20000)));
+ median_fitnesses = {};
+ best_fitnesses = {};
+ }
+
+ Criteria Criteria::get(const parameters::Modules modules)
+ {
+ vCriteria criteria {
+ std::make_shared()
+ };
+
+ 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());
+ criteria.push_back(std::make_shared());
+ }
+
+ if (modules.matrix_adaptation == parameters::MatrixAdaptationType::COVARIANCE ||
+ modules.matrix_adaptation == parameters::MatrixAdaptationType::SEPERABLE)
+ {
+ criteria.push_back(std::make_shared());
+ criteria.push_back(std::make_shared());
+ criteria.push_back(std::make_shared());
+ criteria.push_back(std::make_shared());
+ }
+ return Criteria(criteria);
+ }
+}
\ No newline at end of file
diff --git a/src/restart_strategy.cpp b/src/restart_strategy.cpp
new file mode 100644
index 0000000..9f6c1b8
--- /dev/null
+++ b/src/restart_strategy.cpp
@@ -0,0 +1,61 @@
+#include "restart_strategy.hpp"
+#include "parameters.hpp"
+#include "matrix_adaptation.hpp"
+
+
+
+namespace restart
+{
+
+ Float Strategy::update(parameters::Parameters &p)
+ {
+ return p.settings.sigma0;
+ }
+
+ Float IPOP::update(parameters::Parameters &p)
+ {
+ const size_t max_lambda = static_cast(std::pow(p.settings.dim * p.lambda, 2));
+ if (p.mu < max_lambda)
+ {
+ p.mu *= static_cast(ipop_factor);
+ p.lambda *= static_cast(ipop_factor);
+ }
+ return p.settings.sigma0;
+ }
+
+ Float BIPOP::update(parameters::Parameters &p)
+ {
+ static std::uniform_real_distribution<> dist;
+
+ const auto last_used_budget = p.stats.evaluations - used_budget;
+ used_budget += last_used_budget;
+ const auto remaining_budget = budget - used_budget;
+
+ if (!lambda_large)
+ {
+ lambda_large = lambda_init * 2;
+ budget_small = remaining_budget / 2;
+ budget_large = remaining_budget - budget_small;
+ }
+ else if (large())
+ {
+ budget_large -= last_used_budget;
+ lambda_large *= 2;
+ }
+ else
+ {
+ budget_small -= last_used_budget;
+ }
+
+ lambda_small = static_cast(std::floor(
+ static_cast(lambda_init) * std::pow(.5 / static_cast(lambda_large) / lambda_init,
+ std::pow(dist(rng::GENERATOR), 2))));
+
+ if (lambda_small % 2 != 0)
+ lambda_small++;
+
+ p.lambda = std::max(size_t{2}, large() ? lambda_large : lambda_small);
+ p.mu = std::max(Float{1.0}, p.lambda * mu_factor);
+ return large() ? p.settings.sigma0 : p.settings.sigma0 * std::pow(10, -2 * dist(rng::GENERATOR));
+ }
+}
diff --git a/tests/test_c_adaptation.py b/tests/test_c_adaptation.py
index 400818c..b39cac4 100644
--- a/tests/test_c_adaptation.py
+++ b/tests/test_c_adaptation.py
@@ -29,7 +29,7 @@ def test_matrix_adaptation(self):
new_M = ((0.5 * cma.p.weights.cmu * cma.p.weights.positive) * cma.p.pop.Y[:, :cma.p.mu]).dot(cma.p.pop.Z[:, :cma.p.mu].T)
M = old_M + scaled_ps + new_M
- cma.adapt(sum)
+ cma.adapt()
self.assertTrue(np.all(np.isclose(cma.p.adaptation.M, M)))