use crate::error::OptimizeResult;
use crate::result::OptimizeResults;
use scirs2_core::ndarray::{Array1, ArrayView1};
use scirs2_core::random::{rng, Rng, RngExt};
#[derive(Debug, Clone)]
pub struct EvolutionaryStrategy {
pub population_size: usize,
pub population: Vec<Array1<f64>>,
pub fitness: Vec<f64>,
pub sigma: f64,
}
impl EvolutionaryStrategy {
pub fn new(population_size: usize, dimensions: usize, sigma: f64) -> Self {
let mut population = Vec::with_capacity(population_size);
for _ in 0..population_size {
let individual = Array1::from_shape_fn(dimensions, |_| {
scirs2_core::random::rng().random::<f64>() - 0.5
});
population.push(individual);
}
Self {
population_size,
population,
fitness: vec![f64::INFINITY; population_size],
sigma,
}
}
pub fn evaluate<F>(&mut self, objective: &F)
where
F: Fn(&ArrayView1<f64>) -> f64,
{
for (i, individual) in self.population.iter().enumerate() {
self.fitness[i] = objective(&individual.view());
}
}
pub fn evolve(&mut self) {
let mut indices: Vec<usize> = (0..self.population_size).collect();
indices.sort_by(|&a, &b| {
self.fitness[a]
.partial_cmp(&self.fitness[b])
.expect("Operation failed")
});
let elite_size = self.population_size / 2;
for i in elite_size..self.population_size {
let parent_idx = indices[scirs2_core::random::rng().random_range(0..elite_size)];
let parent = &self.population[parent_idx];
let mut offspring = parent.clone();
for j in 0..offspring.len() {
offspring[j] += self.sigma * (scirs2_core::random::rng().random_range(-0.5..0.5));
}
self.population[i] = offspring;
}
}
pub fn get_best(&self) -> (Array1<f64>, f64) {
let mut best_idx = 0;
let mut best_fitness = self.fitness[0];
for (i, &fitness) in self.fitness.iter().enumerate() {
if fitness < best_fitness {
best_fitness = fitness;
best_idx = i;
}
}
(self.population[best_idx].clone(), best_fitness)
}
}
#[allow(dead_code)]
pub fn evolutionary_optimize<F>(
objective: F,
initial_params: &ArrayView1<f64>,
num_generations: usize,
) -> OptimizeResult<OptimizeResults<f64>>
where
F: Fn(&ArrayView1<f64>) -> f64,
{
let mut es = EvolutionaryStrategy::new(50, initial_params.len(), 0.1);
es.population[0] = initial_params.to_owned();
for _generation in 0..num_generations {
es.evaluate(&objective);
es.evolve();
}
let (best_params, best_fitness) = es.get_best();
Ok(OptimizeResults::<f64> {
x: best_params,
fun: best_fitness,
success: true,
nit: num_generations,
message: "Evolutionary strategy completed".to_string(),
jac: None,
hess: None,
constr: None,
nfev: num_generations * 50, njev: 0,
nhev: 0,
maxcv: 0,
status: 0,
})
}
#[allow(dead_code)]
pub fn placeholder() {}