use super::*;
use std::{
collections::{HashMap, HashSet},
hash::Hash,
ops::Range,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CrossoverStrategy {
GeneralizedPartition2,
SequentialConstructive,
EdgeRecombination,
PartiallyMapped,
Order,
Cycle,
SinglePoint,
}
impl CrossoverStrategy {
pub fn crossover<G: Copy + PartialOrd + Eq + Hash, R: Rng>(
&self,
parent1: &[G],
parent2: &[G],
rng: &mut R,
) -> Vec<G> {
match self {
Self::GeneralizedPartition2 => {
generalized_partition_2_crossover(parent1, parent2, |_, partitions| {
rng.gen_range(0..partitions.len())
})
}
Self::SequentialConstructive => {
let pivot = parent1.choose(rng).copied().unwrap();
sequential_constructive_crossover(parent1, parent2, pivot)
}
Self::EdgeRecombination => {
let pivot = parent1.choose(rng).copied().unwrap();
edge_recombination_crossover(parent1, parent2, pivot)
}
Self::PartiallyMapped => {
let range = random_range(parent1.len(), rng);
partially_mapped_crossover(parent1, parent2, range)
}
Self::Order => {
let range = random_range(parent1.len(), rng);
order_crossover(parent1, parent2, range)
}
Self::Cycle => cycle_crossover(parent1, parent2),
Self::SinglePoint => {
let point = rng.gen_range(0..parent1.len());
single_point_crossover(parent1, parent2, point)
}
}
}
}
pub fn generalized_partition_2_crossover<G, F>(
parent1: &[G],
parent2: &[G],
mut partition_selector: F,
) -> Vec<G>
where
G: Copy + PartialOrd + Eq + Hash,
F: FnMut(Option<&G>, &[Vec<G>]) -> usize,
{
let mut offspring = Vec::with_capacity(parent1.len());
let mut genes_in_selected_partition = HashSet::with_capacity(offspring.len());
let mut partitions = common_partitions(parent1, parent2);
while !partitions.is_empty() {
let partition_index = partition_selector(offspring.last(), &partitions);
let mut partition = partitions.swap_remove(partition_index);
genes_in_selected_partition.clear();
genes_in_selected_partition.extend(partition.iter().copied());
offspring.append(&mut partition);
for other_partition in &mut partitions {
other_partition.retain(|gene| !genes_in_selected_partition.contains(gene));
}
partitions.retain(|partition| !partition.is_empty());
}
offspring
}
pub fn sequential_constructive_crossover<G>(parent1: &[G], parent2: &[G], pivot: G) -> Vec<G>
where
G: Copy + Eq + Hash,
{
assert!(parent1.len() == parent2.len());
let n = parent1.len();
if n < 2 {
return parent1.to_vec();
}
let mut visited = HashSet::with_capacity(n);
let mut current = pivot;
let mut offspring = vec![pivot; n];
for next in offspring.iter_mut().skip(1) {
visited.insert(current);
let current_pos1 = find_position(¤t, parent1).unwrap();
let current_pos2 = find_position(¤t, parent2).unwrap();
let next1 = parent1[if current_pos1 + 1 != n {
current_pos1 + 1
} else {
0
}];
let next2 = parent2[if current_pos2 + 1 != n {
current_pos2 + 1
} else {
0
}];
let is_next1_visited = visited.contains(&next1);
let is_next2_visited = visited.contains(&next2);
*next = if is_next1_visited && is_next2_visited {
parent1
.iter()
.find(|gene| !visited.contains(gene))
.copied()
.unwrap()
} else if is_next1_visited {
next2
} else if is_next2_visited {
next1
} else {
let next1_pos2 = find_position(&next1, parent2).unwrap();
let next2_pos1 = find_position(&next2, parent1).unwrap();
if next1_pos2 < next2_pos1 {
next1
} else {
next2
}
};
current = *next;
}
offspring
}
pub fn edge_recombination_crossover<G>(parent1: &[G], parent2: &[G], pivot: G) -> Vec<G>
where
G: Copy + Eq + Hash,
{
assert!(parent1.len() == parent2.len());
let n = parent1.len();
if n < 2 {
return parent1.to_vec();
}
let mut neighbor_lists = HashMap::with_capacity(n);
[parent1, parent2]
.iter()
.flat_map(|genome| {
genome.iter().enumerate().map(|(i, &gene)| {
let prev = if i != 0 { i - 1 } else { n - 1 };
let next = if i + 1 != n { i + 1 } else { 0 };
(gene, [genome[prev], genome[next]])
})
})
.for_each(|(gene, neighbors)| {
neighbor_lists
.entry(gene)
.or_insert_with(HashSet::new)
.extend(neighbors);
});
let mut visited = HashSet::with_capacity(n);
let mut current = pivot;
let mut offspring = vec![pivot; n];
for next in offspring.iter_mut().skip(1) {
visited.insert(current);
for neighbors in neighbor_lists.values_mut() {
neighbors.remove(¤t);
}
*next = neighbor_lists[¤t]
.iter()
.filter(|gene| !visited.contains(gene))
.min_by_key(|&gene| neighbor_lists[gene].len())
.copied()
.unwrap_or_else(|| {
parent1
.iter()
.find(|gene| !visited.contains(gene))
.copied()
.unwrap()
});
current = *next;
}
offspring
}
pub fn partially_mapped_crossover<G>(parent1: &[G], parent2: &[G], range: Range<usize>) -> Vec<G>
where
G: Copy + PartialEq,
{
assert!(parent1.len() == parent2.len());
let n = parent1.len();
if n < 2 {
return parent1.to_vec();
}
let p1 = range.start;
let p2 = range.end;
let mut offspring = vec![parent1[0]; n];
offspring[p1..p2].copy_from_slice(&parent1[p1..p2]);
let start = &parent1[p1..p2];
let end = &parent2[p1..p2];
for (i, gene) in offspring.iter_mut().enumerate() {
if !range.contains(&i) {
let mut mapped_gene = parent2[i];
while let Some(pos) = find_position(&mapped_gene, start) {
mapped_gene = end[pos];
}
*gene = mapped_gene
}
}
offspring
}
pub fn order_crossover<G>(parent1: &[G], parent2: &[G], range: Range<usize>) -> Vec<G>
where
G: Copy + PartialEq,
{
assert!(parent1.len() == parent2.len());
let n = parent1.len();
if n < 2 {
return parent1.to_vec();
}
let p1 = range.start;
let p2 = range.end;
let mut genome = vec![parent1[0]; n];
genome[p1..p2].copy_from_slice(&parent1[p1..p2]);
let mut index1 = if p2 != n { p2 } else { 0 };
let mut index2 = index1;
while index1 != p1 {
if !genome[p1..p2].contains(&parent2[index2]) {
genome[index1] = parent2[index2];
index1 += 1;
if index1 == n {
index1 = 0;
}
}
index2 += 1;
if index2 == n {
index2 = 0;
}
}
genome
}
pub fn cycle_crossover<G>(parent1: &[G], parent2: &[G]) -> Vec<G>
where
G: Copy + PartialEq,
{
assert!(parent1.len() == parent2.len());
let n = parent1.len();
if n < 2 {
return parent1.to_vec();
}
let mut offspring = vec![parent1[0]; n];
let mut visited = vec![false; n];
let mut cycle_start = 0;
let mut first_parent = true;
while cycle_start < n {
let mut index = cycle_start;
while !visited[index] {
visited[index] = true;
offspring[index] = if first_parent {
parent1[index]
} else {
parent2[index]
};
index = if first_parent {
find_position(&parent1[index], parent2).unwrap()
} else {
find_position(&parent2[index], parent1).unwrap()
};
}
first_parent = !first_parent;
if let Some(next_start) = visited.iter().position(|&x| !x) {
cycle_start = next_start;
} else {
break;
}
}
offspring
}
pub fn single_point_crossover<G>(parent1: &[G], parent2: &[G], point: usize) -> Vec<G>
where
G: Copy,
{
assert!(parent1.len() == parent2.len());
let mut offspring = parent1.to_vec();
if point < parent2.len() {
offspring[point..].copy_from_slice(&parent2[point..]);
}
offspring
}
#[inline]
fn find_position<G: PartialEq>(target_gene: &G, genome: &[G]) -> Option<usize> {
genome.iter().position(|gene| gene == target_gene)
}