#[derive(Debug, Clone)]
pub struct CMAESConfig {
pub population_size: usize,
pub sigma0: f64,
pub max_iter: usize,
pub ftol: f64,
pub xtol: f64,
pub bounds: Option<Vec<(f64, f64)>>,
pub seed: Option<u64>,
pub penalty_coefficient: f64,
pub enable_restarts: bool,
pub max_restarts: usize,
pub restart_pop_multiplier: f64,
pub max_condition_number: f64,
pub weights: Option<Vec<f64>>,
pub c1: Option<f64>,
pub cmu: Option<f64>,
pub cc: Option<f64>,
pub cs: Option<f64>,
pub damps: Option<f64>,
}
impl Default for CMAESConfig {
fn default() -> Self {
Self {
population_size: 0,
sigma0: 0.5,
max_iter: 10000,
ftol: 1e-12,
xtol: 1e-12,
bounds: None,
seed: None,
penalty_coefficient: 1e6,
enable_restarts: false,
max_restarts: 9,
restart_pop_multiplier: 2.0,
max_condition_number: 1e14,
weights: None,
c1: None,
cmu: None,
cc: None,
cs: None,
damps: None,
}
}
}
impl CMAESConfig {
pub fn new(dimension: usize) -> Self {
let lambda = compute_default_lambda(dimension);
Self {
population_size: lambda,
..Self::default()
}
}
pub fn with_population_size(mut self, pop_size: usize) -> Self {
self.population_size = pop_size;
self
}
pub fn with_sigma0(mut self, sigma0: f64) -> Self {
self.sigma0 = sigma0;
self
}
pub fn with_sigma(self, sigma: f64) -> Self {
self.with_sigma0(sigma)
}
pub fn with_max_iter(mut self, max_iter: usize) -> Self {
self.max_iter = max_iter;
self
}
pub fn with_max_generations(self, max_gen: usize) -> Self {
self.with_max_iter(max_gen)
}
pub fn with_bounds(mut self, bounds: Vec<(f64, f64)>) -> Self {
self.bounds = Some(bounds);
self
}
pub fn with_seed(mut self, seed: u64) -> Self {
self.seed = Some(seed);
self
}
pub fn with_ftol(mut self, ftol: f64) -> Self {
self.ftol = ftol;
self
}
pub fn with_xtol(mut self, xtol: f64) -> Self {
self.xtol = xtol;
self
}
pub fn with_restarts(mut self, max_restarts: usize) -> Self {
self.enable_restarts = true;
self.max_restarts = max_restarts;
self
}
pub fn with_lambda(mut self, lambda: usize) -> Self {
self.population_size = lambda;
self
}
pub(crate) fn effective_lambda(&self, dimension: usize) -> usize {
if self.population_size == 0 {
compute_default_lambda(dimension)
} else {
self.population_size
}
}
}
pub(crate) fn compute_default_lambda(dimension: usize) -> usize {
if dimension == 0 {
return 4;
}
4 + (3.0 * (dimension as f64).ln()).floor() as usize
}
pub(crate) fn compute_default_weights(mu: usize, _lambda: usize) -> Vec<f64> {
let mu_f = mu as f64;
let raw: Vec<f64> = (0..mu)
.map(|i| (mu_f + 0.5).ln() - ((i + 1) as f64).ln())
.collect();
let sum: f64 = raw.iter().sum();
if sum > 0.0 {
raw.iter().map(|&w| w / sum).collect()
} else {
vec![1.0 / mu_f; mu]
}
}
pub(crate) fn compute_mu_eff(weights: &[f64], mu: usize) -> f64 {
let sum_w: f64 = weights.iter().take(mu).sum();
let sum_w2: f64 = weights.iter().take(mu).map(|w| w * w).sum();
if sum_w2 > 0.0 {
(sum_w * sum_w) / sum_w2
} else {
1.0
}
}