From 5b7f65d1a2d32992eb9e05d582bde8f9b270f04b Mon Sep 17 00:00:00 2001 From: Kacper Kafara Date: Fri, 22 Dec 2023 23:09:07 +0100 Subject: [PATCH] feat: add `FixedPoint` crossover (#465) ## Description Added `FixedPoint` crossover operator ## Linked issues Resolves kkafar/master-monorepo#110 ## Important implementation details --- examples/jssp/problem/individual.rs | 2 +- src/ga/individual.rs | 2 +- src/ga/operators/crossover.rs | 70 ++++++++++++++++++++++++++++- 3 files changed, 70 insertions(+), 4 deletions(-) diff --git a/examples/jssp/problem/individual.rs b/examples/jssp/problem/individual.rs index 3559d42..e2f0944 100644 --- a/examples/jssp/problem/individual.rs +++ b/examples/jssp/problem/individual.rs @@ -55,7 +55,7 @@ impl PartialEq for JsspIndividual { impl Eq for JsspIndividual {} impl PartialOrd for JsspIndividual { - #[allow(clippy::incorrect_partial_ord_impl_on_ord_type)] + #[allow(clippy::non_canonical_partial_ord_impl)] fn partial_cmp(&self, other: &Self) -> Option { self.fitness.partial_cmp(&other.fitness) } diff --git a/src/ga/individual.rs b/src/ga/individual.rs index e7e7c41..bcd3504 100644 --- a/src/ga/individual.rs +++ b/src/ga/individual.rs @@ -146,7 +146,7 @@ impl PartialEq for Individual { impl Eq for Individual {} impl PartialOrd for Individual { - #[allow(clippy::incorrect_partial_ord_impl_on_ord_type)] + #[allow(clippy::non_canonical_partial_ord_impl)] fn partial_cmp(&self, other: &Self) -> Option { self.fitness.partial_cmp(&other.fitness) } diff --git a/src/ga/operators/crossover.rs b/src/ga/operators/crossover.rs index 3d5612a..2eaa1cc 100644 --- a/src/ga/operators/crossover.rs +++ b/src/ga/operators/crossover.rs @@ -2,7 +2,7 @@ use itertools::{enumerate, Itertools}; use len_trait::Len; use std::collections::{HashMap, HashSet}; use std::hash::Hash; -use std::ops::Index; +use std::ops::{Index, IndexMut}; use crate::ga::individual::{Chromosome, IndividualTrait}; use push_trait::{Nothing, Push}; @@ -913,11 +913,58 @@ where } } +/// # Fixed point crossover operator +/// +/// Works just like `SinglePoint`, however the cut point is fixed and chosen apriori instead of +/// being random. +struct FixedPoint { + pub cut_point: usize, +} + +impl FixedPoint { + /// Returns new instance of the `FixedPoint` operator. + /// + /// # Arguments + /// + /// * `cut_point` - index of first gene that will be taken from second parent to first child + pub fn new(cut_point: usize) -> Self { + Self { cut_point } + } +} + +impl CrossoverOperator for FixedPoint +where + IndividualT: IndividualTrait, + IndividualT::ChromosomeT: IndexMut + Push, + GeneT: Copy, +{ + /// Returns a tuple of children + /// + /// It works by cutting parent chromosomes in single, fixed point and the acting like a single + /// point crossover. + /// + /// # Arguments + /// + /// * `parent_1` - First parent to take part in recombination + /// * `parent_2` - Second parent to take part in recombination + fn apply(&mut self, parent_1: &IndividualT, parent_2: &IndividualT) -> (IndividualT, IndividualT) { + let mut child_1 = parent_1.clone(); + let mut child_2 = parent_2.clone(); + + for i in self.cut_point..parent_1.chromosome().len() { + child_1.chromosome_mut()[i] = parent_2.chromosome()[i]; + child_2.chromosome_mut()[i] = parent_1.chromosome()[i]; + } + + (child_1, child_2) + } +} + #[cfg(test)] mod test { use crate::ga::individual::IndividualTrait; use crate::ga::operators::crossover::Ppx; - use crate::ga::operators::crossover::{CrossoverOperator, Pmx, Shuffle}; + use crate::ga::operators::crossover::{CrossoverOperator, FixedPoint, Pmx, Shuffle}; use crate::ga::Individual; use std::iter::zip; @@ -976,4 +1023,23 @@ mod test { assert_eq!(g1 + g2, 1); } } + + #[test] + fn fixed_point_works_as_expected() { + let mut op = FixedPoint::new(4); + + let parent_1_chromosome = vec![8, 4, 7, 3, 6, 2, 5, 1, 9, 0]; + let parent_2_chromosome = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + + let p1 = Individual::from(parent_1_chromosome.clone()); + let p2 = Individual::from(parent_2_chromosome.clone()); + + let (child_1, child_2) = op.apply(&p1, &p2); + + let child_1_expected_chromosome = vec![8, 4, 7, 3, 4, 5, 6, 7, 8, 9]; + let child_2_expected_chromosome = vec![0, 1, 2, 3, 6, 2, 5, 1, 9, 0]; + + assert_eq!(child_1.chromosome(), &child_1_expected_chromosome); + assert_eq!(child_2.chromosome(), &child_2_expected_chromosome); + } }