use crate::chromosome::Chromosome;
use crate::fitness::FitnessFunction;
#[derive(Debug, Clone)]
pub struct MkTrap {
k: usize,
}
impl MkTrap {
pub fn new(k: usize) -> Self {
assert!(k > 0, "Trap size k must be greater than 0");
Self { k }
}
fn trap_function(&self, ones: usize) -> f64 {
const F_HIGH: f64 = 1.0;
const F_LOW: f64 = 0.8;
if ones == self.k {
F_HIGH
} else {
F_LOW - (ones as f64) * F_LOW / ((self.k - 1) as f64)
}
}
}
impl FitnessFunction for MkTrap {
fn evaluate(&self, chromosome: &Chromosome) -> f64 {
let length = chromosome.length();
assert_eq!(
length % self.k,
0,
"Chromosome length must be divisible by trap size k"
);
let num_blocks = length / self.k;
(0..num_blocks)
.map(|block| {
let start = block * self.k;
let ones = (start..(start + self.k))
.filter(|&i| chromosome.get_gene(i))
.count();
self.trap_function(ones)
})
.sum()
}
fn optimum(&self, length: usize) -> f64 {
(length / self.k) as f64
}
}
#[derive(Debug, Clone, Copy)]
pub struct FoldedTrap;
impl FoldedTrap {
const K: usize = 6;
fn trap_function(&self, ones: usize) -> f64 {
if ones == Self::K {
Self::K as f64
} else {
(Self::K - 1 - ones) as f64
}
}
}
impl FitnessFunction for FoldedTrap {
fn evaluate(&self, chromosome: &Chromosome) -> f64 {
let length = chromosome.length();
assert_eq!(
length % Self::K,
0,
"Chromosome length must be divisible by 6"
);
let num_blocks = length / Self::K;
(0..num_blocks)
.map(|block| {
let start = block * Self::K;
let ones = (start..(start + Self::K))
.filter(|&i| chromosome.get_gene(i))
.count();
self.trap_function(ones)
})
.sum()
}
fn optimum(&self, length: usize) -> f64 {
(length / Self::K) as f64 * Self::K as f64
}
}
#[derive(Debug, Clone)]
pub struct CyclicTrap {
k: usize,
}
impl CyclicTrap {
pub fn new(k: usize) -> Self {
assert!(k > 1, "Trap size k must be greater than 1");
Self { k }
}
fn trap_function(&self, ones: usize) -> f64 {
const F_HIGH: f64 = 1.0;
const F_LOW: f64 = 0.8;
if ones == self.k {
F_HIGH
} else {
F_LOW - (ones as f64) * F_LOW / ((self.k - 1) as f64)
}
}
}
impl FitnessFunction for CyclicTrap {
fn evaluate(&self, chromosome: &Chromosome) -> f64 {
let length = chromosome.length();
let step = self.k - 1;
(0..length)
.step_by(step)
.map(|block_start| {
let ones = (0..self.k)
.filter(|&offset| {
let index = (block_start + offset) % length;
chromosome.get_gene(index)
})
.count();
self.trap_function(ones)
})
.sum()
}
fn optimum(&self, length: usize) -> f64 {
let step = self.k - 1;
let num_blocks = length / step;
num_blocks as f64
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::ZobristKeys;
#[test]
fn test_mk_trap() {
let zobrist = ZobristKeys::new(15, 42);
let mut chromosome = Chromosome::new(15, &zobrist);
let fitness_fn = MkTrap::new(5);
for i in 0..5 {
chromosome.set_gene(i, true, &zobrist);
}
chromosome.set_gene(10, true, &zobrist);
chromosome.set_gene(11, true, &zobrist);
let fitness = fitness_fn.evaluate(&chromosome);
assert_eq!(fitness, 2.2);
}
#[test]
fn test_folded_trap() {
let zobrist = ZobristKeys::new(6, 42);
let mut chromosome = Chromosome::new(6, &zobrist);
let fitness_fn = FoldedTrap;
for i in 0..6 {
chromosome.set_gene(i, true, &zobrist);
}
assert_eq!(fitness_fn.evaluate(&chromosome), 6.0);
assert_eq!(fitness_fn.optimum(6), 6.0);
}
}