Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: change crossover API & move crossover implementations into separate modules #476

Merged
merged 16 commits into from
Apr 24, 2024
Merged
41 changes: 35 additions & 6 deletions examples/jssp/problem/crossover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use ecrs::{
ga::{individual::IndividualTrait, GAMetadata},
prelude::crossover::CrossoverOperator,
};
use push_trait::PushBack;
use rand::{thread_rng, Rng};

use super::individual::JsspIndividual;
Expand All @@ -16,10 +17,8 @@ impl JsspCrossover {
distr: rand::distributions::Uniform::new(0.0, 1.0),
}
}
}

impl CrossoverOperator<JsspIndividual> for JsspCrossover {
fn apply(
fn apply_single(
&mut self,
_metadata: &GAMetadata,
parent_1: &JsspIndividual,
Expand Down Expand Up @@ -53,16 +52,30 @@ impl CrossoverOperator<JsspIndividual> for JsspCrossover {
}
}

impl CrossoverOperator<JsspIndividual> for JsspCrossover {
fn apply(&mut self, metadata: &GAMetadata, selected: &[&JsspIndividual]) -> Vec<JsspIndividual> {
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
}
}

pub struct NoopCrossover;

impl NoopCrossover {
pub fn new() -> Self {
Self
}
}

impl CrossoverOperator<JsspIndividual> for NoopCrossover {
fn apply(
fn apply_single(
&mut self,
_metadata: &GAMetadata,
parent_1: &JsspIndividual,
Expand All @@ -71,3 +84,19 @@ impl CrossoverOperator<JsspIndividual> for NoopCrossover {
(parent_1.clone(), parent_2.clone())
}
}

impl CrossoverOperator<JsspIndividual> for NoopCrossover {
fn apply(&mut self, metadata: &GAMetadata, selected: &[&JsspIndividual]) -> Vec<JsspIndividual> {
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
}
}
12 changes: 1 addition & 11 deletions src/ga.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,19 +316,9 @@ where
self.metadata.selection_dur = Some(self.timer.elapsed());

// 5. From mating pool create new generation (apply crossover & mutation).
let mut children: Vec<IndividualT> = Vec::with_capacity(self.config.params.population_size);

// FIXME: Do not assume that population size is an even number.
self.timer.start();
for parents in mating_pool.chunks(2) {
let crt_children =
self.config
.crossover_operator
.apply(&self.metadata, parents[0], parents[1]);

children.push(crt_children.0);
children.push(crt_children.1);
}
let mut children = self.config.crossover_operator.apply(&self.metadata, &mating_pool);
self.metadata.crossover_dur = Some(self.timer.elapsed());

self.timer.start();
Expand Down
29 changes: 20 additions & 9 deletions src/ga/operators/crossover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,32 @@ pub use impls::*;

use crate::ga::individual::IndividualTrait;
use crate::ga::GAMetadata;

/// # Crossover Operator
///
/// This trait defines common behaviour for crossover operators.
/// You can implement this trait to provide your custom crossover operator to the GA.
pub trait CrossoverOperator<IndividualT: IndividualTrait> {
/// Returns a tuple of children
/// FIXME: Understand lifetimes here!
// fn apply_iter<'i, InputIter, OutputIter>(
// &mut self,
// metadata: &GAMetadata,
// selected: InputIter,
// ) -> OutputIter
// where
// InputIter: Iterator<Item = &'i IndividualT>,
// OutputIter: Iterator<Item = IndividualT>,
// IndividualT: 'i;

/// Apply crossover operator to the selected population part.
///
/// ## Arguments
///
/// * `parent_1` - First parent to take part in recombination
/// * `parent_2` - Second parent to take part in recombination
fn apply(
&mut self,
metadata: &GAMetadata,
parent_1: &IndividualT,
parent_2: &IndividualT,
) -> (IndividualT, IndividualT);
/// * `metadata` - metadata provided by the GA runtime,
/// * `selected` - result of running selection operator,
///
/// ## Returns
///
/// Vector of individuals created during the crossover stage.
fn apply(&mut self, metadata: &GAMetadata, selected: &[&IndividualT]) -> Vec<IndividualT>;
}
Loading
Loading