use ndarray::ArrayViewMut1;
use crate::{operators::MutationOperator, random::RandomGenerator};
#[derive(Debug, Clone)]
pub struct SwapMutation;
impl SwapMutation {
pub fn new() -> Self {
Self
}
}
impl MutationOperator for SwapMutation {
fn mutate<'a>(&self, mut individual: ArrayViewMut1<'a, f64>, rng: &mut impl RandomGenerator) {
let length = individual.len();
if length > 1 {
let idx1 = rng.gen_range_usize(0, length);
let mut idx2 = rng.gen_range_usize(0, length);
while idx2 == idx1 {
idx2 = rng.gen_range_usize(0, length);
}
individual.swap(idx1, idx2);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::random::{RandomGenerator, TestDummyRng};
use ndarray::array;
struct ControlledFakeRandomGenerator {
responses: Vec<usize>,
index: usize,
dummy: TestDummyRng,
}
impl ControlledFakeRandomGenerator {
fn new(responses: Vec<usize>) -> Self {
Self {
responses,
index: 0,
dummy: TestDummyRng,
}
}
}
impl RandomGenerator for ControlledFakeRandomGenerator {
type R = TestDummyRng;
fn rng(&mut self) -> &mut TestDummyRng {
&mut self.dummy
}
fn gen_range_usize(&mut self, _min: usize, _max: usize) -> usize {
let resp = self.responses[self.index];
self.index += 1;
resp
}
}
#[test]
fn test_swap_mutation_controlled() {
let mut original = array![[0.0, 1.0, 2.0, 3.0, 4.0]];
let original_row = original.row(0).to_owned();
let mutation_operator = SwapMutation::new();
let mut rng = ControlledFakeRandomGenerator::new(vec![1, 3]);
mutation_operator.operate(&mut original, 1.0, &mut rng);
let mutated_individual = original.row(0).to_owned();
let mut diff_positions = Vec::new();
for i in 0..original_row.len() {
if original_row[i] != mutated_individual[i] {
diff_positions.push(i);
}
}
assert_eq!(
diff_positions.len(),
2,
"Expected exactly 2 positions to change after swap"
);
let i = diff_positions[0];
let j = diff_positions[1];
assert_eq!(
original_row[i], mutated_individual[j],
"Element at position {} should have been swapped with element at position {}",
i, j
);
assert_eq!(
original_row[j], mutated_individual[i],
"Element at position {} should have been swapped with element at position {}",
j, i
);
}
}