use itertools::{enumerate, Itertools};
use len_trait::Len;
use std::collections::{HashMap, HashSet};
use std::hash::Hash;
use std::ops::Index;
use crate::ga::individual::{Chromosome, IndividualTrait};
use push_trait::{Nothing, Push};
use rand::prelude::SliceRandom;
use rand::{rngs::ThreadRng, Rng};
pub trait CrossoverOperator<IndividualT: IndividualTrait> {
fn apply(&mut self, parent_1: &IndividualT, parent_2: &IndividualT) -> (IndividualT, IndividualT);
}
pub struct SinglePoint<R: Rng = ThreadRng> {
rng: R,
}
impl SinglePoint<ThreadRng> {
pub fn new() -> Self {
Self::with_rng(rand::thread_rng())
}
}
impl<R: Rng> SinglePoint<R> {
pub fn with_rng(rng: R) -> Self {
Self { rng }
}
}
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,
{
fn apply(&mut self, parent_1: &IndividualT, parent_2: &IndividualT) -> (IndividualT, IndividualT) {
let chromosome_len = parent_1.chromosome().len();
let cut_point = self.rng.gen_range(0..chromosome_len);
let mut child_1_ch = IndividualT::ChromosomeT::default();
let mut child_2_ch = IndividualT::ChromosomeT::default();
for locus in 0..cut_point {
child_1_ch.push(parent_1.chromosome()[locus]);
child_2_ch.push(parent_2.chromosome()[locus]);
}
for locus in cut_point..chromosome_len {
child_1_ch.push(parent_2.chromosome()[locus]);
child_2_ch.push(parent_1.chromosome()[locus]);
}
(IndividualT::from(child_1_ch), IndividualT::from(child_2_ch))
}
}
pub struct TwoPoint<R: Rng> {
rng: R,
}
impl TwoPoint<ThreadRng> {
pub fn new() -> Self {
Self::with_rng(rand::thread_rng())
}
}
impl<R: Rng> TwoPoint<R> {
pub fn with_rng(rng: R) -> Self {
TwoPoint { rng }
}
}
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,
{
fn apply(&mut self, parent_1: &IndividualT, parent_2: &IndividualT) -> (IndividualT, IndividualT) {
assert_eq!(
parent_1.chromosome().len(),
parent_2.chromosome().len(),
"Parent chromosome length must match"
);
let chromosome_len = parent_1.chromosome().len();
let cut_points = (
self.rng.gen_range(0..chromosome_len),
self.rng.gen_range(0..chromosome_len),
);
let (cut_point_1, cut_point_2) = if cut_points.0 <= cut_points.1 {
(cut_points.0, cut_points.1)
} else {
(cut_points.1, cut_points.0)
};
let mut child_1_ch = IndividualT::ChromosomeT::default();
let mut child_2_ch = IndividualT::ChromosomeT::default();
for locus in 0..cut_point_1 {
child_1_ch.push(parent_1.chromosome()[locus]);
child_2_ch.push(parent_2.chromosome()[locus]);
}
for locus in cut_point_1..cut_point_2 {
child_1_ch.push(parent_2.chromosome()[locus]);
child_2_ch.push(parent_1.chromosome()[locus]);
}
for locus in cut_point_2..chromosome_len {
child_1_ch.push(parent_1.chromosome()[locus]);
child_2_ch.push(parent_2.chromosome()[locus]);
}
(IndividualT::from(child_1_ch), IndividualT::from(child_2_ch))
}
}
pub struct MultiPoint<R: Rng> {
cut_points_no: usize,
rng: R,
}
impl MultiPoint<ThreadRng> {
pub fn new(cut_points_no: usize) -> Self {
Self::with_rng(cut_points_no, rand::thread_rng())
}
}
impl Default for MultiPoint<ThreadRng> {
fn default() -> Self {
Self::with_rng(4, rand::thread_rng())
}
}
impl<R: Rng> MultiPoint<R> {
pub fn with_rng(cut_points_no: usize, rng: R) -> Self {
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,
{
fn apply(&mut self, parent_1: &IndividualT, parent_2: &IndividualT) -> (IndividualT, IndividualT) {
assert_eq!(
parent_1.chromosome().len(),
parent_2.chromosome().len(),
"Parent chromosome length must match"
);
assert!(
self.cut_points_no <= parent_1.chromosome().len(),
"There can't be more cut points than chromosome length"
);
assert!(self.cut_points_no >= 1, "Numver of cut points must be >= 1");
let chromosome_len = parent_1.chromosome().len();
let mut cut_points =
rand::seq::index::sample(&mut self.rng, chromosome_len, self.cut_points_no).into_vec();
cut_points.sort_unstable();
let mut child_1_ch = IndividualT::ChromosomeT::default();
let mut child_2_ch = IndividualT::ChromosomeT::default();
let (mut curr_parent_1, mut curr_parent_2) = (&parent_1, &parent_2);
for locus in 0..cut_points[0] {
child_1_ch.push(parent_1.chromosome()[locus]);
child_2_ch.push(parent_2.chromosome()[locus]);
(curr_parent_1, curr_parent_2) = (curr_parent_2, curr_parent_1);
}
for cut_point_idx in 0..self.cut_points_no - 1 {
for locus in cut_points[cut_point_idx]..cut_points[cut_point_idx + 1] {
child_1_ch.push(curr_parent_1.chromosome()[locus]);
child_2_ch.push(curr_parent_2.chromosome()[locus]);
}
(curr_parent_1, curr_parent_2) = (curr_parent_2, curr_parent_1);
}
for locus in cut_points[self.cut_points_no - 1]..chromosome_len {
child_1_ch.push(curr_parent_1.chromosome()[locus]);
child_2_ch.push(curr_parent_2.chromosome()[locus]);
}
(IndividualT::from(child_1_ch), IndividualT::from(child_2_ch))
}
}
pub struct Uniform<R: Rng + Clone = ThreadRng> {
rng: R,
}
impl Uniform<ThreadRng> {
pub fn new() -> Self {
Self::with_rng(rand::thread_rng())
}
}
impl<R: Rng + Clone> Uniform<R> {
pub fn with_rng(rng: R) -> Self {
Self { rng }
}
}
impl<GeneT, IndividualT, R> CrossoverOperator<IndividualT> for Uniform<R>
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy,
R: Rng + Clone,
{
fn apply(&mut self, parent_1: &IndividualT, parent_2: &IndividualT) -> (IndividualT, IndividualT) {
assert_eq!(
parent_1.chromosome().len(),
parent_2.chromosome().len(),
"Parent chromosome length must match"
);
let chromosome_len = parent_1.chromosome().len();
let mut child_1_ch = IndividualT::ChromosomeT::default();
let mut child_2_ch = IndividualT::ChromosomeT::default();
let mask = self
.rng
.clone()
.sample_iter(rand::distributions::Uniform::new(0.0, 1.0))
.take(chromosome_len);
for (locus, val) in mask.enumerate() {
if val >= 0.5 {
child_1_ch.push(parent_1.chromosome()[locus]);
child_2_ch.push(parent_2.chromosome()[locus]);
} else {
child_1_ch.push(parent_2.chromosome()[locus]);
child_2_ch.push(parent_1.chromosome()[locus]);
}
}
(IndividualT::from(child_1_ch), IndividualT::from(child_2_ch))
}
}
pub struct UniformParameterized<R: Rng = ThreadRng> {
rng: R,
distr: rand::distributions::Uniform<f64>,
bias: f64,
}
impl UniformParameterized<ThreadRng> {
pub fn new(bias: f64) -> Self {
Self::with_rng(rand::thread_rng(), bias)
}
}
impl<R: Rng> UniformParameterized<R> {
pub fn with_rng(rng: R, bias: f64) -> Self {
Self {
rng,
distr: rand::distributions::Uniform::new(0.0, 1.0),
bias,
}
}
}
impl<GeneT, IndividualT, R> CrossoverOperator<IndividualT> for UniformParameterized<R>
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy,
R: Rng + Clone,
{
fn apply(&mut self, parent_1: &IndividualT, parent_2: &IndividualT) -> (IndividualT, IndividualT) {
assert_eq!(
parent_1.chromosome().len(),
parent_2.chromosome().len(),
"Parent chromosome length must match"
);
let chromosome_len = parent_1.chromosome().len();
let mut child_1_ch = IndividualT::ChromosomeT::default();
let mut child_2_ch = IndividualT::ChromosomeT::default();
let mask = self.rng.clone().sample_iter(self.distr).take(chromosome_len);
for (locus, val) in mask.enumerate() {
if val <= self.bias {
child_1_ch.push(parent_1.chromosome()[locus]);
child_2_ch.push(parent_2.chromosome()[locus]);
} else {
child_1_ch.push(parent_2.chromosome()[locus]);
child_2_ch.push(parent_1.chromosome()[locus]);
}
}
(IndividualT::from(child_1_ch), IndividualT::from(child_2_ch))
}
}
pub struct OrderedCrossover<R: Rng> {
rng: R,
}
impl OrderedCrossover<ThreadRng> {
pub fn new() -> Self {
Self::with_rng(rand::thread_rng())
}
}
impl<R: Rng> OrderedCrossover<R> {
pub fn with_rng(rng: R) -> Self {
Self { rng }
}
fn create_child<GeneT, IndividualT>(
&self,
p1: &IndividualT,
p2: &IndividualT,
begin: usize,
end: usize,
) -> IndividualT
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy + Eq + Hash,
{
let chromosome_len = p1.chromosome().len();
let mut substring_set: HashSet<GeneT> = HashSet::new();
for i in begin..end {
substring_set.push(p1.chromosome()[i]);
}
let mut child_ch = IndividualT::ChromosomeT::default();
let mut index: usize = 0;
while child_ch.len() < begin {
let gene = p2.chromosome()[index];
if !substring_set.contains(&gene) {
child_ch.push(gene);
}
index += 1;
}
for i in begin..end {
child_ch.push(p1.chromosome()[i]);
}
while index < chromosome_len {
let gene = p2.chromosome()[index];
if !substring_set.contains(&gene) {
child_ch.push(gene);
}
index += 1;
}
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,
{
fn apply(&mut self, parent_1: &IndividualT, parent_2: &IndividualT) -> (IndividualT, IndividualT) {
assert_eq!(
parent_1.chromosome().len(),
parent_2.chromosome().len(),
"Parent chromosome length must match"
);
let chromosome_len = parent_1.chromosome().len();
let begin: usize = self.rng.gen_range(0..chromosome_len);
let end: usize = self.rng.gen_range(begin..=chromosome_len);
let child_1 = self.create_child(parent_1, parent_2, begin, end);
let child_2 = self.create_child(parent_2, parent_1, begin, end);
(child_1, child_2)
}
}
pub struct Ppx<R: Rng> {
rng: R,
distribution: rand::distributions::Standard,
}
impl Ppx<ThreadRng> {
pub fn new() -> Self {
Self::with_rng(rand::thread_rng())
}
}
impl<R: Rng> Ppx<R> {
pub fn with_rng(rng: R) -> Self {
Self {
rng,
distribution: rand::distributions::Standard,
}
}
fn create_child<GeneT, IndividualT>(
&self,
p1: &IndividualT,
p2: &IndividualT,
take_from_p1: &[bool],
) -> IndividualT
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy + Eq + Hash,
{
let chromosome_len = p1.chromosome().len();
let mut already_taken: HashSet<GeneT> = HashSet::new();
let mut child_ch = IndividualT::ChromosomeT::default();
let mut index_p: [usize; 2] = [0, 0];
let parents = [p1, p2];
while child_ch.len() < chromosome_len {
let index_child = child_ch.len();
let parent_i = usize::from(!take_from_p1[index_child]);
while child_ch.len() == index_child {
let gene = parents[parent_i].chromosome()[index_p[parent_i]];
index_p[parent_i] += 1;
if !already_taken.contains(&gene) {
already_taken.push(gene);
child_ch.push(gene);
}
}
}
IndividualT::from(child_ch)
}
}
impl<GeneT, IndividualT, R> CrossoverOperator<IndividualT> for Ppx<R>
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy + Eq + Hash,
R: Rng,
{
fn apply(&mut self, parent_1: &IndividualT, parent_2: &IndividualT) -> (IndividualT, IndividualT) {
assert_eq!(
parent_1.chromosome().len(),
parent_2.chromosome().len(),
"Parent chromosome length must match"
);
let chromosome_len = parent_1.chromosome().len();
let take_from_p1: Vec<bool> = (&mut self.rng)
.sample_iter(self.distribution)
.take(chromosome_len)
.collect_vec();
let child_1 = self.create_child(parent_1, parent_2, &take_from_p1);
let child_2 = self.create_child(parent_2, parent_1, &take_from_p1);
(child_1, child_2)
}
}
pub struct Pmx<R: Rng> {
rng: R,
}
impl Pmx<ThreadRng> {
pub fn new() -> Self {
Self::with_rng(rand::thread_rng())
}
}
impl<R: Rng> Pmx<R> {
pub fn with_rng(rng: R) -> Self {
Self { rng }
}
fn to_val_index_map<GeneT, ChT>(&self, chromosome: &ChT) -> HashMap<GeneT, usize>
where
ChT: Chromosome + Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy + Eq + Hash,
{
let chromosome_len = chromosome.len();
let mut val_index_map: HashMap<GeneT, usize> = HashMap::new();
for i in 0..chromosome_len {
val_index_map.push((chromosome[i], i));
}
val_index_map
}
fn create_child<GeneT, IndividualT>(
&self,
p1: &IndividualT,
p2: &IndividualT,
begin: usize,
end: usize,
) -> IndividualT
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy + Eq + Hash,
{
let chromosome_len = p1.chromosome().len();
let mut substring_set: HashSet<GeneT> = HashSet::new();
let mut new_chromosome: Vec<Option<GeneT>> = vec![None; chromosome_len];
let val_to_i_p2 = self.to_val_index_map(p2.chromosome());
#[allow(clippy::needless_range_loop)]
for i in begin..end {
substring_set.push(p1.chromosome()[i]);
new_chromosome[i] = Some(p1.chromosome()[i])
}
for i in begin..end {
let gene = p2.chromosome()[i];
if substring_set.contains(&gene) {
continue;
}
let mut j = i;
loop {
let val = &p1.chromosome()[j];
let gene_place_candidate = val_to_i_p2.get(val).unwrap();
if !(begin..end).contains(gene_place_candidate) {
new_chromosome[*gene_place_candidate] = Some(gene);
break;
}
j = *gene_place_candidate;
}
}
let mut child_ch = IndividualT::ChromosomeT::default();
for (index, gene_opt) in enumerate(new_chromosome) {
match gene_opt {
Some(gene) => child_ch.push(gene),
None => child_ch.push(p2.chromosome()[index]),
};
}
IndividualT::from(child_ch)
}
}
impl<GeneT, IndividualT, R> CrossoverOperator<IndividualT> for Pmx<R>
where
IndividualT: IndividualTrait,
IndividualT::ChromosomeT: Index<usize, Output = GeneT> + Push<GeneT, PushedOut = Nothing>,
GeneT: Copy + Eq + Hash,
R: Rng,
{
fn apply(&mut self, parent_1: &IndividualT, parent_2: &IndividualT) -> (IndividualT, IndividualT) {
assert_eq!(
parent_1.chromosome().len(),
parent_2.chromosome().len(),
"Parent chromosome length must match"
);
let chromosome_len = parent_1.chromosome().len();
let begin: usize = self.rng.gen_range(0..chromosome_len);
let end: usize = self.rng.gen_range(begin..=chromosome_len);
let child_1 = self.create_child(parent_1, parent_2, begin, end);
let child_2 = self.create_child(parent_2, parent_1, begin, end);
(child_1, child_2)
}
}
pub struct Shuffle<R: Rng> {
rng: R,
}
impl Shuffle<ThreadRng> {
pub fn new() -> Self {
Self::with_rng(rand::thread_rng())
}
}
impl<R: Rng> Shuffle<R> {
pub fn with_rng(rng: R) -> Self {
Self { rng }
}
}
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,
{
fn apply(&mut self, parent_1: &IndividualT, parent_2: &IndividualT) -> (IndividualT, IndividualT) {
let chromosome_len = parent_1.chromosome().len();
let cut_point = self.rng.gen_range(0..chromosome_len);
let mut shuffled = Vec::from_iter(0..chromosome_len);
shuffled.shuffle(&mut self.rng);
let mask = shuffled.iter().map(|x| x < &cut_point).collect_vec();
let mut child_1_ch = IndividualT::ChromosomeT::default();
let mut child_2_ch = IndividualT::ChromosomeT::default();
for (i, fist_parent) in enumerate(mask) {
if fist_parent {
child_1_ch.push(parent_1.chromosome()[i]);
child_2_ch.push(parent_2.chromosome()[i]);
} else {
child_1_ch.push(parent_2.chromosome()[i]);
child_2_ch.push(parent_1.chromosome()[i]);
}
}
(IndividualT::from(child_1_ch), IndividualT::from(child_2_ch))
}
}
#[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::Individual;
use std::iter::zip;
#[test]
fn check_ppx_example() {
let op = Ppx::new();
let p1 = Individual::from(vec![1, 2, 3, 4, 5, 6]);
let p2 = Individual::from(vec![3, 1, 2, 6, 4, 5]);
let take_from_p1 = [true, false, true, true, false, false];
let child = op.create_child(&p1, &p2, &take_from_p1);
child
.chromosome()
.iter()
.zip(vec![1, 3, 2, 4, 6, 5].iter())
.for_each(|(x, x_expected)| assert_eq!(x, x_expected))
}
#[test]
fn check_pmx_example() {
let op = Pmx::new();
let p1 = Individual::from(vec![8, 4, 7, 3, 6, 2, 5, 1, 9, 0]);
let p2 = Individual::from(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
let child = op.create_child(&p1, &p2, 3, 8);
for (i, j) in zip(child.chromosome, vec![0, 7, 4, 3, 6, 2, 5, 1, 8, 9]) {
assert_eq!(i, j);
}
}
#[test]
fn shuffle_gives_appropriate_len() {
let mut op = Shuffle::new();
let p1 = Individual::from(vec![8, 4, 7, 3, 6, 2, 5, 1, 9, 0]);
let p2 = Individual::from(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
let (child_1, child_2) = op.apply(&p1, &p2);
assert_eq!(child_1.chromosome.len(), 10);
assert_eq!(child_2.chromosome.len(), 10);
}
#[test]
fn shuffle_fulfills_conditions() {
let mut op = Shuffle::new();
let p1 = Individual::from(vec![1, 0, 0, 1, 0, 1, 0, 1, 0, 0]);
let p2 = Individual::from(vec![0, 1, 1, 0, 1, 0, 1, 0, 1, 1]);
let (c1, c2) = op.apply(&p1, &p2);
for (g1, g2) in c1.chromosome.iter().zip(c2.chromosome.iter()) {
assert_eq!(g1 * g2, 0);
assert_eq!(g1 + g2, 1);
}
}
}