Skip to content

Commit

Permalink
Migrate another few crossover impls
Browse files Browse the repository at this point in the history
  • Loading branch information
kkafar committed Apr 24, 2024
1 parent f24f31c commit 411add4
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 52 deletions.
55 changes: 40 additions & 15 deletions src/ga/operators/crossover/impls/shuffle.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
use itertools::{enumerate, Itertools};
use len_trait::Len;


use std::ops::{Index};

use crate::ga::individual::{IndividualTrait};
use crate::ga::individual::IndividualTrait;
use crate::ga::GAMetadata;
use push_trait::{Nothing, Push};
use rand::prelude::SliceRandom;
use rand::{rngs::ThreadRng, Rng};
use std::ops::Index;

use super::CrossoverOperator;

Expand Down Expand Up @@ -41,13 +39,7 @@ impl<R: Rng> Shuffle<R> {
}
}

impl<GeneT, IndividualT, R> CrossoverOperator<IndividualT> for Shuffle<R>
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy,
R: Rng,
{
impl<R: Rng> Shuffle<R> {
/// Returns a tuple of children
///
/// It works by randomly shuffling both parents chromosome the same way then
Expand All @@ -62,12 +54,17 @@ where
///
/// * `parent_1` - First parent to take part in recombination
/// * `parent_2` - Second parent to take part in recombination
fn apply_legacy(
fn apply_single<GeneT, IndividualT>(
&mut self,
_metadata: &GAMetadata,
parent_1: &IndividualT,
parent_2: &IndividualT,
) -> (IndividualT, IndividualT) {
) -> (IndividualT, IndividualT)
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy,
{
let chromosome_len = parent_1.chromosome().len();
let cut_point = self.rng.gen_range(0..chromosome_len);

Expand All @@ -90,13 +87,41 @@ where

(IndividualT::from(child_1_ch), IndividualT::from(child_2_ch))
}
}

fn apply(&mut self, metadata: &GAMetadata, selected: &[&IndividualT], output: &mut Vec<IndividualT>) {
impl<GeneT, IndividualT, R> CrossoverOperator<IndividualT> for Shuffle<R>
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy,
R: Rng,
{
/// Returns vector of owned individuals which were created in result of applying crossover
/// operator.
///
/// It works by randomly shuffling both parents chromosome the same way then
/// selecting single cutpoint splitting both parents shuffled chromosomes in two parts.
/// First child gets `parent_1`'s first part and `parent_2`'s second part.
/// Second child gets `parent_2`'s first part and `parent_1`'s second part.
/// Lastly childs chromosomes are de-shuffled.
///
/// Degenerated case when cutpoint is selected at index 0 or last can occur.
///
/// ## Arguments
///
/// * `metadata` - algorithm state metadata, see the structure details for more info,
/// * `selected` - references to individuals selected during selection step.
fn apply(&mut self, metadata: &GAMetadata, selected: &[&IndividualT]) -> Vec<IndividualT> {
assert!(selected.len() & 1 == 0);

let mut output = Vec::with_capacity(selected.len());

for parents in selected.chunks(2) {
let (child_1, child_2) = self.apply_legacy(metadata, parents[0], parents[1]);
let (child_1, child_2) = self.apply_single(metadata, parents[0], parents[1]);
output.push(child_1);
output.push(child_2);
}

output
}
}
51 changes: 38 additions & 13 deletions src/ga/operators/crossover/impls/single_point.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use len_trait::Len;
use std::ops::{Index};
use std::ops::Index;

use crate::ga::individual::{IndividualTrait};
use crate::ga::individual::IndividualTrait;
use crate::ga::GAMetadata;
use push_trait::{Nothing, Push};

Expand Down Expand Up @@ -36,13 +36,7 @@ impl<R: Rng> SinglePoint<R> {
}
}

impl<GeneT, IndividualT, R> CrossoverOperator<IndividualT> for SinglePoint<R>
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy,
R: Rng,
{
impl<R: Rng> SinglePoint<R> {
/// Returns a tuple of children
///
/// It works by randomly selecting single cutpoint splitting both parent chromosomes in two parts.
Expand All @@ -55,12 +49,17 @@ where
///
/// * `parent_1` - First parent to take part in recombination
/// * `parent_2` - Second parent to take part in recombination
fn apply_legacy(
fn apply_single<GeneT, IndividualT>(
&mut self,
_metadata: &GAMetadata,
parent_1: &IndividualT,
parent_2: &IndividualT,
) -> (IndividualT, IndividualT) {
) -> (IndividualT, IndividualT)
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy,
{
let chromosome_len = parent_1.chromosome().len();
let cut_point = self.rng.gen_range(0..chromosome_len);

Expand All @@ -79,13 +78,39 @@ where

(IndividualT::from(child_1_ch), IndividualT::from(child_2_ch))
}
}

fn apply(&mut self, metadata: &GAMetadata, selected: &[&IndividualT], output: &mut Vec<IndividualT>) {
impl<GeneT, IndividualT, R> CrossoverOperator<IndividualT> for SinglePoint<R>
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy,
R: Rng,
{
/// Returns vector of owned individuals which were created in result of applying crossover
/// operator.
///
/// It works by randomly selecting single cutpoint splitting both parent chromosomes in two parts.
/// First child gets `parent_1`'s first part and `parent_2`'s second part.
/// Second child gets `parent_2`'s first part and `parent_1`'s second part.
///
/// Degenerated case when cutpoint is selected at index 0 or last can occur.
///
/// ## Arguments
///
/// * `metadata` - algorithm state metadata, see the structure details for more info,
/// * `selected` - references to individuals selected during selection step.
fn apply(&mut self, metadata: &GAMetadata, selected: &[&IndividualT]) -> Vec<IndividualT> {
assert!(selected.len() & 1 == 0);

let mut output = Vec::with_capacity(selected.len());

for parents in selected.chunks(2) {
let (child_1, child_2) = self.apply_legacy(metadata, parents[0], parents[1]);
let (child_1, child_2) = self.apply_single(metadata, parents[0], parents[1]);
output.push(child_1);
output.push(child_2);
}

output
}
}
46 changes: 36 additions & 10 deletions src/ga/operators/crossover/impls/two_point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,7 @@ impl<R: Rng> TwoPoint<R> {
}
}

impl<GeneT, IndividualT, R> CrossoverOperator<IndividualT> for TwoPoint<R>
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy,
R: Rng,
impl<R: Rng> TwoPoint<R>
{
/// Returns a tuple of children
///
Expand All @@ -56,12 +51,17 @@ where
///
/// * `parent_1` - First parent to take part in recombination
/// * `parent_2` - Second parent to take part in recombination
fn apply_legacy(
fn apply_single<GeneT, IndividualT>(
&mut self,
_metadata: &GAMetadata,
parent_1: &IndividualT,
parent_2: &IndividualT,
) -> (IndividualT, IndividualT) {
) -> (IndividualT, IndividualT)
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy,
{
assert_eq!(
parent_1.chromosome().len(),
parent_2.chromosome().len(),
Expand Down Expand Up @@ -101,13 +101,39 @@ where

(IndividualT::from(child_1_ch), IndividualT::from(child_2_ch))
}
}

fn apply(&mut self, metadata: &GAMetadata, selected: &[&IndividualT], output: &mut Vec<IndividualT>) {
impl<GeneT, IndividualT, R> CrossoverOperator<IndividualT> for TwoPoint<R>
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy,
R: Rng,
{
/// Returns vector of owned individuals which were created in result of applying crossover
/// operator.
///
/// It works by randomly selecting two cutpoints splitting parents chromosomes in three parts.
/// Then it creates children by taking parents chromosome parts interchangeably.
/// Its mechanism is analoguous to [SinglePoint].
///
/// Degenerate case when both cutpoints are in the same place or at position 0 or last can occur.
///
/// ## Arguments
///
/// * `metadata` - algorithm state metadata, see the structure details for more info,
/// * `selected` - references to individuals selected during selection step.
fn apply(&mut self, metadata: &GAMetadata, selected: &[&IndividualT]) -> Vec<IndividualT> {
assert!(selected.len() & 1 == 0);

let mut output = Vec::with_capacity(selected.len());

for parents in selected.chunks(2) {
let (child_1, child_2) = self.apply_legacy(metadata, parents[0], parents[1]);
let (child_1, child_2) = self.apply_single(metadata, parents[0], parents[1]);
output.push(child_1);
output.push(child_2);
}

output
}
}
22 changes: 14 additions & 8 deletions src/ga/operators/crossover/impls/uniform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,8 @@ impl<R: Rng + Clone> Uniform<R> {
}
}

impl<GeneT, IndividualT, R> Uniform<R>
impl<R> Uniform<R>
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy,
R: Rng + Clone,
{
/// Returns a tuple of children
Expand All @@ -50,12 +47,17 @@ where
///
/// * `parent_1` - First parent to take part in recombination
/// * `parent_2` - Second parent to take part in recombination
fn apply_single(
fn apply_single<GeneT, IndividualT>(
&mut self,
_metadata: &GAMetadata,
parent_1: &IndividualT,
parent_2: &IndividualT,
) -> (IndividualT, IndividualT) {
) -> (IndividualT, IndividualT)
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy,
{
assert_eq!(
parent_1.chromosome().len(),
parent_2.chromosome().len(),
Expand Down Expand Up @@ -85,7 +87,6 @@ where

(IndividualT::from(child_1_ch), IndividualT::from(child_2_ch))
}

}

impl<GeneT, IndividualT, R> CrossoverOperator<IndividualT> for Uniform<R>
Expand All @@ -105,12 +106,17 @@ where
///
/// * `metadata` - algorithm state metadata, see the structure details for more info,
/// * `selected` - references to individuals selected during selection step.
fn apply(&mut self, metadata: &GAMetadata, selected: &[&IndividualT], output: &mut Vec<IndividualT>) {
fn apply(&mut self, metadata: &GAMetadata, selected: &[&IndividualT]) -> Vec<IndividualT> {
assert!(selected.len() & 1 == 0);

let mut output = Vec::with_capacity(selected.len());

for parents in selected.chunks(2) {
let (child_1, child_2) = self.apply_single(metadata, parents[0], parents[1]);
output.push(child_1);
output.push(child_2);
}

output
}
}
14 changes: 8 additions & 6 deletions src/ga/operators/crossover/impls/uniform_parameterized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,8 @@ impl<R: Rng> UniformParameterized<R> {
}
}

impl<GeneT, IndividualT, R> UniformParameterized<R>
impl<R> UniformParameterized<R>
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy,
R: Rng + Clone,
{
/// Returns a tuple of children
Expand All @@ -56,12 +53,17 @@ where
///
/// * `parent_1` - First parent to take part in recombination
/// * `parent_2` - Second parent to take part in recombination
fn apply_single(
fn apply_single<GeneT, IndividualT>(
&mut self,
_metadata: &GAMetadata,
parent_1: &IndividualT,
parent_2: &IndividualT,
) -> (IndividualT, IndividualT) {
) -> (IndividualT, IndividualT)
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy,
{
assert_eq!(
parent_1.chromosome().len(),
parent_2.chromosome().len(),
Expand Down

0 comments on commit 411add4

Please sign in to comment.