use crate::{
core::{context::Context, run_result::RunResult, state::State},
fitness::{FitnessEvaluator, Maximize},
initialization::Initializer,
observer::{NoOp, Observer},
operators::GeneticOperator,
termination::TerminationCondition,
};
use std::{marker::PhantomData, num::NonZero};
pub struct EvolutionaryAlgorithm<G, F, I, T, Fe, Ops, R, C = Maximize> {
initializer: I,
termination: T,
fitness_evaluator: Fe,
operators: Ops,
population_size: NonZero<usize>,
rng: R,
comparator: C,
#[cfg(feature = "parallel")]
runtime: pooled::Runtime,
_marker: PhantomData<(G, F)>,
}
impl EvolutionaryAlgorithm<(), (), (), (), (), (), (), ()> {
pub fn builder(
population_size: NonZero<usize>,
) -> EvolutionaryAlgorithmBuilder<(), (), (), (), (), (), (), ()> {
EvolutionaryAlgorithmBuilder::new(population_size)
}
}
impl<G, F, I, T, Fe, Ops, R, C> EvolutionaryAlgorithm<G, F, I, T, Fe, Ops, R, C>
where
I: Initializer<G, F, Fe, R, C>,
T: TerminationCondition<G, F>,
Fe: FitnessEvaluator<G, F>,
Ops: GeneticOperator<G, F, Fe, R, C>,
{
pub fn new(
initializer: I,
termination: T,
fitness_evaluator: Fe,
operators: Ops,
population_size: NonZero<usize>,
rng: R,
comparator: C,
) -> Self {
Self {
initializer,
termination,
fitness_evaluator,
operators,
population_size,
rng,
comparator,
#[cfg(feature = "parallel")]
runtime: pooled::Runtime::new(
std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1),
),
_marker: PhantomData,
}
}
pub fn run(&mut self) -> RunResult<G, F> {
self.run_with(NoOp::new())
}
pub fn run_with<O>(&mut self, mut observer: O) -> RunResult<G, F>
where
O: Observer<G, F, Fe, R, C>,
{
#[cfg(not(feature = "parallel"))]
let mut ctx = Context::new(&self.fitness_evaluator, &mut self.rng, &self.comparator);
#[cfg(feature = "parallel")]
let mut ctx = Context::new(
&self.fitness_evaluator,
&mut self.rng,
&self.comparator,
&self.runtime,
);
let population = self.initializer.initialize(self.population_size, &mut ctx);
let mut state = State::new(population, 0);
observer.on_start(&state, &ctx);
while !self.termination.should_terminate(&state) {
state.apply_operators(&mut ctx, &mut self.operators);
state.inc_generation();
observer.on_generation(&state, &ctx);
}
observer.on_end(&state, &ctx);
state.into()
}
}
pub struct EvolutionaryAlgorithmBuilder<G, F, I, T, Fe, Ops, R, C> {
initializer: I,
termination: T,
fitness_evaluator: Fe,
operators: Ops,
population_size: NonZero<usize>,
rng: R,
comparator: C,
#[cfg(feature = "parallel")]
runtime: Option<pooled::Runtime>,
_marker: PhantomData<(G, F)>,
}
impl EvolutionaryAlgorithmBuilder<(), (), (), (), (), (), (), ()> {
pub fn new(population_size: NonZero<usize>) -> Self {
Self {
initializer: (),
termination: (),
fitness_evaluator: (),
operators: (),
population_size,
rng: (),
comparator: (),
#[cfg(feature = "parallel")]
runtime: None,
_marker: PhantomData,
}
}
}
impl<G, F, I, T, Fe, Ops, R, C> EvolutionaryAlgorithmBuilder<G, F, I, T, Fe, Ops, R, C> {
pub fn initializer<I2>(
self,
initializer: I2,
) -> EvolutionaryAlgorithmBuilder<G, F, I2, T, Fe, Ops, R, C> {
EvolutionaryAlgorithmBuilder {
initializer,
termination: self.termination,
fitness_evaluator: self.fitness_evaluator,
operators: self.operators,
population_size: self.population_size,
rng: self.rng,
comparator: self.comparator,
#[cfg(feature = "parallel")]
runtime: self.runtime,
_marker: PhantomData,
}
}
pub fn termination<T2>(
self,
termination: T2,
) -> EvolutionaryAlgorithmBuilder<G, F, I, T2, Fe, Ops, R, C> {
EvolutionaryAlgorithmBuilder {
initializer: self.initializer,
termination,
fitness_evaluator: self.fitness_evaluator,
operators: self.operators,
population_size: self.population_size,
rng: self.rng,
comparator: self.comparator,
#[cfg(feature = "parallel")]
runtime: self.runtime,
_marker: PhantomData,
}
}
pub fn fitness<G2, F2, Fe2>(
self,
fitness_evaluator: Fe2,
) -> EvolutionaryAlgorithmBuilder<G2, F2, I, T, Fe2, Ops, R, C>
where
Fe2: FitnessEvaluator<G2, F2>,
{
EvolutionaryAlgorithmBuilder {
initializer: self.initializer,
termination: self.termination,
fitness_evaluator,
operators: self.operators,
population_size: self.population_size,
rng: self.rng,
comparator: self.comparator,
#[cfg(feature = "parallel")]
runtime: self.runtime,
_marker: PhantomData,
}
}
pub fn operators<Ops2>(
self,
operators: Ops2,
) -> EvolutionaryAlgorithmBuilder<G, F, I, T, Fe, Ops2, R, C> {
EvolutionaryAlgorithmBuilder {
initializer: self.initializer,
termination: self.termination,
fitness_evaluator: self.fitness_evaluator,
operators,
population_size: self.population_size,
rng: self.rng,
comparator: self.comparator,
#[cfg(feature = "parallel")]
runtime: self.runtime,
_marker: PhantomData,
}
}
pub fn rng<R2>(self, rng: R2) -> EvolutionaryAlgorithmBuilder<G, F, I, T, Fe, Ops, R2, C> {
EvolutionaryAlgorithmBuilder {
initializer: self.initializer,
termination: self.termination,
fitness_evaluator: self.fitness_evaluator,
operators: self.operators,
population_size: self.population_size,
rng,
comparator: self.comparator,
#[cfg(feature = "parallel")]
runtime: self.runtime,
_marker: PhantomData,
}
}
pub fn comparator<C2>(
self,
comparator: C2,
) -> EvolutionaryAlgorithmBuilder<G, F, I, T, Fe, Ops, R, C2> {
EvolutionaryAlgorithmBuilder {
initializer: self.initializer,
termination: self.termination,
fitness_evaluator: self.fitness_evaluator,
operators: self.operators,
population_size: self.population_size,
rng: self.rng,
comparator,
#[cfg(feature = "parallel")]
runtime: self.runtime,
_marker: PhantomData,
}
}
#[cfg(feature = "parallel")]
pub fn runtime(mut self, runtime: pooled::Runtime) -> Self {
self.runtime = Some(runtime);
self
}
}
impl<G, F, I, T, Fe, Ops, R, C> EvolutionaryAlgorithmBuilder<G, F, I, T, Fe, Ops, R, C>
where
I: Initializer<G, F, Fe, R, C>,
T: TerminationCondition<G, F>,
Fe: FitnessEvaluator<G, F>,
Ops: GeneticOperator<G, F, Fe, R, C>,
{
pub fn build(self) -> EvolutionaryAlgorithm<G, F, I, T, Fe, Ops, R, C> {
EvolutionaryAlgorithm {
initializer: self.initializer,
termination: self.termination,
fitness_evaluator: self.fitness_evaluator,
operators: self.operators,
population_size: self.population_size,
rng: self.rng,
comparator: self.comparator,
#[cfg(feature = "parallel")]
runtime: self.runtime.unwrap_or_else(|| {
pooled::Runtime::new(
std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1),
)
}),
_marker: PhantomData,
}
}
}
impl<G, F, I, T, Fe, Ops, R, C> std::fmt::Debug for EvolutionaryAlgorithm<G, F, I, T, Fe, Ops, R, C>
where
I: std::fmt::Debug,
T: std::fmt::Debug,
Fe: std::fmt::Debug,
Ops: std::fmt::Debug,
R: std::fmt::Debug,
C: std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EvolutionaryAlgorithm")
.field("initializer", &self.initializer)
.field("termination", &self.termination)
.field("fitness_evaluator", &self.fitness_evaluator)
.field("operators", &self.operators)
.field("population_size", &self.population_size)
.field("rng", &self.rng)
.field("comparator", &self.comparator)
.finish_non_exhaustive()
}
}
impl<G, F, I, T, Fe, Ops, R, C> std::fmt::Debug
for EvolutionaryAlgorithmBuilder<G, F, I, T, Fe, Ops, R, C>
where
I: std::fmt::Debug,
T: std::fmt::Debug,
Fe: std::fmt::Debug,
Ops: std::fmt::Debug,
R: std::fmt::Debug,
C: std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EvolutionaryAlgorithmBuilder")
.field("initializer", &self.initializer)
.field("termination", &self.termination)
.field("fitness_evaluator", &self.fitness_evaluator)
.field("operators", &self.operators)
.field("population_size", &self.population_size)
.field("rng", &self.rng)
.field("comparator", &self.comparator)
.finish_non_exhaustive()
}
}