1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
use anyhow::{anyhow, Result};
use ndarray::{s, Array1};
/// Parameters for CMA-ES (Covariance Matrix Adaptation Evolution Strategy).
#[derive(Debug, Clone)]
pub struct CmaesParams {
/// Population size.
pub popsize: i32,
/// Initial solution vector.
pub xstart: Vec<f32>,
/// Step-size (standard deviation).
pub sigma: f32,
}
/// Validated parameters for CMA-ES.
#[derive(Debug, Clone)]
pub struct CmaesParamsValid {
pub popsize: i32,
pub xstart: Vec<f32>,
pub sigma: f32,
pub n: f32,
// pub chin: f32,
pub mu: i32,
pub weights: Array1<f32>,
pub mueff: f32,
pub cc: f32,
pub cs: f32,
pub c1: f32,
pub cmu: f32,
pub damps: f32,
// pub lazy_gap_evals: f32,
}
impl CmaesParamsValid {
/// Validates the provided parameters and returns a validated parameter set.
///
/// # Errors
/// Returns an error if any of the initial parameters do not meet their constraints.
pub fn validate(params: &CmaesParams) -> Result<CmaesParamsValid> {
// print!("Validating initial parameters... ");
let params = match CmaesParamsValid::validate_params(params) {
Ok(params) => params,
Err(e) => {
eprintln!(
"An initial Cmaes parameter is not following its constraint: {}",
e
);
return Err(e);
}
};
// print!("Computing default parameters... ");
let params = CmaesParamsValid::create_default_params(params)?;
Ok(params)
}
/// Creates default parameters for the CMA-ES algorithm based on the provided parameters.
fn create_default_params(params: CmaesParams) -> Result<CmaesParamsValid> {
let popsize = params.popsize;
let xstart = params.xstart;
let sigma = params.sigma;
let n = xstart.len() as f32;
let k = popsize as f32;
// let chin = n.sqrt() * (1. - 1. / (4. * n) + 1. / (21. * n * n));
let mu = popsize / 2;
let _iterable: Vec<f32> = (0..popsize)
.map(|_x| {
if _x < mu {
(k / 2.0 + 0.5).ln() - ((_x + 1) as f32).ln()
} else {
0.0
}
})
.collect();
let _weights: Array1<f32> = Array1::from_iter(_iterable);
let _w_sum = _weights.slice(s![..mu]).sum();
let weights: Array1<f32> = _weights.mapv(|x| x / _w_sum);
let mueff: f32 = (weights.slice(s![..mu]).sum() * weights.slice(s![..mu]).sum())
/ weights.slice(s![..mu]).mapv(|x| x * x).sum();
let cc = (4. + mueff / n) / (n + 4. + 2. * mueff / n);
let cs = (mueff + 2.) / (n + mueff + 5.);
let c1 = 2. / ((n + 1.3) * (n + 1.3) + mueff);
let cmu = (1. - c1).min(2. * (mueff - 2. + 1. / mueff) / ((n + 2.) * (n + 2.) + mueff));
let damps = 2. * mueff / k + 0.3 + cs;
// gap to postpone eigendecomposition to achieve O(N**2) per eval
// 0.5 is chosen such that eig takes 2 times the time of tell in >=20-D
// let lazy_gap_evals = 0.5 * n * (k) * (c1 + cmu).powi(-1) / (n * n);
let valid_params = CmaesParamsValid {
popsize,
xstart,
sigma,
n,
// chin,
mu,
weights,
mueff,
cc,
cs,
c1,
cmu,
damps,
// lazy_gap_evals,
};
Ok(valid_params)
}
/// Validates the provided parameters to ensure they meet the constraints.
///
/// # Errors
/// Returns an error if any parameter does not meet its constraint.
fn validate_params(params: &CmaesParams) -> Result<CmaesParams> {
CmaesParamsValid::check_popsize(params)?;
CmaesParamsValid::check_xstart(params)?;
CmaesParamsValid::check_sigma(params)?;
Ok(params.clone())
}
/// Checks if the `xstart` parameter meets its constraints.
///
/// # Errors
/// Returns an error if the number of dimensions is not greater than 1.
fn check_xstart(params: &CmaesParams) -> Result<()> {
if params.xstart.len() <= 1 {
return Err(anyhow!("==> number of dimensions must be > 1."));
}
Ok(())
}
/// Checks if the `popsize` parameter meets its constraints.
///
/// # Errors
/// Returns an error if the population size is not greater than 5.
fn check_popsize(params: &CmaesParams) -> Result<()> {
if params.popsize <= 5 {
return Err(anyhow!("==> popsize must be > 5."));
}
Ok(())
}
/// Checks if the `sigma` parameter meets its constraints.
///
/// # Errors
/// Returns an error if the step-size is not greater than 0.
fn check_sigma(params: &CmaesParams) -> Result<()> {
if params.sigma <= 0.0 {
return Err(anyhow!("==> sigma must be greater than 0.0."));
}
Ok(())
}
}