Skip to content

Commit

Permalink
feat!: fix Random & add UniqueRandom (#485)
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

## Linked issues <!-- Please use "Resolves #<issue_no> syntax in case
this PR should be linked to an issue -->

Closes #483 
Closes #484

## Important implementation details <!-- if any, optional section -->
  • Loading branch information
kkafar authored May 4, 2024
1 parent 78b8de6 commit 2efb6e2
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 6 deletions.
1 change: 1 addition & 0 deletions src/ga/operators/selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use crate::ga::{individual::IndividualTrait, Metrics};
///
/// * [RouletteWheel]
/// * [Random]
/// * [UniqueRandom]
/// * [Rank]
/// * [RankR]
/// * [Tournament]
Expand Down
81 changes: 75 additions & 6 deletions src/ga/operators/selection/impls.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
use std::{iter::Sum, ops::Index};

use num_traits::{identities::Zero, NumAssignOps};
use rand::{distributions::Standard, prelude::Distribution, rngs::ThreadRng, Rng};
use rand::{
distributions::{Standard, Uniform},
prelude::Distribution,
rngs::ThreadRng,
Rng,
};

use crate::ga::{individual::IndividualTrait, value_provider::ValueProvider, Metrics};

Expand Down Expand Up @@ -101,8 +106,8 @@ where
///
/// Individuals are selected with uniform probability.
///
/// **Note**: The same individual *can not* be selected mutiple times.
pub struct Random<SizeValue: ValueProvider<usize>, R: Rng = ThreadRng> {
/// **Note**: The same individual *can* be selected mutiple times.
pub struct Random<SizeValue: ValueProvider<usize>, R: Rng + Clone = ThreadRng> {
selection_size: SizeValue,
rng: R,
}
Expand All @@ -119,7 +124,7 @@ impl<SizeValue: ValueProvider<usize>> Random<SizeValue, ThreadRng> {
}
}

impl<SizeValue: ValueProvider<usize>, R: Rng> Random<SizeValue, R> {
impl<SizeValue: ValueProvider<usize>, R: Rng + Clone> Random<SizeValue, R> {
/// Returns new instance of [Random] selection operator with custom RNG
///
/// ## Arguments
Expand All @@ -131,21 +136,85 @@ impl<SizeValue: ValueProvider<usize>, R: Rng> Random<SizeValue, R> {
}
}

impl<IndividualT: IndividualTrait, SizeValue: ValueProvider<usize>, R: Rng> SelectionOperator<IndividualT>
for Random<SizeValue, R>
impl<IndividualT: IndividualTrait, SizeValue: ValueProvider<usize>, R: Rng + Clone>
SelectionOperator<IndividualT> for Random<SizeValue, R>
{
/// Returns a vector of references to individuals selected to mating pool.
///
/// Individuals are selected with uniform probability.
///
/// ### Arguments
///
/// * `metrics` - [crate::ga::Metrics] information on current stage of the algorithm (iteration, elapsed time, etc.)
/// * `population` - individuals to choose mating pool from
fn apply<'a>(&mut self, metrics: &Metrics, population: &'a [IndividualT]) -> Vec<&'a IndividualT> {
let count = self.selection_size.get(metrics);
let distr_ind = Uniform::new(0, population.len());
let selection_iter = self
.rng
.clone()
.sample_iter(distr_ind)
.take(count)
.map(|i| &population[i]);
Vec::<&'a IndividualT>::from_iter(selection_iter)

// We must use index API, as we want to return vector of references, not vector of actual items
// let indices = rand::seq::index::sample(&mut self.rng, population.len(), count);
// let mut selected: Vec<&IndividualT> = Vec::with_capacity(count);
//
// for i in indices {
// selected.push(&population[i]);
// }
// selected
}
}

/// ### UniqueRandom selection operator
///
/// This struct implements [SelectionOperator] trait and can be used with GA.
///
/// Individuals are selected with uniform probability.
///
/// **Note**: The same individual *can not* be selected mutiple times.
pub struct UniqueRandom<SizeValue: ValueProvider<usize>, R: Rng = ThreadRng> {
selection_size: SizeValue,
rng: R,
}

impl<SizeValue: ValueProvider<usize>> UniqueRandom<SizeValue, ThreadRng> {
pub fn new(selection_size: SizeValue) -> Self {
Self::with_rng(selection_size, rand::thread_rng())
}
}

impl<SizeValue: ValueProvider<usize>, R: Rng> UniqueRandom<SizeValue, R> {
pub fn with_rng(selection_size: SizeValue, rng: R) -> Self {
Self { selection_size, rng }
}
}

impl<IndividualT: IndividualTrait, SizeValue: ValueProvider<usize>, R: Rng + Clone>
SelectionOperator<IndividualT> for UniqueRandom<SizeValue, R>
{
/// Returns a vector of references to individuals selected to mating pool.
///
/// Individuals are selected with uniform probability.
///
/// **Note**: The same individual *can not* be selected multiple times.
/// **Note**: Selection size must not be greater than population size, in such case
/// this operator panics.
///
/// ### Arguments
///
/// * `metrics` - [crate::ga::Metrics] information on current stage of the algorithm (iteration, elapsed time, etc.)
/// * `population` - individuals to choose mating pool from
///
/// ### Panics
///
/// When selection size is greater than population size.
fn apply<'a>(&mut self, metrics: &Metrics, population: &'a [IndividualT]) -> Vec<&'a IndividualT> {
let count = self.selection_size.get(metrics);

// We must use index API, as we want to return vector of references, not vector of actual items
let indices = rand::seq::index::sample(&mut self.rng, population.len(), count);
let mut selected: Vec<&IndividualT> = Vec::with_capacity(count);
Expand Down

0 comments on commit 2efb6e2

Please sign in to comment.