scirs2-optimize 0.4.2

Optimization module for SciRS2 (scirs2-optimize)
Documentation
//! Evolutionary Strategies for RL Optimization
//!
//! Population-based reinforcement learning optimization methods.

use crate::error::OptimizeResult;
use crate::result::OptimizeResults;
use scirs2_core::ndarray::{Array1, ArrayView1};
use scirs2_core::random::{rng, Rng, RngExt};
// Unused import
// use scirs2_core::error::CoreResult;

/// Evolutionary strategy optimizer
#[derive(Debug, Clone)]
pub struct EvolutionaryStrategy {
    /// Population size
    pub population_size: usize,
    /// Current population
    pub population: Vec<Array1<f64>>,
    /// Population fitness
    pub fitness: Vec<f64>,
    /// Mutation strength
    pub sigma: f64,
}

impl EvolutionaryStrategy {
    /// Create new evolutionary strategy
    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,
        }
    }

    /// Evaluate population
    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());
        }
    }

    /// Evolve population
    pub fn evolve(&mut self) {
        // Select best half
        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;

        // Generate new population
        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];

            // Mutate
            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;
        }
    }

    /// Get best individual
    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)
    }
}

/// Evolutionary strategy optimization
#[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);

    // Initialize with initial _params
    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, // Population size * _generations
        njev: 0,
        nhev: 0,
        maxcv: 0,
        status: 0,
    })
}

#[allow(dead_code)]
pub fn placeholder() {}