use derive_builder::Builder;
use ndarray::Array2;
use thiserror::Error;
use std::sync::Arc;
use crate::{
duplicates::{NoDuplicatesCleaner, PopulationCleaner},
genetic::{D01, D12, Population},
operators::repair::{NoRepair, RepairOperator},
operators::{CrossoverOperator, MutationOperator, SelectionOperator},
random::RandomGenerator,
};
#[derive(Debug, Clone, Builder)]
#[builder(pattern = "owned")]
pub struct Evolve<Sel, Cross, Mut>
where
Sel: SelectionOperator,
Cross: CrossoverOperator,
Mut: MutationOperator,
{
selection: Sel,
crossover: Cross,
mutation: Mut,
#[builder(default = "Arc::new(NoDuplicatesCleaner)")]
pub duplicates_cleaner: Arc<dyn PopulationCleaner>,
#[builder(default = "Arc::new(NoRepair)")]
repair: Arc<dyn RepairOperator>,
mutation_rate: f64,
crossover_rate: f64,
lower_bound: Option<f64>,
upper_bound: Option<f64>,
}
#[derive(Debug, Error)]
pub enum EvolveError {
#[error("No offsprings were generated in the mating process")]
EmptyMatingResult,
}
impl<Sel, Cross, Mut> Evolve<Sel, Cross, Mut>
where
Sel: SelectionOperator,
Cross: CrossoverOperator,
Mut: MutationOperator,
{
fn mating_batch(
&self,
parents_a: &Array2<f64>,
parents_b: &Array2<f64>,
rng: &mut impl RandomGenerator,
) -> Array2<f64> {
let mut offsprings = self
.crossover
.operate(parents_a, parents_b, self.crossover_rate, rng);
self.mutation
.operate(&mut offsprings, self.mutation_rate, rng);
self.repair.operate(&mut offsprings);
if let Some(lb) = self.lower_bound {
for x in offsprings.iter_mut() {
*x = (*x).max(lb);
}
}
if let Some(ub) = self.upper_bound {
for x in offsprings.iter_mut() {
*x = (*x).min(ub);
}
}
offsprings
}
pub fn evolve<ConstrDim>(
&self,
population: &Population<Sel::FDim, ConstrDim>,
num_offsprings: usize,
max_iter: usize,
rng: &mut impl RandomGenerator,
) -> Result<Array2<f64>, EvolveError>
where
ConstrDim: D12,
<Sel::FDim as ndarray::Dimension>::Smaller: D01,
<ConstrDim as ndarray::Dimension>::Smaller: D01,
{
let mut all_offsprings: Vec<Vec<f64>> = Vec::with_capacity(num_offsprings);
let num_genes = population.genes.ncols();
let mut iterations = 0;
while all_offsprings.len() < num_offsprings && iterations < max_iter {
let remaining = num_offsprings - all_offsprings.len();
let crossover_needed = remaining / 2 + 1;
let (parents_a, parents_b) = self.selection.operate(population, crossover_needed, rng);
let mut new_offsprings = self.mating_batch(&parents_a.genes, &parents_b.genes, rng);
new_offsprings = (self.duplicates_cleaner).remove(new_offsprings, None);
new_offsprings =
(self.duplicates_cleaner).remove(new_offsprings, Some(&population.genes));
if !all_offsprings.is_empty() {
let acc_array = Array2::<f64>::from_shape_vec(
(all_offsprings.len(), num_genes),
all_offsprings.iter().flatten().cloned().collect(),
)
.expect("Failed to create accumulator array");
new_offsprings = (self.duplicates_cleaner).remove(new_offsprings, Some(&acc_array));
}
for row in new_offsprings.outer_iter() {
if all_offsprings.len() >= num_offsprings {
break;
}
all_offsprings.push(row.to_vec());
}
iterations += 1;
}
if all_offsprings.is_empty() {
return Err(EvolveError::EmptyMatingResult);
}
let all_offsprings_len = all_offsprings.len();
let offspring_data: Vec<f64> = all_offsprings.into_iter().flatten().collect();
let offspring_array =
Array2::<f64>::from_shape_vec((all_offsprings_len, num_genes), offspring_data)
.expect("Failed to create offspring array from the accumulated data");
if offspring_array.nrows() < num_offsprings {
println!(
"Warning: Only {} offspring were generated out of the desired {}.",
offspring_array.nrows(),
num_offsprings
);
}
Ok(offspring_array)
}
}