1use rand::Rng;
6use rand_distr::{Distribution, Normal};
7
8use crate::genome::bit_string::BitString;
9use crate::genome::bounds::MultiBounds;
10use crate::genome::permutation::Permutation;
11use crate::genome::real_vector::RealVector;
12use crate::genome::traits::{
13 BinaryGenome, EvolutionaryGenome, PermutationGenome, RealValuedGenome,
14};
15use crate::genome::tree::{Function, Terminal, TreeGenome, TreeNode};
16use crate::operators::traits::{BoundedMutationOperator, MutationOperator};
17
18#[derive(Clone, Debug)]
25pub struct PolynomialMutation {
26 pub eta_m: f64,
29 pub mutation_probability: Option<f64>,
31}
32
33impl PolynomialMutation {
34 pub fn new(eta_m: f64) -> Self {
36 assert!(eta_m >= 0.0, "Distribution index must be non-negative");
37 Self {
38 eta_m,
39 mutation_probability: None,
40 }
41 }
42
43 pub fn with_probability(mut self, probability: f64) -> Self {
45 assert!(
46 (0.0..=1.0).contains(&probability),
47 "Probability must be in [0, 1]"
48 );
49 self.mutation_probability = Some(probability);
50 self
51 }
52
53 fn mutate_gene<R: Rng>(&self, gene: f64, min: f64, max: f64, rng: &mut R) -> f64 {
55 let range = max - min;
56 if range <= 0.0 {
57 return gene;
58 }
59
60 let delta1 = (gene - min) / range;
61 let delta2 = (max - gene) / range;
62
63 let u = rng.gen::<f64>();
64 let delta_q = if u <= 0.5 {
65 let val = 2.0 * u + (1.0 - 2.0 * u) * (1.0 - delta1).powf(self.eta_m + 1.0);
66 val.powf(1.0 / (self.eta_m + 1.0)) - 1.0
67 } else {
68 let val = 2.0 * (1.0 - u) + 2.0 * (u - 0.5) * (1.0 - delta2).powf(self.eta_m + 1.0);
69 1.0 - val.powf(1.0 / (self.eta_m + 1.0))
70 };
71
72 (gene + delta_q * range).clamp(min, max)
73 }
74}
75
76impl MutationOperator<RealVector> for PolynomialMutation {
77 fn mutate<R: Rng>(&self, genome: &mut RealVector, rng: &mut R) {
78 let default_bounds = MultiBounds::symmetric(1e10, genome.dimension());
80 self.mutate_bounded(genome, &default_bounds, rng);
81 }
82
83 fn mutation_probability(&self) -> f64 {
84 self.mutation_probability.unwrap_or(1.0)
85 }
86}
87
88impl BoundedMutationOperator<RealVector> for PolynomialMutation {
89 fn mutate_bounded<R: Rng>(&self, genome: &mut RealVector, bounds: &MultiBounds, rng: &mut R) {
90 let n = genome.dimension();
91 let prob = self.mutation_probability.unwrap_or(1.0 / n as f64);
92
93 for i in 0..n {
94 if rng.gen::<f64>() < prob {
95 if let Some(bound) = bounds.get(i) {
96 genome.genes_mut()[i] =
97 self.mutate_gene(genome.genes()[i], bound.min, bound.max, rng);
98 }
99 }
100 }
101 }
102}
103
104#[derive(Clone, Debug)]
108pub struct GaussianMutation {
109 pub sigma: f64,
111 pub mutation_probability: Option<f64>,
113}
114
115impl GaussianMutation {
116 pub fn new(sigma: f64) -> Self {
118 assert!(sigma >= 0.0, "Sigma must be non-negative");
119 Self {
120 sigma,
121 mutation_probability: None,
122 }
123 }
124
125 pub fn with_probability(mut self, probability: f64) -> Self {
127 assert!(
128 (0.0..=1.0).contains(&probability),
129 "Probability must be in [0, 1]"
130 );
131 self.mutation_probability = Some(probability);
132 self
133 }
134}
135
136impl MutationOperator<RealVector> for GaussianMutation {
137 fn mutate<R: Rng>(&self, genome: &mut RealVector, rng: &mut R) {
138 let n = genome.dimension();
139 let prob = self.mutation_probability.unwrap_or(1.0 / n as f64);
140 let normal = Normal::new(0.0, self.sigma).unwrap();
141
142 for gene in genome.genes_mut() {
143 if rng.gen::<f64>() < prob {
144 *gene += normal.sample(rng);
145 }
146 }
147 }
148
149 fn mutation_probability(&self) -> f64 {
150 self.mutation_probability.unwrap_or(1.0)
151 }
152}
153
154impl BoundedMutationOperator<RealVector> for GaussianMutation {
155 fn mutate_bounded<R: Rng>(&self, genome: &mut RealVector, bounds: &MultiBounds, rng: &mut R) {
156 let n = genome.dimension();
157 let prob = self.mutation_probability.unwrap_or(1.0 / n as f64);
158 let normal = Normal::new(0.0, self.sigma).unwrap();
159
160 for i in 0..n {
161 if rng.gen::<f64>() < prob {
162 genome.genes_mut()[i] += normal.sample(rng);
163 if let Some(bound) = bounds.get(i) {
164 genome.genes_mut()[i] = bound.clamp(genome.genes()[i]);
165 }
166 }
167 }
168 }
169}
170
171#[derive(Clone, Debug)]
175pub struct UniformMutation {
176 pub mutation_probability: Option<f64>,
178}
179
180impl UniformMutation {
181 pub fn new() -> Self {
183 Self {
184 mutation_probability: None,
185 }
186 }
187
188 pub fn with_probability(mut self, probability: f64) -> Self {
190 assert!(
191 (0.0..=1.0).contains(&probability),
192 "Probability must be in [0, 1]"
193 );
194 self.mutation_probability = Some(probability);
195 self
196 }
197}
198
199impl Default for UniformMutation {
200 fn default() -> Self {
201 Self::new()
202 }
203}
204
205impl MutationOperator<RealVector> for UniformMutation {
206 fn mutate<R: Rng>(&self, genome: &mut RealVector, rng: &mut R) {
207 let default_bounds = MultiBounds::symmetric(1.0, genome.dimension());
208 self.mutate_bounded(genome, &default_bounds, rng);
209 }
210}
211
212impl BoundedMutationOperator<RealVector> for UniformMutation {
213 fn mutate_bounded<R: Rng>(&self, genome: &mut RealVector, bounds: &MultiBounds, rng: &mut R) {
214 let n = genome.dimension();
215 let prob = self.mutation_probability.unwrap_or(1.0 / n as f64);
216
217 for i in 0..n {
218 if rng.gen::<f64>() < prob {
219 if let Some(bound) = bounds.get(i) {
220 genome.genes_mut()[i] = rng.gen_range(bound.min..=bound.max);
221 }
222 }
223 }
224 }
225}
226
227#[derive(Clone, Debug)]
231pub struct BitFlipMutation {
232 pub mutation_probability: Option<f64>,
234}
235
236impl BitFlipMutation {
237 pub fn new() -> Self {
239 Self {
240 mutation_probability: None,
241 }
242 }
243
244 pub fn with_probability(mut self, probability: f64) -> Self {
246 assert!(
247 (0.0..=1.0).contains(&probability),
248 "Probability must be in [0, 1]"
249 );
250 self.mutation_probability = Some(probability);
251 self
252 }
253}
254
255impl Default for BitFlipMutation {
256 fn default() -> Self {
257 Self::new()
258 }
259}
260
261impl MutationOperator<BitString> for BitFlipMutation {
262 fn mutate<R: Rng>(&self, genome: &mut BitString, rng: &mut R) {
263 let n = genome.len();
264 let prob = self.mutation_probability.unwrap_or(1.0 / n as f64);
265
266 for i in 0..n {
267 if rng.gen::<f64>() < prob {
268 genome.flip(i);
269 }
270 }
271 }
272
273 fn mutation_probability(&self) -> f64 {
274 self.mutation_probability.unwrap_or(1.0)
275 }
276}
277
278#[derive(Clone, Debug, Default)]
282pub struct SwapMutation {
283 pub num_swaps: usize,
285}
286
287impl SwapMutation {
288 pub fn new() -> Self {
290 Self { num_swaps: 1 }
291 }
292
293 pub fn with_swaps(num_swaps: usize) -> Self {
295 Self { num_swaps }
296 }
297}
298
299impl MutationOperator<BitString> for SwapMutation {
300 fn mutate<R: Rng>(&self, genome: &mut BitString, rng: &mut R) {
301 let n = genome.len();
302 if n < 2 {
303 return;
304 }
305
306 for _ in 0..self.num_swaps {
307 let i = rng.gen_range(0..n);
308 let j = rng.gen_range(0..n);
309 if i != j {
310 let temp = genome.bits()[i];
311 genome.bits_mut()[i] = genome.bits()[j];
312 genome.bits_mut()[j] = temp;
313 }
314 }
315 }
316}
317
318impl MutationOperator<RealVector> for SwapMutation {
319 fn mutate<R: Rng>(&self, genome: &mut RealVector, rng: &mut R) {
320 let n = genome.dimension();
321 if n < 2 {
322 return;
323 }
324
325 for _ in 0..self.num_swaps {
326 let i = rng.gen_range(0..n);
327 let j = rng.gen_range(0..n);
328 if i != j {
329 genome.genes_mut().swap(i, j);
330 }
331 }
332 }
333}
334
335#[derive(Clone, Debug, Default)]
339pub struct ScrambleMutation;
340
341impl ScrambleMutation {
342 pub fn new() -> Self {
344 Self
345 }
346}
347
348impl MutationOperator<BitString> for ScrambleMutation {
349 fn mutate<R: Rng>(&self, genome: &mut BitString, rng: &mut R) {
350 use rand::seq::SliceRandom;
351
352 let n = genome.len();
353 if n < 2 {
354 return;
355 }
356
357 let mut start = rng.gen_range(0..n);
358 let mut end = rng.gen_range(0..n);
359 if start > end {
360 std::mem::swap(&mut start, &mut end);
361 }
362
363 let segment: Vec<bool> = (start..=end).map(|i| genome.bits()[i]).collect();
365 let mut shuffled = segment;
366 shuffled.shuffle(rng);
367
368 for (i, val) in shuffled.into_iter().enumerate() {
369 genome.bits_mut()[start + i] = val;
370 }
371 }
372}
373
374impl MutationOperator<RealVector> for ScrambleMutation {
375 fn mutate<R: Rng>(&self, genome: &mut RealVector, rng: &mut R) {
376 use rand::seq::SliceRandom;
377
378 let n = genome.dimension();
379 if n < 2 {
380 return;
381 }
382
383 let mut start = rng.gen_range(0..n);
384 let mut end = rng.gen_range(0..n);
385 if start > end {
386 std::mem::swap(&mut start, &mut end);
387 }
388
389 let slice = &mut genome.genes_mut()[start..=end];
391 slice.shuffle(rng);
392 }
393}
394
395#[derive(Clone, Debug, Default)]
404pub struct PermutationSwapMutation {
405 pub num_swaps: usize,
407}
408
409impl PermutationSwapMutation {
410 pub fn new() -> Self {
412 Self { num_swaps: 1 }
413 }
414
415 pub fn with_swaps(num_swaps: usize) -> Self {
417 Self { num_swaps }
418 }
419}
420
421impl MutationOperator<Permutation> for PermutationSwapMutation {
422 fn mutate<R: Rng>(&self, genome: &mut Permutation, rng: &mut R) {
423 let n = genome.dimension();
424 if n < 2 {
425 return;
426 }
427
428 for _ in 0..self.num_swaps {
429 let i = rng.gen_range(0..n);
430 let j = rng.gen_range(0..n);
431 if i != j {
432 genome.swap(i, j);
433 }
434 }
435 }
436}
437
438#[derive(Clone, Debug, Default)]
443pub struct InsertMutation {
444 pub num_inserts: usize,
446}
447
448impl InsertMutation {
449 pub fn new() -> Self {
451 Self { num_inserts: 1 }
452 }
453
454 pub fn with_inserts(num_inserts: usize) -> Self {
456 Self { num_inserts }
457 }
458}
459
460impl MutationOperator<Permutation> for InsertMutation {
461 fn mutate<R: Rng>(&self, genome: &mut Permutation, rng: &mut R) {
462 let n = genome.dimension();
463 if n < 2 {
464 return;
465 }
466
467 for _ in 0..self.num_inserts {
468 let from = rng.gen_range(0..n);
469 let to = rng.gen_range(0..n);
470 if from != to {
471 genome.insert(from, to);
472 }
473 }
474 }
475}
476
477#[derive(Clone, Debug, Default)]
483pub struct InversionMutation;
484
485impl InversionMutation {
486 pub fn new() -> Self {
488 Self
489 }
490}
491
492impl MutationOperator<Permutation> for InversionMutation {
493 fn mutate<R: Rng>(&self, genome: &mut Permutation, rng: &mut R) {
494 let n = genome.dimension();
495 if n < 2 {
496 return;
497 }
498
499 let mut start = rng.gen_range(0..n);
500 let mut end = rng.gen_range(0..n);
501 if start > end {
502 std::mem::swap(&mut start, &mut end);
503 }
504
505 genome.reverse_segment(start, end);
506 }
507}
508
509#[derive(Clone, Debug, Default)]
514pub struct PermutationScrambleMutation;
515
516impl PermutationScrambleMutation {
517 pub fn new() -> Self {
519 Self
520 }
521}
522
523impl MutationOperator<Permutation> for PermutationScrambleMutation {
524 fn mutate<R: Rng>(&self, genome: &mut Permutation, rng: &mut R) {
525 use rand::seq::SliceRandom;
526
527 let n = genome.dimension();
528 if n < 2 {
529 return;
530 }
531
532 let mut start = rng.gen_range(0..n);
533 let mut end = rng.gen_range(0..n);
534 if start > end {
535 std::mem::swap(&mut start, &mut end);
536 }
537
538 let perm = genome.permutation_mut();
540 perm[start..=end].shuffle(rng);
541 }
542}
543
544#[derive(Clone, Debug, Default)]
549pub struct DisplacementMutation;
550
551impl DisplacementMutation {
552 pub fn new() -> Self {
554 Self
555 }
556}
557
558impl MutationOperator<Permutation> for DisplacementMutation {
559 fn mutate<R: Rng>(&self, genome: &mut Permutation, rng: &mut R) {
560 let n = genome.dimension();
561 if n < 3 {
562 return;
563 }
564
565 let mut start = rng.gen_range(0..n);
567 let mut end = rng.gen_range(0..n);
568 if start > end {
569 std::mem::swap(&mut start, &mut end);
570 }
571
572 let segment_len = end - start + 1;
573 if segment_len >= n {
574 return; }
576
577 let perm = genome.permutation_mut();
579 let segment: Vec<usize> = perm[start..=end].to_vec();
580
581 let remaining: Vec<usize> = perm[..start]
583 .iter()
584 .chain(perm[end + 1..].iter())
585 .copied()
586 .collect();
587
588 let insert_pos = rng.gen_range(0..=remaining.len());
590
591 let new_perm: Vec<usize> = remaining[..insert_pos]
593 .iter()
594 .chain(segment.iter())
595 .chain(remaining[insert_pos..].iter())
596 .copied()
597 .collect();
598
599 perm.copy_from_slice(&new_perm);
600 }
601}
602
603#[derive(Clone, Debug)]
607pub struct AdaptivePermutationMutation {
608 pub swap_prob: f64,
610 pub insert_prob: f64,
612 pub inversion_prob: f64,
614 pub scramble_prob: f64,
616}
617
618impl AdaptivePermutationMutation {
619 pub fn new() -> Self {
621 Self {
622 swap_prob: 0.25,
623 insert_prob: 0.25,
624 inversion_prob: 0.25,
625 scramble_prob: 0.25,
626 }
627 }
628
629 pub fn with_probs(swap: f64, insert: f64, inversion: f64, scramble: f64) -> Self {
631 Self {
632 swap_prob: swap,
633 insert_prob: insert,
634 inversion_prob: inversion,
635 scramble_prob: scramble,
636 }
637 }
638}
639
640impl Default for AdaptivePermutationMutation {
641 fn default() -> Self {
642 Self::new()
643 }
644}
645
646impl MutationOperator<Permutation> for AdaptivePermutationMutation {
647 fn mutate<R: Rng>(&self, genome: &mut Permutation, rng: &mut R) {
648 let total = self.swap_prob + self.insert_prob + self.inversion_prob + self.scramble_prob;
649 if total <= 0.0 {
650 return;
651 }
652
653 let r = rng.gen::<f64>() * total;
654 let mut cumulative = 0.0;
655
656 cumulative += self.swap_prob;
657 if r < cumulative {
658 PermutationSwapMutation::new().mutate(genome, rng);
659 return;
660 }
661
662 cumulative += self.insert_prob;
663 if r < cumulative {
664 InsertMutation::new().mutate(genome, rng);
665 return;
666 }
667
668 cumulative += self.inversion_prob;
669 if r < cumulative {
670 InversionMutation::new().mutate(genome, rng);
671 return;
672 }
673
674 PermutationScrambleMutation::new().mutate(genome, rng);
675 }
676}
677
678#[derive(Clone, Debug)]
691pub struct PointMutation {
692 pub mutation_probability: f64,
694 pub function_probability: f64,
696}
697
698impl PointMutation {
699 pub fn new() -> Self {
703 Self {
704 mutation_probability: 0.1,
705 function_probability: 0.9,
706 }
707 }
708
709 pub fn with_probability(mut self, probability: f64) -> Self {
711 assert!(
712 (0.0..=1.0).contains(&probability),
713 "Probability must be in [0, 1]"
714 );
715 self.mutation_probability = probability;
716 self
717 }
718
719 pub fn with_function_probability(mut self, probability: f64) -> Self {
721 assert!(
722 (0.0..=1.0).contains(&probability),
723 "Probability must be in [0, 1]"
724 );
725 self.function_probability = probability;
726 self
727 }
728
729 fn mutate_node<T: Terminal, F: Function, R: Rng>(
731 &self,
732 node: &TreeNode<T, F>,
733 rng: &mut R,
734 ) -> TreeNode<T, F> {
735 match node {
736 TreeNode::Terminal(_) => {
737 TreeNode::Terminal(T::random(rng))
739 }
740 TreeNode::Function(func, children) => {
741 let target_arity = func.arity();
743 let funcs = F::functions();
744
745 let matching_funcs: Vec<&F> =
747 funcs.iter().filter(|f| f.arity() == target_arity).collect();
748
749 if matching_funcs.is_empty() {
750 TreeNode::Function(func.clone(), children.clone())
752 } else {
753 let new_func = matching_funcs[rng.gen_range(0..matching_funcs.len())].clone();
755 TreeNode::Function(new_func, children.clone())
756 }
757 }
758 }
759 }
760
761 fn mutate_recursive<T: Terminal, F: Function, R: Rng>(
763 &self,
764 node: &TreeNode<T, F>,
765 rng: &mut R,
766 ) -> TreeNode<T, F> {
767 let mutated_node = if rng.gen::<f64>() < self.mutation_probability {
769 self.mutate_node(node, rng)
770 } else {
771 node.clone()
772 };
773
774 match mutated_node {
776 TreeNode::Terminal(t) => TreeNode::Terminal(t),
777 TreeNode::Function(func, children) => {
778 let new_children: Vec<TreeNode<T, F>> = children
779 .iter()
780 .map(|c| self.mutate_recursive(c, rng))
781 .collect();
782 TreeNode::Function(func, new_children)
783 }
784 }
785 }
786}
787
788impl Default for PointMutation {
789 fn default() -> Self {
790 Self::new()
791 }
792}
793
794impl<T: Terminal, F: Function> MutationOperator<TreeGenome<T, F>> for PointMutation {
795 fn mutate<R: Rng>(&self, genome: &mut TreeGenome<T, F>, rng: &mut R) {
796 let new_root = self.mutate_recursive(&genome.root, rng);
797 genome.root = new_root;
798 }
799
800 fn mutation_probability(&self) -> f64 {
801 self.mutation_probability
802 }
803}
804
805#[derive(Clone, Debug)]
810pub struct SubtreeMutation {
811 pub max_subtree_depth: usize,
813 pub function_probability: f64,
815 pub terminal_probability: f64,
817}
818
819impl SubtreeMutation {
820 pub fn new() -> Self {
822 Self {
823 max_subtree_depth: 4,
824 function_probability: 0.9,
825 terminal_probability: 0.3,
826 }
827 }
828
829 pub fn with_max_depth(mut self, depth: usize) -> Self {
831 self.max_subtree_depth = depth;
832 self
833 }
834
835 pub fn with_function_probability(mut self, probability: f64) -> Self {
837 assert!(
838 (0.0..=1.0).contains(&probability),
839 "Probability must be in [0, 1]"
840 );
841 self.function_probability = probability;
842 self
843 }
844
845 pub fn with_terminal_probability(mut self, probability: f64) -> Self {
847 assert!(
848 (0.0..=1.0).contains(&probability),
849 "Probability must be in [0, 1]"
850 );
851 self.terminal_probability = probability;
852 self
853 }
854}
855
856impl Default for SubtreeMutation {
857 fn default() -> Self {
858 Self::new()
859 }
860}
861
862impl<T: Terminal, F: Function> MutationOperator<TreeGenome<T, F>> for SubtreeMutation {
863 fn mutate<R: Rng>(&self, genome: &mut TreeGenome<T, F>, rng: &mut R) {
864 let position = if rng.gen::<f64>() < self.function_probability {
866 genome
867 .random_function_position(rng)
868 .unwrap_or_else(|| genome.random_terminal_position(rng).unwrap_or_default())
869 } else {
870 genome
871 .random_terminal_position(rng)
872 .unwrap_or_else(|| genome.random_function_position(rng).unwrap_or_default())
873 };
874
875 let current_depth = position.len();
877 let remaining_depth = genome.max_depth.saturating_sub(current_depth);
878 let subtree_depth = remaining_depth.min(self.max_subtree_depth).max(1);
879
880 let new_subtree: TreeGenome<T, F> =
882 TreeGenome::generate_grow(rng, subtree_depth, self.terminal_probability);
883
884 genome.root.replace_subtree(&position, new_subtree.root);
886 }
887}
888
889#[derive(Clone, Debug, Default)]
894pub struct HoistMutation {
895 pub function_probability: f64,
897}
898
899impl HoistMutation {
900 pub fn new() -> Self {
902 Self {
903 function_probability: 0.5,
904 }
905 }
906
907 pub fn with_function_probability(mut self, probability: f64) -> Self {
909 assert!(
910 (0.0..=1.0).contains(&probability),
911 "Probability must be in [0, 1]"
912 );
913 self.function_probability = probability;
914 self
915 }
916}
917
918impl<T: Terminal, F: Function> MutationOperator<TreeGenome<T, F>> for HoistMutation {
919 fn mutate<R: Rng>(&self, genome: &mut TreeGenome<T, F>, rng: &mut R) {
920 let position = if rng.gen::<f64>() < self.function_probability {
922 genome
923 .random_function_position(rng)
924 .unwrap_or_else(|| genome.random_terminal_position(rng).unwrap_or_default())
925 } else {
926 genome.random_position(rng)
927 };
928
929 if position.is_empty() {
931 return;
932 }
933
934 if let Some(subtree) = genome.root.get_subtree(&position) {
936 genome.root = subtree.clone();
937 }
938 }
939}
940
941#[derive(Clone, Debug, Default)]
946pub struct ShrinkMutation;
947
948impl ShrinkMutation {
949 pub fn new() -> Self {
951 Self
952 }
953}
954
955impl<T: Terminal, F: Function> MutationOperator<TreeGenome<T, F>> for ShrinkMutation {
956 fn mutate<R: Rng>(&self, genome: &mut TreeGenome<T, F>, rng: &mut R) {
957 if let Some(func_position) = genome.random_function_position(rng) {
959 if func_position.is_empty() {
961 return;
962 }
963
964 if let Some(subtree) = genome.root.get_subtree(&func_position) {
966 let terminal_positions = subtree.terminal_positions();
967 if !terminal_positions.is_empty() {
968 let term_pos = &terminal_positions[rng.gen_range(0..terminal_positions.len())];
970
971 if let Some(terminal_node) = subtree.get_subtree(term_pos) {
973 let replacement = terminal_node.clone();
974 genome.root.replace_subtree(&func_position, replacement);
976 }
977 }
978 }
979 }
980 }
981}
982
983#[derive(Clone, Debug)]
987pub struct AdaptiveTreeMutation {
988 pub point_prob: f64,
990 pub subtree_prob: f64,
992 pub hoist_prob: f64,
994 pub shrink_prob: f64,
996}
997
998impl AdaptiveTreeMutation {
999 pub fn new() -> Self {
1001 Self {
1002 point_prob: 0.4,
1003 subtree_prob: 0.3,
1004 hoist_prob: 0.15,
1005 shrink_prob: 0.15,
1006 }
1007 }
1008
1009 pub fn with_probs(point: f64, subtree: f64, hoist: f64, shrink: f64) -> Self {
1011 Self {
1012 point_prob: point,
1013 subtree_prob: subtree,
1014 hoist_prob: hoist,
1015 shrink_prob: shrink,
1016 }
1017 }
1018}
1019
1020impl Default for AdaptiveTreeMutation {
1021 fn default() -> Self {
1022 Self::new()
1023 }
1024}
1025
1026impl<T: Terminal, F: Function> MutationOperator<TreeGenome<T, F>> for AdaptiveTreeMutation {
1027 fn mutate<R: Rng>(&self, genome: &mut TreeGenome<T, F>, rng: &mut R) {
1028 let total = self.point_prob + self.subtree_prob + self.hoist_prob + self.shrink_prob;
1029 if total <= 0.0 {
1030 return;
1031 }
1032
1033 let r = rng.gen::<f64>() * total;
1034 let mut cumulative = 0.0;
1035
1036 cumulative += self.point_prob;
1037 if r < cumulative {
1038 PointMutation::new().mutate(genome, rng);
1039 return;
1040 }
1041
1042 cumulative += self.subtree_prob;
1043 if r < cumulative {
1044 SubtreeMutation::new().mutate(genome, rng);
1045 return;
1046 }
1047
1048 cumulative += self.hoist_prob;
1049 if r < cumulative {
1050 HoistMutation::new().mutate(genome, rng);
1051 return;
1052 }
1053
1054 ShrinkMutation::new().mutate(genome, rng);
1055 }
1056}
1057
1058#[cfg(test)]
1059mod tests {
1060 use super::*;
1061 use approx::assert_relative_eq;
1062
1063 #[test]
1064 fn test_polynomial_mutation_respects_bounds() {
1065 let mut rng = rand::thread_rng();
1066 let bounds = MultiBounds::symmetric(5.0, 10);
1067
1068 for _ in 0..100 {
1069 let mut genome = RealVector::generate(&mut rng, &bounds);
1070 let mutation = PolynomialMutation::new(20.0).with_probability(1.0);
1071 mutation.mutate_bounded(&mut genome, &bounds, &mut rng);
1072
1073 for (i, &gene) in genome.genes().iter().enumerate() {
1074 let bound = bounds.get(i).unwrap();
1075 assert!(
1076 gene >= bound.min && gene <= bound.max,
1077 "Gene {} out of bounds: {} not in [{}, {}]",
1078 i,
1079 gene,
1080 bound.min,
1081 bound.max
1082 );
1083 }
1084 }
1085 }
1086
1087 #[test]
1088 fn test_polynomial_mutation_changes_genome() {
1089 let mut rng = rand::thread_rng();
1090 let bounds = MultiBounds::symmetric(5.0, 10);
1091 let original = RealVector::zeros(10);
1092 let mut genome = original.clone();
1093
1094 let mutation = PolynomialMutation::new(20.0).with_probability(1.0);
1095 mutation.mutate_bounded(&mut genome, &bounds, &mut rng);
1096
1097 let changed = genome
1099 .genes()
1100 .iter()
1101 .zip(original.genes())
1102 .filter(|(&a, &b)| a != b)
1103 .count();
1104 assert!(changed > 0, "No genes were mutated");
1105 }
1106
1107 #[test]
1108 fn test_polynomial_mutation_eta_effect() {
1109 let mut rng = rand::thread_rng();
1110 let bounds = MultiBounds::symmetric(1.0, 1);
1111
1112 let low_eta = PolynomialMutation::new(1.0).with_probability(1.0);
1114 let high_eta = PolynomialMutation::new(100.0).with_probability(1.0);
1116
1117 let mut low_total_change = 0.0;
1118 let mut high_total_change = 0.0;
1119 let trials = 1000;
1120
1121 for _ in 0..trials {
1122 let mut genome_low = RealVector::new(vec![0.0]);
1123 let mut genome_high = RealVector::new(vec![0.0]);
1124
1125 low_eta.mutate_bounded(&mut genome_low, &bounds, &mut rng);
1126 high_eta.mutate_bounded(&mut genome_high, &bounds, &mut rng);
1127
1128 low_total_change += genome_low[0].abs();
1129 high_total_change += genome_high[0].abs();
1130 }
1131
1132 assert!(
1133 low_total_change > high_total_change,
1134 "Low eta should produce larger average changes"
1135 );
1136 }
1137
1138 #[test]
1139 fn test_gaussian_mutation_changes_genome() {
1140 let mut rng = rand::thread_rng();
1141 let original = RealVector::zeros(10);
1142 let mut genome = original.clone();
1143
1144 let mutation = GaussianMutation::new(0.1).with_probability(1.0);
1145 mutation.mutate(&mut genome, &mut rng);
1146
1147 assert_ne!(genome, original);
1148 }
1149
1150 #[test]
1151 fn test_gaussian_mutation_bounded() {
1152 let mut rng = rand::thread_rng();
1153 let bounds = MultiBounds::symmetric(1.0, 10);
1154
1155 for _ in 0..100 {
1156 let mut genome = RealVector::zeros(10);
1157 let mutation = GaussianMutation::new(10.0).with_probability(1.0);
1158 mutation.mutate_bounded(&mut genome, &bounds, &mut rng);
1159
1160 for (i, &gene) in genome.genes().iter().enumerate() {
1161 let bound = bounds.get(i).unwrap();
1162 assert!(gene >= bound.min && gene <= bound.max);
1163 }
1164 }
1165 }
1166
1167 #[test]
1168 fn test_uniform_mutation() {
1169 let mut rng = rand::thread_rng();
1170 let bounds = MultiBounds::symmetric(1.0, 10);
1171
1172 for _ in 0..100 {
1173 let mut genome = RealVector::zeros(10);
1174 let mutation = UniformMutation::new().with_probability(1.0);
1175 mutation.mutate_bounded(&mut genome, &bounds, &mut rng);
1176
1177 for (i, &gene) in genome.genes().iter().enumerate() {
1178 let bound = bounds.get(i).unwrap();
1179 assert!(gene >= bound.min && gene <= bound.max);
1180 }
1181 }
1182 }
1183
1184 #[test]
1185 fn test_bit_flip_mutation() {
1186 let mut rng = rand::thread_rng();
1187 let original = BitString::zeros(100);
1188 let mut genome = original.clone();
1189
1190 let mutation = BitFlipMutation::new().with_probability(0.5);
1191 mutation.mutate(&mut genome, &mut rng);
1192
1193 let flipped = genome.count_ones();
1195 assert!(
1196 flipped > 20 && flipped < 80,
1197 "Expected ~50 flips, got {}",
1198 flipped
1199 );
1200 }
1201
1202 #[test]
1203 fn test_bit_flip_mutation_default_probability() {
1204 let mut rng = rand::thread_rng();
1205 let original = BitString::zeros(100);
1206 let mut genome = original.clone();
1207
1208 let mutation = BitFlipMutation::new(); mutation.mutate(&mut genome, &mut rng);
1210
1211 let mut total_flips = 0;
1215 for _ in 0..100 {
1216 let mut g = BitString::zeros(100);
1217 mutation.mutate(&mut g, &mut rng);
1218 total_flips += g.count_ones();
1219 }
1220
1221 let avg = total_flips as f64 / 100.0;
1223 assert!(avg > 0.5 && avg < 2.0, "Expected avg ~1, got {}", avg);
1224 }
1225
1226 #[test]
1227 fn test_swap_mutation() {
1228 let mut rng = rand::thread_rng();
1229 let mut genome = RealVector::new(vec![0.0, 1.0, 2.0, 3.0, 4.0]);
1230
1231 let mutation = SwapMutation::new();
1232 mutation.mutate(&mut genome, &mut rng);
1233
1234 let sum: f64 = genome.genes().iter().sum();
1236 assert_relative_eq!(sum, 10.0);
1237 }
1238
1239 #[test]
1240 fn test_swap_mutation_multiple() {
1241 let mut rng = rand::thread_rng();
1242 let original: Vec<f64> = (0..10).map(|i| i as f64).collect();
1243 let mut genome = RealVector::new(original.clone());
1244
1245 let mutation = SwapMutation::with_swaps(5);
1246 mutation.mutate(&mut genome, &mut rng);
1247
1248 let sum: f64 = genome.genes().iter().sum();
1250 assert_relative_eq!(sum, 45.0);
1251 }
1252
1253 #[test]
1254 fn test_scramble_mutation() {
1255 let mut rng = rand::thread_rng();
1256 let original: Vec<f64> = (0..10).map(|i| i as f64).collect();
1257 let mut genome = RealVector::new(original.clone());
1258
1259 let mutation = ScrambleMutation::new();
1260 mutation.mutate(&mut genome, &mut rng);
1261
1262 let sum: f64 = genome.genes().iter().sum();
1264 assert_relative_eq!(sum, 45.0);
1265
1266 let mut sorted: Vec<f64> = genome.genes().to_vec();
1268 sorted.sort_by(|a, b| a.partial_cmp(b).unwrap());
1269 assert_eq!(sorted, original);
1270 }
1271
1272 #[test]
1273 fn test_scramble_mutation_bitstring() {
1274 let mut rng = rand::thread_rng();
1275 let original = BitString::new(vec![true, true, true, false, false, false, true, false]);
1276 let mut genome = original.clone();
1277
1278 let mutation = ScrambleMutation::new();
1279 mutation.mutate(&mut genome, &mut rng);
1280
1281 assert_eq!(genome.count_ones(), original.count_ones());
1283 }
1284
1285 #[test]
1290 fn test_permutation_swap_mutation() {
1291 let mut rng = rand::thread_rng();
1292 let original = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
1293 let mut genome = original.clone();
1294
1295 let mutation = PermutationSwapMutation::new();
1296 mutation.mutate(&mut genome, &mut rng);
1297
1298 assert!(genome.is_valid_permutation());
1300 assert_eq!(genome.dimension(), 8);
1301 }
1302
1303 #[test]
1304 fn test_permutation_swap_mutation_multiple() {
1305 let mut rng = rand::thread_rng();
1306 let original = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
1307 let mut genome = original.clone();
1308
1309 let mutation = PermutationSwapMutation::with_swaps(5);
1310 mutation.mutate(&mut genome, &mut rng);
1311
1312 assert!(genome.is_valid_permutation());
1314 assert_eq!(genome.dimension(), 10);
1315 }
1316
1317 #[test]
1318 fn test_insert_mutation() {
1319 let mut rng = rand::thread_rng();
1320
1321 for _ in 0..50 {
1322 let mut genome = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
1323
1324 let mutation = InsertMutation::new();
1325 mutation.mutate(&mut genome, &mut rng);
1326
1327 assert!(genome.is_valid_permutation());
1328 assert_eq!(genome.dimension(), 8);
1329 }
1330 }
1331
1332 #[test]
1333 fn test_inversion_mutation() {
1334 let mut rng = rand::thread_rng();
1335
1336 for _ in 0..50 {
1337 let mut genome = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
1338
1339 let mutation = InversionMutation::new();
1340 mutation.mutate(&mut genome, &mut rng);
1341
1342 assert!(genome.is_valid_permutation());
1343 assert_eq!(genome.dimension(), 8);
1344 }
1345 }
1346
1347 #[test]
1348 fn test_inversion_mutation_reverses_segment() {
1349 use rand::SeedableRng;
1350 let mut rng = rand::rngs::StdRng::seed_from_u64(42);
1351
1352 let mut genome = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
1353
1354 let mutation = InversionMutation::new();
1355 mutation.mutate(&mut genome, &mut rng);
1356
1357 assert!(genome.is_valid_permutation());
1359 }
1360
1361 #[test]
1362 fn test_permutation_scramble_mutation() {
1363 let mut rng = rand::thread_rng();
1364
1365 for _ in 0..50 {
1366 let mut genome = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
1367
1368 let mutation = PermutationScrambleMutation::new();
1369 mutation.mutate(&mut genome, &mut rng);
1370
1371 assert!(genome.is_valid_permutation());
1372 assert_eq!(genome.dimension(), 8);
1373 }
1374 }
1375
1376 #[test]
1377 fn test_displacement_mutation() {
1378 let mut rng = rand::thread_rng();
1379
1380 for _ in 0..50 {
1381 let mut genome = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
1382
1383 let mutation = DisplacementMutation::new();
1384 mutation.mutate(&mut genome, &mut rng);
1385
1386 assert!(genome.is_valid_permutation());
1387 assert_eq!(genome.dimension(), 10);
1388 }
1389 }
1390
1391 #[test]
1392 fn test_adaptive_permutation_mutation() {
1393 let mut rng = rand::thread_rng();
1394
1395 for _ in 0..100 {
1396 let mut genome = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
1397
1398 let mutation = AdaptivePermutationMutation::new();
1399 mutation.mutate(&mut genome, &mut rng);
1400
1401 assert!(genome.is_valid_permutation());
1402 assert_eq!(genome.dimension(), 8);
1403 }
1404 }
1405
1406 #[test]
1407 fn test_adaptive_permutation_mutation_custom_probs() {
1408 let mut rng = rand::thread_rng();
1409
1410 let mutation = AdaptivePermutationMutation::with_probs(0.0, 0.0, 1.0, 0.0);
1412
1413 for _ in 0..50 {
1414 let mut genome = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
1415 mutation.mutate(&mut genome, &mut rng);
1416
1417 assert!(genome.is_valid_permutation());
1418 }
1419 }
1420
1421 use crate::genome::tree::{ArithmeticFunction, ArithmeticTerminal};
1426
1427 fn create_test_tree() -> TreeGenome<ArithmeticTerminal, ArithmeticFunction> {
1428 let x0 = TreeNode::terminal(ArithmeticTerminal::Variable(0));
1430 let c1 = TreeNode::terminal(ArithmeticTerminal::Constant(1.0));
1431 let x1 = TreeNode::terminal(ArithmeticTerminal::Variable(1));
1432 let mul = TreeNode::function(ArithmeticFunction::Mul, vec![c1, x1]);
1433 let add = TreeNode::function(ArithmeticFunction::Add, vec![x0, mul]);
1434 TreeGenome::new(add, 5)
1435 }
1436
1437 #[test]
1438 fn test_point_mutation_preserves_structure() {
1439 let mut rng = rand::thread_rng();
1440 let original = create_test_tree();
1441 let original_size = original.size();
1442
1443 for _ in 0..50 {
1444 let mut genome = original.clone();
1445 let mutation = PointMutation::new().with_probability(1.0);
1446 mutation.mutate(&mut genome, &mut rng);
1447
1448 assert_eq!(genome.size(), original_size);
1450 assert!(genome.evaluate(&[1.0, 2.0]).is_finite());
1451 }
1452 }
1453
1454 #[test]
1455 fn test_point_mutation_changes_tree() {
1456 let mut rng = rand::thread_rng();
1457 let original = create_test_tree();
1458 let mut any_changed = false;
1459
1460 for _ in 0..100 {
1461 let mut genome = original.clone();
1462 let mutation = PointMutation::new().with_probability(1.0);
1463 mutation.mutate(&mut genome, &mut rng);
1464
1465 let orig_val = original.evaluate(&[1.0, 2.0]);
1467 let new_val = genome.evaluate(&[1.0, 2.0]);
1468 if (orig_val - new_val).abs() > 1e-10 {
1469 any_changed = true;
1470 break;
1471 }
1472 }
1473
1474 assert!(
1475 any_changed,
1476 "Point mutation should sometimes change the tree"
1477 );
1478 }
1479
1480 #[test]
1481 fn test_subtree_mutation() {
1482 use rand::SeedableRng;
1483 let mut rng = rand::rngs::StdRng::seed_from_u64(42);
1484 let original = create_test_tree();
1485
1486 for _ in 0..50 {
1487 let mut genome = original.clone();
1488 let mutation = SubtreeMutation::new().with_max_depth(3);
1489 mutation.mutate(&mut genome, &mut rng);
1490
1491 assert!(genome.size() >= 1);
1493 let result = genome.evaluate(&[1.0, 2.0]);
1496 assert!(result.is_nan() || result.is_finite());
1497 }
1498 }
1499
1500 #[test]
1501 fn test_hoist_mutation_reduces_tree() {
1502 let mut rng = rand::thread_rng();
1503
1504 let tree: TreeGenome<ArithmeticTerminal, ArithmeticFunction> =
1506 TreeGenome::generate_full(&mut rng, 4, 5);
1507
1508 let original_size = tree.size();
1509
1510 for _ in 0..50 {
1511 let mut genome = tree.clone();
1512 let mutation = HoistMutation::new();
1513 mutation.mutate(&mut genome, &mut rng);
1514
1515 assert!(genome.size() <= original_size);
1517 assert!(genome.evaluate(&[1.0, 2.0]).is_finite());
1518 }
1519 }
1520
1521 #[test]
1522 fn test_shrink_mutation_reduces_tree() {
1523 let mut rng = rand::thread_rng();
1524
1525 let tree: TreeGenome<ArithmeticTerminal, ArithmeticFunction> =
1527 TreeGenome::generate_full(&mut rng, 4, 5);
1528
1529 for _ in 0..50 {
1530 let mut genome = tree.clone();
1531 let original_size = genome.size();
1532 let mutation = ShrinkMutation::new();
1533 mutation.mutate(&mut genome, &mut rng);
1534
1535 assert!(genome.size() <= original_size);
1537 assert!(genome.evaluate(&[1.0, 2.0]).is_finite());
1538 }
1539 }
1540
1541 #[test]
1542 fn test_adaptive_tree_mutation() {
1543 let mut rng = rand::thread_rng();
1544
1545 for _ in 0..100 {
1546 let mut genome: TreeGenome<ArithmeticTerminal, ArithmeticFunction> =
1547 TreeGenome::generate_ramped_half_and_half(&mut rng, 2, 5);
1548
1549 let mutation = AdaptiveTreeMutation::new();
1550 mutation.mutate(&mut genome, &mut rng);
1551
1552 assert!(genome.size() >= 1);
1554 assert!(genome.evaluate(&[1.0, 2.0]).is_finite());
1555 }
1556 }
1557
1558 #[test]
1559 fn test_adaptive_tree_mutation_custom_probs() {
1560 let mut rng = rand::thread_rng();
1561
1562 let mutation = AdaptiveTreeMutation::with_probs(1.0, 0.0, 0.0, 0.0);
1564
1565 for _ in 0..50 {
1566 let mut genome: TreeGenome<ArithmeticTerminal, ArithmeticFunction> =
1567 TreeGenome::generate_ramped_half_and_half(&mut rng, 2, 5);
1568 let original_size = genome.size();
1569 mutation.mutate(&mut genome, &mut rng);
1570
1571 assert_eq!(genome.size(), original_size);
1573 }
1574 }
1575}