feat(operators): add SwapMutation for permutations
Variation that clones the first parent (a Vec<usize> permutation) and swaps two distinct random indices when len >= 2 (spec §11.4). Tests confirm the multiset of contents is preserved.
This commit is contained in:
@@ -1,7 +1,9 @@
|
|||||||
//! Built-in operators for common decision types.
|
//! Built-in operators for common decision types.
|
||||||
|
|
||||||
pub mod binary;
|
pub mod binary;
|
||||||
|
pub mod permutation;
|
||||||
pub mod real;
|
pub mod real;
|
||||||
|
|
||||||
pub use binary::*;
|
pub use binary::*;
|
||||||
|
pub use permutation::*;
|
||||||
pub use real::*;
|
pub use real::*;
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
//! Operators for permutation (`Vec<usize>`) decisions.
|
||||||
|
|
||||||
|
use rand::Rng as _;
|
||||||
|
|
||||||
|
use crate::core::rng::Rng;
|
||||||
|
use crate::traits::Variation;
|
||||||
|
|
||||||
|
/// Swap two distinct random indices in the first parent (spec §11.4).
|
||||||
|
///
|
||||||
|
/// If the parent has length `< 2` the child is returned unchanged.
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct SwapMutation;
|
||||||
|
|
||||||
|
impl Variation<Vec<usize>> for SwapMutation {
|
||||||
|
fn vary(&mut self, parents: &[Vec<usize>], rng: &mut Rng) -> Vec<Vec<usize>> {
|
||||||
|
assert!(
|
||||||
|
!parents.is_empty(),
|
||||||
|
"SwapMutation requires at least one parent",
|
||||||
|
);
|
||||||
|
let mut child = parents[0].clone();
|
||||||
|
let n = child.len();
|
||||||
|
if n >= 2 {
|
||||||
|
let i = rng.random_range(0..n);
|
||||||
|
let mut j = rng.random_range(0..n);
|
||||||
|
while j == i {
|
||||||
|
j = rng.random_range(0..n);
|
||||||
|
}
|
||||||
|
child.swap(i, j);
|
||||||
|
}
|
||||||
|
vec![child]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::core::rng::rng_from_seed;
|
||||||
|
|
||||||
|
fn sorted(mut v: Vec<usize>) -> Vec<usize> {
|
||||||
|
v.sort();
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn preserves_multiset_contents() {
|
||||||
|
let mut m = SwapMutation;
|
||||||
|
let mut rng = rng_from_seed(11);
|
||||||
|
let parent = vec![0_usize, 1, 2, 3, 4];
|
||||||
|
let children = m.vary(&[parent.clone()], &mut rng);
|
||||||
|
assert_eq!(children.len(), 1);
|
||||||
|
assert_eq!(sorted(children[0].clone()), sorted(parent));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn single_element_unchanged() {
|
||||||
|
let mut m = SwapMutation;
|
||||||
|
let mut rng = rng_from_seed(0);
|
||||||
|
let parent = vec![42_usize];
|
||||||
|
let children = m.vary(&[parent.clone()], &mut rng);
|
||||||
|
assert_eq!(children[0], parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn two_elements_always_swapped() {
|
||||||
|
let mut m = SwapMutation;
|
||||||
|
let mut rng = rng_from_seed(0);
|
||||||
|
let parent = vec![1_usize, 2];
|
||||||
|
let children = m.vary(&[parent.clone()], &mut rng);
|
||||||
|
assert_eq!(children[0], vec![2, 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -16,4 +16,4 @@ pub use crate::pareto::{
|
|||||||
pareto_compare, pareto_front,
|
pareto_compare, pareto_front,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use crate::operators::{BitFlipMutation, GaussianMutation, RealBounds};
|
pub use crate::operators::{BitFlipMutation, GaussianMutation, RealBounds, SwapMutation};
|
||||||
|
|||||||
Reference in New Issue
Block a user