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
//! The search goal to optimize towards (maximize or minimize).
//!
//! Each problem will usually have its own specific [Fitness] function, therefore you need to
//! implement it yourself. Because the [Fitness] function is specific, it is also bound to the
//! [Genotype] through a trait attribute (no reason to make it generic, as the client implements for
//! a single [Genotype] type).
//!
//! See [Fitness] Trait
pub mod placeholders;
pub mod prelude;
use crate::chromosome::Chromosome;
use crate::genotype::Genotype;
use crate::population::Population;
use crate::strategy::{StrategyAction, StrategyState};
use rayon::prelude::*;
use std::cell::RefCell;
use std::time::Instant;
use thread_local::ThreadLocal;
/// Use isize for easy handling of scores (ordering, comparing) as floats are tricky in that regard.
pub type FitnessValue = isize;
#[derive(Copy, Clone, Debug)]
pub enum FitnessOrdering {
Maximize,
Minimize,
}
/// The fitness function, is implemented as a fitness method object.
///
/// Normally the fitness returns [`Some(FitnessValue)`](FitnessValue) for the chromosome, which can be minimized or
/// maximized in the search strategy (e.g. [Evolve](crate::strategy::evolve::Evolve) or
/// [Permutate](crate::strategy::permutate::Permutate)) by providing the [FitnessOrdering].
///
/// If the fitness returns `None`, the chromosome is assumed invalid and taken last in the [competition](crate::compete) phase (also when minimizing).
///
/// # Example:
/// ```rust
/// use genetic_algorithm::fitness::prelude::*;
///
/// #[derive(Clone, Debug)]
/// pub struct CountTrue;
/// impl Fitness for CountTrue {
/// type Genotype = BinaryGenotype;
/// fn calculate_for_chromosome(&mut self, chromosome: &Chromosome<Self::Genotype>) -> Option<FitnessValue> {
/// Some(chromosome.genes.iter().filter(|&value| *value).count() as FitnessValue)
/// }
/// }
/// ```
pub trait Fitness: Clone + Send + Sync + std::fmt::Debug {
type Genotype: Genotype;
fn call_for_state_population<S: StrategyState<Self::Genotype>>(
&mut self,
state: &mut S,
thread_local: Option<&ThreadLocal<RefCell<Self>>>,
) {
let now = Instant::now();
self.call_for_population(state.population_as_mut(), thread_local);
state.add_duration(StrategyAction::Fitness, now.elapsed());
}
fn call_for_state_chromosome<S: StrategyState<Self::Genotype>>(&mut self, state: &mut S) {
let now = Instant::now();
self.call_for_chromosome(state.chromosome_as_mut());
state.add_duration(StrategyAction::Fitness, now.elapsed());
}
/// pass thread_local for external control of fitness caching in multithreading
fn call_for_population(
&mut self,
population: &mut Population<Self::Genotype>,
thread_local: Option<&ThreadLocal<RefCell<Self>>>,
) {
if let Some(thread_local) = thread_local {
population
.chromosomes
.par_iter_mut()
.filter(|c| c.fitness_score.is_none())
.for_each_init(
|| {
thread_local
.get_or(|| std::cell::RefCell::new(self.clone()))
.borrow_mut()
},
|fitness, chromosome| {
fitness.call_for_chromosome(chromosome);
},
);
} else {
population
.chromosomes
.iter_mut()
.filter(|c| c.fitness_score.is_none())
.for_each(|c| self.call_for_chromosome(c));
}
}
fn call_for_chromosome(&mut self, chromosome: &mut Chromosome<Self::Genotype>) {
chromosome.fitness_score = self.calculate_for_chromosome(chromosome);
}
fn calculate_for_chromosome(
&mut self,
chromosome: &Chromosome<Self::Genotype>,
) -> Option<FitnessValue>;
}