Skip to content

Commit

Permalink
feat: add FixedPoint crossover (#465)
Browse files Browse the repository at this point in the history
<!-- If applicable - remember to add the PR to the EA Rust project (ONLY
IF THERE IS NO LINKED ISSUE) -->

## Description

Added `FixedPoint` crossover operator


## Linked issues

Resolves kkafar/master-monorepo#110


## Important implementation details

<!-- if any, optional section -->
  • Loading branch information
kkafar authored Dec 22, 2023
1 parent 77cc30b commit 5b7f65d
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 4 deletions.
2 changes: 1 addition & 1 deletion examples/jssp/problem/individual.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::cmp::Ordering> {
self.fitness.partial_cmp(&other.fitness)
}
Expand Down
2 changes: 1 addition & 1 deletion src/ga/individual.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ impl<T: Chromosome> PartialEq<Self> for Individual<T> {
impl<T: Chromosome> Eq for Individual<T> {}

impl<T: Chromosome> PartialOrd<Self> for Individual<T> {
#[allow(clippy::incorrect_partial_ord_impl_on_ord_type)]
#[allow(clippy::non_canonical_partial_ord_impl)]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.fitness.partial_cmp(&other.fitness)
}
Expand Down
70 changes: 68 additions & 2 deletions src/ga/operators/crossover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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<GeneT, IndividualT> CrossoverOperator<IndividualT> for FixedPoint
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: IndexMut<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
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;

Expand Down Expand Up @@ -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);
}
}

0 comments on commit 5b7f65d

Please sign in to comment.