@@ 517,9 517,182 @@ mod tests {
let offsprings = crossover.crossover(&parents, pairings, &mut MockRng);
let offspring = offsprings.into_iter().next().unwrap();
- eprintln!("{:?}", offspring);
-
+ // NOTE: this sort of relies on the implementation of the algorithm (when there are multiple possibilities
+ // currently the algorithm always chooses last). It's possible this test will break due to valid changes to the algorithm.
assert_eq!(vec![0usize, 1, 3, 4, 5, 2], offspring.permutation.into_iter().copied().collect::<Vec<_>>())
}
+
+ #[test]
+ fn test_reverse_subsequence_perturbation_behavior() {
+ let perturbation = ReverseSubsequencePerturbation::<Const<6>>::new();
+
+ // Test multiple specific seeds to get predictable behavior
+ // We'll try different seeds until we find ones that give us the patterns we want to test
+
+ // Test case 1: Try to find a seed that reverses a middle subsequence
+ let mut found_middle_reverse = false;
+ for seed in 0..1000 {
+ let mut rng = StdRng::seed_from_u64(seed);
+ let mut chromosome = NodePermutation::<Const<6>> {
+ permutation: SVector::<usize, 6>::from_vec(vec![0, 1, 2, 3, 4, 5])
+ };
+ let original = chromosome.clone();
+
+ perturbation.perturb(&mut chromosome, &mut rng);
+
+ // Check if it's a valid reverse pattern and not the whole array or single element
+ let result: Vec<usize> = chromosome.permutation.into_iter().copied().collect();
+ if result != vec![0, 1, 2, 3, 4, 5] && // Changed
+ result != vec![5, 4, 3, 2, 1, 0] && // Not whole array reverse
+ TSPInstance::verify_solution(&chromosome) {
+ found_middle_reverse = true;
+ break;
+ }
+ }
+ assert!(found_middle_reverse, "Should find at least one case of partial subsequence reversal");
+ }
+
+ #[test]
+ fn test_reverse_subsequence_perturbation_deterministic_seed() {
+ let perturbation = ReverseSubsequencePerturbation::<Const<6>>::new();
+
+ // Use a specific seed that we know produces a certain result
+ let mut rng1 = StdRng::seed_from_u64(42);
+ let mut chromosome1 = NodePermutation::<Const<6>> {
+ permutation: SVector::<usize, 6>::from_vec(vec![0, 1, 2, 3, 4, 5])
+ };
+ perturbation.perturb(&mut chromosome1, &mut rng1);
+
+ // Same seed should produce same result
+ let mut rng2 = StdRng::seed_from_u64(42);
+ let mut chromosome2 = NodePermutation::<Const<6>> {
+ permutation: SVector::<usize, 6>::from_vec(vec![0, 1, 2, 3, 4, 5])
+ };
+ perturbation.perturb(&mut chromosome2, &mut rng2);
+
+ assert_eq!(chromosome1.permutation, chromosome2.permutation);
+ assert!(TSPInstance::verify_solution(&chromosome1));
+ assert!(TSPInstance::verify_solution(&chromosome2));
+ }
+
+ #[test]
+ fn test_reverse_subsequence_perturbation_different_initial_permutations() {
+ let perturbation = ReverseSubsequencePerturbation::<Const<5>>::new();
+
+ // Test with a non-sequential initial permutation
+ let mut rng = StdRng::seed_from_u64(123);
+ let mut chromosome = NodePermutation::<Const<5>> {
+ permutation: SVector::<usize, 5>::from_vec(vec![2, 0, 4, 1, 3])
+ };
+ let original_elements: std::collections::HashSet<usize> =
+ chromosome.permutation.iter().copied().collect();
+
+ perturbation.perturb(&mut chromosome, &mut rng);
+
+ // Verify all original elements are still present
+ let new_elements: std::collections::HashSet<usize> =
+ chromosome.permutation.iter().copied().collect();
+ assert_eq!(original_elements, new_elements);
+
+ // Verify it's still a valid permutation
+ assert!(TSPInstance::verify_solution(&chromosome));
+ }
+
+ #[test]
+ fn test_reverse_subsequence_perturbation_edge_cases() {
+ let perturbation = ReverseSubsequencePerturbation::<Const<2>>::new();
+
+ // Test with minimum size permutation (2 elements)
+ let mut rng = StdRng::seed_from_u64(456);
+ let mut chromosome = NodePermutation::<Const<2>> {
+ permutation: SVector::<usize, 2>::from_vec(vec![0, 1])
+ };
+
+ perturbation.perturb(&mut chromosome, &mut rng);
+
+ let result: Vec<usize> = chromosome.permutation.into_iter().copied().collect();
+ // With 2 elements, it should either stay [0,1] or become [1,0]
+ assert!(result == vec![0, 1] || result == vec![1, 0]);
+ assert!(TSPInstance::verify_solution(&chromosome));
+ }
+
+ #[test]
+ fn test_reverse_subsequence_perturbation_is_reversible() {
+ let perturbation = ReverseSubsequencePerturbation::<Const<6>>::new();
+
+ // Any sequence of reversals should be reversible
+ let mut rng = StdRng::seed_from_u64(789);
+ let original = NodePermutation::<Const<6>> {
+ permutation: SVector::<usize, 6>::from_vec(vec![0, 1, 2, 3, 4, 5])
+ };
+ let mut chromosome = original.clone();
+
+ // Apply perturbation twice with same seed (reset RNG)
+ perturbation.perturb(&mut chromosome, &mut rng);
+ let after_first = chromosome.clone();
+
+ // Since we can't easily reverse the exact operation, at least verify
+ // that multiple applications maintain the permutation property
+ for _ in 0..10 {
+ perturbation.perturb(&mut chromosome, &mut rng);
+ assert!(TSPInstance::verify_solution(&chromosome));
+ }
+ }
+
+ #[test]
+ fn test_reverse_subsequence_perturbation_preserves_elements() {
+ let perturbation = ReverseSubsequencePerturbation::<Const<10>>::new();
+ let initializer = TSPRandomInitializer::<Const<10>>::new();
+
+ let mut rng = StdRng::seed_from_u64(42);
+
+ // Test with multiple random permutations
+ for _ in 0..50 {
+ let mut chromosome = initializer.initialize_single(Const::<10>, &mut rng);
+ let original_elements: std::collections::HashSet<usize> = chromosome.permutation.iter().copied().collect();
+
+ perturbation.perturb(&mut chromosome, &mut rng);
+
+ // Verify all elements are still present
+ let new_elements: std::collections::HashSet<usize> = chromosome.permutation.iter().copied().collect();
+ assert_eq!(original_elements, new_elements);
+
+ // Verify it's still a valid permutation
+ assert!(TSPInstance::verify_solution(&chromosome));
+ }
+ }
+
+ #[test]
+ fn test_reverse_subsequence_perturbation_actually_changes_permutation() {
+ let perturbation = ReverseSubsequencePerturbation::<Const<8>>::new();
+ let mut rng = StdRng::seed_from_u64(12345);
+
+ // Test that the perturbation actually changes the permutation (with high probability)
+ let mut changes_detected = 0;
+ let total_tests = 100;
+
+ for _ in 0..total_tests {
+ let mut chromosome = NodePermutation::<Const<8>> {
+ permutation: SVector::<usize, 8>::from_vec(vec![0, 1, 2, 3, 4, 5, 6, 7])
+ };
+ let original = chromosome.clone();
+
+ perturbation.perturb(&mut chromosome, &mut rng);
+
+ if chromosome.permutation != original.permutation {
+ changes_detected += 1;
+ }
+
+ // Always verify it's still a valid permutation
+ assert!(TSPInstance::verify_solution(&chromosome));
+ }
+
+ // We expect at least 85% of random perturbations to actually change the permutation
+ // (only fails if start == end randomly, which should be rare)
+ assert!(changes_detected >= 85,
+ "Expected at least 85 changes out of {} tests, but got {}",
+ total_tests, changes_detected);
+ }
+
}