@@ 1,10 1,12 @@
use std::{convert::Infallible, marker::PhantomData};
-use eoa_lib::{fitness::FitnessFunction, initializer::Initializer, perturbation::PerturbationOperator};
+use eoa_lib::{crossover::Crossover, fitness::FitnessFunction, initializer::Initializer, perturbation::PerturbationOperator, replacement::Population};
use itertools::Itertools;
use nalgebra::{allocator::Allocator, distance, Const, DefaultAllocator, Dim, Dyn, OMatrix, OVector, Point, U1};
use plotters::prelude::*;
-use rand::{seq::SliceRandom, Rng, RngCore};
+use rand::{seq::{IteratorRandom, SliceRandom}, Rng, RngCore};
+
+use crate::graph::Edge;
#[derive(PartialEq, Clone, Debug)]
pub struct TSPCity {
@@ 91,7 93,7 @@ where
self.distances.shape_generic().0
}
- pub fn verify_solution(&self, solution: &NodePermutation<D>) -> bool {
+ pub fn verify_solution(solution: &NodePermutation<D>) -> bool {
let mut seen_vertices = OVector::from_element_generic(
solution.permutation.shape_generic().0,
solution.permutation.shape_generic().1,
@@ 100,7 102,7 @@ where
for &vertex in solution.permutation.iter() {
// This vertex index is out of bounds
- if vertex >= self.cities.len() {
+ if vertex >= solution.permutation.len() {
return false;
}
@@ 119,10 121,14 @@ where
solution.permutation
.iter()
.circular_tuple_windows()
- .map(|(&node1, &node2): (&usize, &usize)| self.distances[(node1, node2)])
+ .map(|(&node1, &node2): (&usize, &usize)| self.distances(node1, node2))
.sum()
}
+ pub fn distances(&self, city_a: usize, city_b: usize) -> f64 {
+ self.distances[(city_a, city_b)]
+ }
+
fn plot_internal(&self, solution: Option<&NodePermutation<D>>, filename: &str) -> Result<(), Box<dyn std::error::Error>> {
let root = BitMapBackend::new(filename, (800, 600)).into_drawing_area();
root.fill(&WHITE)?;
@@ 263,3 269,255 @@ where
chromosome.permutation.swap_rows(first, second);
}
}
+
+pub struct ReverseSubsequencePerturbation<D> {
+ _phantom: PhantomData<D>
+}
+
+impl<D> ReverseSubsequencePerturbation<D> {
+ pub fn new() -> Self {
+ Self { _phantom: PhantomData }
+ }
+}
+
+impl<D> PerturbationOperator for ReverseSubsequencePerturbation<D>
+where
+ D: Dim,
+ DefaultAllocator: Allocator<D, D>,
+ DefaultAllocator: Allocator<D>,
+{
+ type Chromosome = NodePermutation<D>;
+
+ fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore) {
+ let first = rng.random_range(0..chromosome.permutation.len());
+ let second = rng.random_range(0..chromosome.permutation.len());
+
+ let start = first.min(second);
+ let end = first.max(second);
+
+ // Only to half of the interval to swap
+ let end = (end + start) / 2;
+
+ for (i, j) in (start..=end).zip(end..=start) {
+ chromosome.permutation.swap_rows(i, j);
+ }
+
+ }
+}
+
+pub struct EdgeRecombinationCrossover<D> {
+ _phantom: PhantomData<D>
+}
+
+impl<D> EdgeRecombinationCrossover<D> {
+ pub fn new() -> Self {
+ Self { _phantom: PhantomData }
+ }
+}
+
+impl<D> Crossover<2> for EdgeRecombinationCrossover<D>
+where
+ D: Dim,
+ DefaultAllocator: Allocator<D, D>,
+ DefaultAllocator: Allocator<D>,
+ DefaultAllocator: nalgebra::allocator::Allocator<D, Const<4>>
+{
+ type Chromosome = NodePermutation<D>;
+ type Out = f64;
+
+ fn crossover(
+ &self,
+ parents: &eoa_lib::replacement::EvaluatedPopulation<Self::Chromosome, Self::Out>,
+ pairs: impl Iterator<Item = eoa_lib::pairing::ParentPairing<2>>,
+ rng: &mut dyn RngCore
+ ) -> eoa_lib::replacement::Population<Self::Chromosome> {
+ let mut offsprings = vec![];
+
+ let permutation = &parents.population[0].chromosome.permutation;
+ let len = permutation.len();
+ let mut adjacency_lists = OMatrix::from_element_generic(
+ permutation.shape_generic().0,
+ Const::<4>,
+ None);
+ let mut used_nodes = OVector::from_element_generic(
+ permutation.shape_generic().0,
+ Const::<1>,
+ false
+ );
+
+ let mut neighbors_count = OVector::from_element_generic(
+ permutation.shape_generic().0,
+ Const::<1>,
+ 2usize
+ );
+
+ for pair in pairs {
+ let parent1 = &parents.population[pair.x].chromosome;
+ let parent2 = &parents.population[pair.y].chromosome;
+
+ used_nodes.apply(|n| *n = false);
+
+ // 1. Populate adjacency lists
+ for (&c1, &n, &c2) in parent1.permutation.iter().circular_tuple_windows() {
+ adjacency_lists[(n, 0)] = Some(c1);
+ adjacency_lists[(n, 1)] = Some(c2);
+ neighbors_count[n] = 2;
+ }
+
+ for (&c1, &n, &c2) in parent2.permutation.iter().circular_tuple_windows() {
+ // Not duplicit?
+ if adjacency_lists[(n, 0)].unwrap() != c1 && adjacency_lists[(n, 1)].unwrap() != c1 {
+ neighbors_count[n] += 1;
+ adjacency_lists[(n, 2)] = Some(c1);
+ } else { // Duplicit
+ adjacency_lists[(n, 2)] = None;
+ }
+
+ // Not duplicit
+ if adjacency_lists[(n, 0)].unwrap() != c2 && adjacency_lists[(n, 1)].unwrap() != c2 {
+ neighbors_count[n] += 1;
+ adjacency_lists[(n, 3)] = Some(c2);
+ } else { // Duplicit
+ adjacency_lists[(n, 3)] = None;
+ }
+ }
+
+ let chosen_parent = if rng.random_bool(0.5) {
+ &parent1
+ } else {
+ &parent2
+ };
+
+ let mut offspring = OVector::from_element_generic(permutation.shape_generic().0, Const::<1>, 0);
+
+ let mut current_node = chosen_parent.permutation[0];
+
+ for i in 0..len-1 {
+ offspring[i] = current_node;
+ used_nodes[current_node] = true;
+
+ for neighbor in adjacency_lists.row(current_node) {
+ if let Some(neighbor) = neighbor {
+ neighbors_count[*neighbor] -= 1;
+ }
+ }
+
+ let min_neighbors = adjacency_lists.row(current_node)
+ .iter()
+ .flatten()
+ .filter(|&&neighbor| !used_nodes[neighbor])
+ .map(|&neighbor| neighbors_count[neighbor])
+ .min();
+
+ let neighbor = if let Some(min_neighbors) = min_neighbors {
+ adjacency_lists.row(current_node)
+ .iter()
+ .flatten()
+ .copied()
+ .filter(|&neighbor| !used_nodes[neighbor] && neighbors_count[neighbor] == min_neighbors)
+ .choose(rng)
+ } else {
+ None
+ };
+
+ current_node = if let Some(neighbor) = neighbor {
+ neighbor
+ } else {
+ (0..len).filter(|&node| !used_nodes[node])
+ .choose(rng)
+ .unwrap()
+ };
+ }
+
+ offspring[len - 1] = current_node;
+
+ offsprings.push(NodePermutation { permutation: offspring });
+ }
+
+ Population::from_vec(offsprings)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::convert::Infallible;
+
+ use eoa_lib::{crossover::Crossover, fitness::FitnessFunction, initializer::Initializer, pairing::{AdjacentPairing, Pairing}, replacement::Population};
+ use nalgebra::{Const, SVector, U2, U6};
+ use rand::{rngs::StdRng, RngCore, SeedableRng};
+
+ use crate::tsp::TSPInstance;
+
+ use super::{EdgeRecombinationCrossover, NodePermutation, TSPRandomInitializer};
+
+ struct MockRng;
+ impl RngCore for MockRng {
+ fn next_u32(&mut self) -> u32 {
+ 0
+ }
+
+ fn next_u64(&mut self) -> u64 {
+ 0
+ }
+
+ fn fill_bytes(&mut self, _: &mut [u8]) {
+ panic!()
+ }
+ }
+
+ struct ZeroFitness<const LEN: usize>;
+ impl<const LEN: usize> FitnessFunction for ZeroFitness<LEN> {
+ type In = NodePermutation<Const<LEN>>;
+ type Out = f64;
+ type Err = Infallible;
+
+ fn fit(self: &Self, _: &Self::In) -> Result<Self::Out, Self::Err> {
+ Ok(0.0)
+ }
+ }
+
+ #[test]
+ fn test_edge_recombination_properties() {
+ let crossover = EdgeRecombinationCrossover::<Const<10>>::new();
+ let initializer = TSPRandomInitializer::<Const<10>>::new();
+ let adjacency_pairing = AdjacentPairing::new();
+
+ let mut rng = StdRng::seed_from_u64(0);
+ for _ in 0..100 {
+ let parents = Population::from_vec(initializer.initialize(Const::<10>, 10, &mut rng));
+ let parents = parents.evaluate(&ZeroFitness).unwrap();
+
+ let pairs = adjacency_pairing.pair(&parents, 0..10);
+ let result = crossover.crossover(&parents, pairs, &mut rng);
+
+ // Test invariants that should always hold:
+ for chromosome in result.into_iter() {
+ assert!(TSPInstance::verify_solution(&chromosome));
+ }
+ }
+ }
+
+ #[test]
+ fn test_edge_recombination_specific_case() {
+ let parent1: Vec<usize> = vec![0, 1, 2, 4, 5, 3];
+ let parent2: Vec<usize> = vec![2, 0, 1, 3, 4, 5];
+
+ let parent1 = NodePermutation::<U6> { permutation: SVector::<usize, 6>::from_vec(parent1) };
+ let parent2 = NodePermutation::<U6> { permutation: SVector::<usize, 6>::from_vec(parent2) };
+
+ let pairing = SVector::<usize, 2>::new(0, 1);
+ let pairings = vec![pairing].into_iter();
+
+ let parents = Population::from_vec(vec![parent1, parent2]).evaluate(&ZeroFitness).unwrap();
+
+ let crossover = EdgeRecombinationCrossover::<U6>::new();
+
+ let offsprings = crossover.crossover(&parents, pairings, &mut MockRng);
+ let offspring = offsprings.into_iter().next().unwrap();
+
+ eprintln!("{:?}", offspring);
+
+ assert_eq!(vec![0usize, 1, 3, 4, 5, 2], offspring.permutation.into_iter().copied().collect::<Vec<_>>())
+ }
+
+}