pub trait FitnessEvaluator<G, F> {
fn evaluate(&self, genome: &G) -> F;
}
impl<G, F, E> FitnessEvaluator<G, F> for E
where
E: Fn(&G) -> F,
{
fn evaluate(&self, genome: &G) -> F {
self(genome)
}
}
pub trait FitnessComparator<F> {
fn is_better(&self, f1: &F, f2: &F) -> bool;
}
impl<F, C> FitnessComparator<F> for C
where
C: Fn(&F, &F) -> bool,
{
fn is_better(&self, f1: &F, f2: &F) -> bool {
self(f1, f2)
}
}
#[derive(Debug, Clone, Copy)]
pub struct Maximize;
impl<F> FitnessComparator<F> for Maximize
where
F: PartialOrd,
{
fn is_better(&self, f1: &F, f2: &F) -> bool {
f1 > f2
}
}
#[derive(Debug, Clone, Copy)]
pub struct Minimize;
impl<F> FitnessComparator<F> for Minimize
where
F: PartialOrd,
{
fn is_better(&self, f1: &F, f2: &F) -> bool {
f1 < f2
}
}
use std::marker::PhantomData;
use crate::grammar::grammar_def::GrammarDef;
use crate::grammar::mapper::{Codon, map};
use crate::phenotype::PhenotypeBuilder;
#[derive(Debug)]
pub struct GeFitness<G, C, F, E, B> {
grammar: G,
max_wraps: usize,
evaluator: E,
penalty: F,
_marker: PhantomData<(C, B)>,
}
impl<G, C, F, E, B> GeFitness<G, C, F, E, B> {
pub fn new(grammar: G, max_wraps: usize, evaluator: E, penalty: F) -> Self {
Self {
grammar,
max_wraps,
evaluator,
penalty,
_marker: PhantomData,
}
}
pub fn phenotype(&self, genome: &[C]) -> Option<B::Output>
where
G: GrammarDef,
G::Terminal: Clone,
C: Codon,
B: PhenotypeBuilder<G::Terminal> + Default,
{
map(&self.grammar, genome, self.max_wraps, B::default())
}
}
impl<G, C, F, E, B> FitnessEvaluator<Vec<C>, F> for GeFitness<G, C, F, E, B>
where
G: GrammarDef,
G::Terminal: Clone,
C: Codon,
F: Clone,
B: PhenotypeBuilder<G::Terminal> + Default,
E: Fn(&B::Output) -> F,
{
fn evaluate(&self, genome: &Vec<C>) -> F {
match map(&self.grammar, genome, self.max_wraps, B::default()) {
Some(phenotype) => (self.evaluator)(&phenotype),
None => self.penalty.clone(),
}
}
}
#[cfg(test)]
mod ge_fitness_tests {
use super::*;
use crate::grammar::Grammar;
use crate::phenotype::Event;
#[derive(Debug, PartialEq)]
struct Program(String);
impl Program {
fn run(&self, _: &()) -> String {
self.0.clone()
}
}
#[derive(Default)]
struct ProgramBuilder(String);
impl PhenotypeBuilder<&'static str> for ProgramBuilder {
type Output = Program;
fn push(&mut self, event: Event<&'static str>) {
if let Event::Terminal(val) = event {
self.0.push_str(val);
}
}
fn finish(self) -> Program {
Program(self.0)
}
}
fn simple_grammar() -> Grammar<&'static str> {
Grammar::builder()
.rule("expr", &[&["x"], &["y"]])
.start("expr")
.build()
}
#[test]
fn valid_mapping_calls_evaluator() {
let fitness = GeFitness::<_, u8, f64, _, ProgramBuilder>::new(
simple_grammar(),
0,
|p: &Program| p.run(&()).len() as f64,
-1.0,
);
assert_eq!(fitness.evaluate(&vec![0u8]), 1.0);
}
#[test]
fn invalid_mapping_returns_penalty() {
let g = Grammar::builder()
.rule("expr", &[&["expr", "op", "expr"], &["x"]])
.rule("op", &[&["+"], &["-"]])
.start("expr")
.build();
let fitness = GeFitness::<_, u8, f64, _, ProgramBuilder>::new(
g,
0,
|p: &Program| p.run(&()).len() as f64,
-99.0,
);
assert_eq!(fitness.evaluate(&vec![0u8]), -99.0);
}
}