radiate_core/
problem.rs

1use super::{Chromosome, Codec, Genotype, Score};
2use std::sync::Arc;
3
4/// [Problem] represents the interface for the fitness function or evaluation and encoding/decoding
5/// of a genotype to a phenotype within the genetic algorithm framework.
6///
7/// To run the genetic algorithm the three things that must be supplied are the encoding & decoding of
8/// the [Genotype] and the fitness function. [Problem] wraps all three into a
9/// single trait that can be supplied to the engine builder.
10pub trait Problem<C: Chromosome, T>: Send + Sync {
11    fn encode(&self) -> Genotype<C>;
12    fn decode(&self, genotype: &Genotype<C>) -> T;
13    fn eval(&self, individual: &Genotype<C>) -> Score;
14    fn eval_batch(&self, individuals: &[Genotype<C>]) -> Vec<Score> {
15        individuals.iter().map(|ind| self.eval(ind)).collect()
16    }
17}
18
19/// [EngineProblem] is a generic, base level concrete implementation of the [Problem] trait that is the
20/// default problem used by the engine if none other is specified during building. We take the
21/// [Codec] and the fitness function from the user and simply wrap them into this struct.
22pub struct EngineProblem<C, T>
23where
24    C: Chromosome,
25{
26    pub codec: Arc<dyn Codec<C, T>>,
27    pub fitness_fn: Arc<dyn Fn(T) -> Score + Send + Sync>,
28}
29
30impl<C: Chromosome, T> Problem<C, T> for EngineProblem<C, T> {
31    fn encode(&self) -> Genotype<C> {
32        self.codec.encode()
33    }
34
35    fn decode(&self, genotype: &Genotype<C>) -> T {
36        self.codec.decode(genotype)
37    }
38
39    fn eval(&self, individual: &Genotype<C>) -> Score {
40        let phenotype = self.decode(individual);
41        (self.fitness_fn)(phenotype)
42    }
43}
44
45unsafe impl<C: Chromosome, T> Send for EngineProblem<C, T> {}
46unsafe impl<C: Chromosome, T> Sync for EngineProblem<C, T> {}