Skip to content

Commit

Permalink
Migrate rest of operators, examples & tests
Browse files Browse the repository at this point in the history
  • Loading branch information
kkafar committed Apr 24, 2024
1 parent 411add4 commit 3de3a70
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 86 deletions.
32 changes: 20 additions & 12 deletions examples/jssp/problem/crossover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@ impl JsspCrossover {
distr: rand::distributions::Uniform::new(0.0, 1.0),
}
}
}

impl CrossoverOperator<JsspIndividual> for JsspCrossover {
fn apply_legacy(
fn apply_single(
&mut self,
_metadata: &GAMetadata,
parent_1: &JsspIndividual,
Expand Down Expand Up @@ -52,19 +50,25 @@ impl CrossoverOperator<JsspIndividual> for JsspCrossover {

(child_1, child_2)
}
}

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

Expand All @@ -74,29 +78,33 @@ impl NoopCrossover {
pub fn new() -> Self {
Self
}
}

impl CrossoverOperator<JsspIndividual> for NoopCrossover {
fn apply_legacy(
fn apply_single(
&mut self,
_metadata: &GAMetadata,
parent_1: &JsspIndividual,
parent_2: &JsspIndividual,
) -> (JsspIndividual, JsspIndividual) {
(parent_1.clone(), parent_2.clone())
}
}

impl CrossoverOperator<JsspIndividual> for NoopCrossover {
fn apply(
&mut self,
metadata: &GAMetadata,
selected: &[&JsspIndividual],
output: &mut Vec<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_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
}
}
5 changes: 2 additions & 3 deletions src/ga.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,13 +316,12 @@ 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();
self.config
let mut children = self.config
.crossover_operator
.apply(&self.metadata, &mating_pool, &mut children);
.apply(&self.metadata, &mating_pool);
self.metadata.crossover_dur = Some(self.timer.elapsed());

self.timer.start();
Expand Down
12 changes: 6 additions & 6 deletions src/ga/operators/crossover/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ mod test {
let p2 = Individual::from(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);

let children = op.apply(&GAMetadata::default(), &[&p1, &p2]);
let child_1 = children[0];
let child_2 = children[1];
let child_1 = &children[0];
let child_2 = &children[1];
assert_eq!(child_1.chromosome.len(), 10);
assert_eq!(child_2.chromosome.len(), 10);
}
Expand All @@ -81,8 +81,8 @@ mod test {
let p2 = Individual::from(vec![0, 1, 1, 0, 1, 0, 1, 0, 1, 1]);

let children = op.apply(&GAMetadata::default(), &[&p1, &p2]);
let child_1 = children[0];
let child_2 = children[1];
let child_1 = &children[0];
let child_2 = &children[1];
for (g1, g2) in child_1.chromosome.iter().zip(child_2.chromosome.iter()) {
assert_eq!(g1 * g2, 0);
assert_eq!(g1 + g2, 1);
Expand All @@ -100,8 +100,8 @@ mod test {
let p2 = Individual::from(parent_2_chromosome.clone());

let children = op.apply(&GAMetadata::default(), &[&p1, &p2]);
let child_1 = children[0];
let child_2 = children[1];
let child_1 = &children[0];
let child_2 = &children[1];

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];
Expand Down
43 changes: 32 additions & 11 deletions src/ga/operators/crossover/impls/fixed_point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,7 @@ impl FixedPoint {
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
Expand All @@ -42,12 +35,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: IndexMut<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy,
{
let mut child_1 = parent_1.clone();
let mut child_2 = parent_2.clone();

Expand All @@ -59,12 +57,35 @@ where
(child_1, child_2)
}

fn apply(&mut self, metadata: &GAMetadata, selected: &[&IndividualT], output: &mut Vec<IndividualT>) {
}

impl<GeneT, IndividualT> CrossoverOperator<IndividualT> for FixedPoint
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: IndexMut<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy,
{
/// Returns vector of owned individuals which were created in result of applying crossover
/// operator.
///
/// It works by cutting parent chromosomes in single, fixed point and the acting like a single
/// point crossover.
///
/// ## 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: 34 additions & 12 deletions src/ga/operators/crossover/impls/multi_point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,7 @@ impl<R: Rng> MultiPoint<R> {
assert!(cut_points_no >= 1, "Number of cut points must be >= 1");
Self { cut_points_no, rng }
}
}

impl<GeneT, IndividualT, R> CrossoverOperator<IndividualT> for MultiPoint<R>
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy,
R: Rng,
{
/// Returns a tuple of children
///
/// It works analogously to [SinglePoint] or [TwoPoint]. One important difference is that
Expand All @@ -70,12 +62,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 @@ -120,12 +117,37 @@ 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 MultiPoint<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 analogously to [SinglePoint] or [TwoPoint]. One important difference is that
/// all cutpoints are distinct, thus single or two point crossover with guarantee of distinct cutpoints
/// can be achieved.
///
/// ## 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
}
}
54 changes: 42 additions & 12 deletions src/ga/operators/crossover/impls/ordered.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,7 @@ impl<R: Rng> OrderedCrossover<R> {
}
IndividualT::from(child_ch)
}
}

impl<GeneT, IndividualT, R> CrossoverOperator<IndividualT> for OrderedCrossover<R>
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy + Eq + Hash,
R: Rng,
{
/// Returns a tuple of children, first child is created by taking a substring from parent_1,
/// second child is created by using a substring from parent_2
///
Expand All @@ -115,12 +107,17 @@ where
///
/// * `parent_1` - First parent to take part in crossover
/// * `parent_2` - Second parent to take part in crossover
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 + Eq + Hash,
{
assert_eq!(
parent_1.chromosome().len(),
parent_2.chromosome().len(),
Expand All @@ -137,13 +134,46 @@ where

(child_1, child_2)
}
}

fn apply(&mut self, metadata: &GAMetadata, selected: &[&IndividualT], output: &mut Vec<IndividualT>) {
impl<GeneT, IndividualT, R> CrossoverOperator<IndividualT> for OrderedCrossover<R>
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy + Eq + Hash,
R: Rng,
{
/// Returns vector of owned individuals which were created in result of applying crossover
/// operator.
///
/// First child is created by taking a substring from parent_1,
/// second child is created by using a substring from parent_2
///
/// It works by taking a substring from one parent, and filling the missing places with alleles from
/// second parent in the order they appear in.
///
/// P1 : 2 <b>4 1 3</b> 5 <br>
/// P2 : 5 2 1 4 3 <br>
/// Ch : 5 <b>4 1 3</b> 2
///
/// Degenerated case when substring has length equal to genome length 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
}
}
Loading

0 comments on commit 3de3a70

Please sign in to comment.