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 pub unbounded_sigma: Option<f64>,
35}
36
37impl PolynomialMutation {
38 pub fn new(eta_m: f64) -> Self {
40 assert!(eta_m >= 0.0, "Distribution index must be non-negative");
41 Self {
42 eta_m,
43 mutation_probability: None,
44 unbounded_sigma: None,
45 }
46 }
47
48 pub fn with_probability(mut self, probability: f64) -> Self {
50 assert!(
51 (0.0..=1.0).contains(&probability),
52 "Probability must be in [0, 1]"
53 );
54 self.mutation_probability = Some(probability);
55 self
56 }
57
58 pub fn with_unbounded_sigma(mut self, sigma: f64) -> Self {
65 assert!(sigma >= 0.0, "Sigma must be non-negative");
66 self.unbounded_sigma = Some(sigma);
67 self
68 }
69
70 fn mutate_gene<R: Rng>(&self, gene: f64, min: f64, max: f64, rng: &mut R) -> f64 {
72 let range = max - min;
73 if range <= 0.0 {
74 return gene;
75 }
76
77 let delta1 = (gene - min) / range;
78 let delta2 = (max - gene) / range;
79
80 let u = rng.gen::<f64>();
81 let delta_q = if u <= 0.5 {
82 let val = 2.0 * u + (1.0 - 2.0 * u) * (1.0 - delta1).powf(self.eta_m + 1.0);
83 val.powf(1.0 / (self.eta_m + 1.0)) - 1.0
84 } else {
85 let val = 2.0 * (1.0 - u) + 2.0 * (u - 0.5) * (1.0 - delta2).powf(self.eta_m + 1.0);
86 1.0 - val.powf(1.0 / (self.eta_m + 1.0))
87 };
88
89 (gene + delta_q * range).clamp(min, max)
90 }
91}
92
93impl MutationOperator<RealVector> for PolynomialMutation {
94 fn mutate<R: Rng>(&self, genome: &mut RealVector, rng: &mut R) {
95 let n = genome.dimension();
100 let prob = self.mutation_probability.unwrap_or(1.0 / n as f64);
101
102 for gene in genome.genes_mut() {
103 if rng.gen::<f64>() < prob {
104 let sigma = self
105 .unbounded_sigma
106 .unwrap_or_else(|| 0.1 * (1.0 + gene.abs()));
107 if sigma > 0.0 {
108 let normal = Normal::new(0.0, sigma).unwrap();
109 *gene += normal.sample(rng);
110 }
111 }
112 }
113 }
114
115 fn mutation_probability(&self) -> Option<f64> {
116 self.mutation_probability
117 }
118}
119
120impl BoundedMutationOperator<RealVector> for PolynomialMutation {
121 fn mutate_bounded<R: Rng>(&self, genome: &mut RealVector, bounds: &MultiBounds, rng: &mut R) {
122 let n = genome.dimension();
123 let prob = self.mutation_probability.unwrap_or(1.0 / n as f64);
124
125 for i in 0..n {
126 if rng.gen::<f64>() < prob {
127 if let Some(bound) = bounds.get(i) {
128 genome.genes_mut()[i] =
129 self.mutate_gene(genome.genes()[i], bound.min, bound.max, rng);
130 }
131 }
132 }
133 }
134}
135
136#[derive(Clone, Debug)]
140pub struct GaussianMutation {
141 pub sigma: f64,
143 pub mutation_probability: Option<f64>,
145}
146
147impl GaussianMutation {
148 pub fn new(sigma: f64) -> Self {
150 assert!(sigma >= 0.0, "Sigma must be non-negative");
151 Self {
152 sigma,
153 mutation_probability: None,
154 }
155 }
156
157 pub fn with_probability(mut self, probability: f64) -> Self {
159 assert!(
160 (0.0..=1.0).contains(&probability),
161 "Probability must be in [0, 1]"
162 );
163 self.mutation_probability = Some(probability);
164 self
165 }
166}
167
168impl MutationOperator<RealVector> for GaussianMutation {
169 fn mutate<R: Rng>(&self, genome: &mut RealVector, rng: &mut R) {
170 let n = genome.dimension();
171 let prob = self.mutation_probability.unwrap_or(1.0 / n as f64);
172 let normal = Normal::new(0.0, self.sigma).unwrap();
173
174 for gene in genome.genes_mut() {
175 if rng.gen::<f64>() < prob {
176 *gene += normal.sample(rng);
177 }
178 }
179 }
180
181 fn mutation_probability(&self) -> Option<f64> {
182 self.mutation_probability
183 }
184}
185
186impl BoundedMutationOperator<RealVector> for GaussianMutation {
187 fn mutate_bounded<R: Rng>(&self, genome: &mut RealVector, bounds: &MultiBounds, rng: &mut R) {
188 let n = genome.dimension();
189 let prob = self.mutation_probability.unwrap_or(1.0 / n as f64);
190 let normal = Normal::new(0.0, self.sigma).unwrap();
191
192 for i in 0..n {
193 if rng.gen::<f64>() < prob {
194 genome.genes_mut()[i] += normal.sample(rng);
195 if let Some(bound) = bounds.get(i) {
196 genome.genes_mut()[i] = bound.clamp(genome.genes()[i]);
197 }
198 }
199 }
200 }
201}
202
203#[derive(Clone, Debug)]
207pub struct UniformMutation {
208 pub mutation_probability: Option<f64>,
210}
211
212impl UniformMutation {
213 pub fn new() -> Self {
215 Self {
216 mutation_probability: None,
217 }
218 }
219
220 pub fn with_probability(mut self, probability: f64) -> Self {
222 assert!(
223 (0.0..=1.0).contains(&probability),
224 "Probability must be in [0, 1]"
225 );
226 self.mutation_probability = Some(probability);
227 self
228 }
229}
230
231impl Default for UniformMutation {
232 fn default() -> Self {
233 Self::new()
234 }
235}
236
237impl MutationOperator<RealVector> for UniformMutation {
238 fn mutate<R: Rng>(&self, genome: &mut RealVector, rng: &mut R) {
239 let default_bounds = MultiBounds::symmetric(1.0, genome.dimension());
243 self.mutate_bounded(genome, &default_bounds, rng);
244 }
245
246 fn mutation_probability(&self) -> Option<f64> {
247 self.mutation_probability
248 }
249}
250
251impl BoundedMutationOperator<RealVector> for UniformMutation {
252 fn mutate_bounded<R: Rng>(&self, genome: &mut RealVector, bounds: &MultiBounds, rng: &mut R) {
253 let n = genome.dimension();
254 let prob = self.mutation_probability.unwrap_or(1.0 / n as f64);
255
256 for i in 0..n {
257 if rng.gen::<f64>() < prob {
258 if let Some(bound) = bounds.get(i) {
259 genome.genes_mut()[i] = rng.gen_range(bound.min..=bound.max);
260 }
261 }
262 }
263 }
264}
265
266#[derive(Clone, Debug)]
270pub struct BitFlipMutation {
271 pub mutation_probability: Option<f64>,
273}
274
275impl BitFlipMutation {
276 pub fn new() -> Self {
278 Self {
279 mutation_probability: None,
280 }
281 }
282
283 pub fn with_probability(mut self, probability: f64) -> Self {
285 assert!(
286 (0.0..=1.0).contains(&probability),
287 "Probability must be in [0, 1]"
288 );
289 self.mutation_probability = Some(probability);
290 self
291 }
292}
293
294impl Default for BitFlipMutation {
295 fn default() -> Self {
296 Self::new()
297 }
298}
299
300impl MutationOperator<BitString> for BitFlipMutation {
301 fn mutate<R: Rng>(&self, genome: &mut BitString, rng: &mut R) {
302 let n = genome.len();
303 let prob = self.mutation_probability.unwrap_or(1.0 / n as f64);
304
305 for i in 0..n {
306 if rng.gen::<f64>() < prob {
307 genome.flip(i);
308 }
309 }
310 }
311
312 fn mutation_probability(&self) -> Option<f64> {
313 self.mutation_probability
314 }
315}
316
317#[derive(Clone, Debug)]
321pub struct SwapMutation {
322 pub num_swaps: usize,
324}
325
326impl SwapMutation {
327 pub fn new() -> Self {
329 Self { num_swaps: 1 }
330 }
331
332 pub fn with_swaps(num_swaps: usize) -> Self {
334 Self { num_swaps }
335 }
336}
337
338impl Default for SwapMutation {
339 fn default() -> Self {
342 Self::new()
343 }
344}
345
346impl MutationOperator<BitString> for SwapMutation {
347 fn mutate<R: Rng>(&self, genome: &mut BitString, rng: &mut R) {
348 let n = genome.len();
349 if n < 2 {
350 return;
351 }
352
353 for _ in 0..self.num_swaps {
354 let i = rng.gen_range(0..n);
355 let j = rng.gen_range(0..n);
356 if i != j {
357 let temp = genome.bits()[i];
358 genome.bits_mut()[i] = genome.bits()[j];
359 genome.bits_mut()[j] = temp;
360 }
361 }
362 }
363}
364
365impl MutationOperator<RealVector> for SwapMutation {
366 fn mutate<R: Rng>(&self, genome: &mut RealVector, rng: &mut R) {
367 let n = genome.dimension();
368 if n < 2 {
369 return;
370 }
371
372 for _ in 0..self.num_swaps {
373 let i = rng.gen_range(0..n);
374 let j = rng.gen_range(0..n);
375 if i != j {
376 genome.genes_mut().swap(i, j);
377 }
378 }
379 }
380}
381
382#[derive(Clone, Debug, Default)]
386pub struct ScrambleMutation;
387
388impl ScrambleMutation {
389 pub fn new() -> Self {
391 Self
392 }
393}
394
395impl MutationOperator<BitString> for ScrambleMutation {
396 fn mutate<R: Rng>(&self, genome: &mut BitString, rng: &mut R) {
397 use rand::seq::SliceRandom;
398
399 let n = genome.len();
400 if n < 2 {
401 return;
402 }
403
404 let mut start = rng.gen_range(0..n);
405 let mut end = rng.gen_range(0..n);
406 if start > end {
407 std::mem::swap(&mut start, &mut end);
408 }
409
410 let segment: Vec<bool> = (start..=end).map(|i| genome.bits()[i]).collect();
412 let mut shuffled = segment;
413 shuffled.shuffle(rng);
414
415 for (i, val) in shuffled.into_iter().enumerate() {
416 genome.bits_mut()[start + i] = val;
417 }
418 }
419}
420
421impl MutationOperator<RealVector> for ScrambleMutation {
422 fn mutate<R: Rng>(&self, genome: &mut RealVector, rng: &mut R) {
423 use rand::seq::SliceRandom;
424
425 let n = genome.dimension();
426 if n < 2 {
427 return;
428 }
429
430 let mut start = rng.gen_range(0..n);
431 let mut end = rng.gen_range(0..n);
432 if start > end {
433 std::mem::swap(&mut start, &mut end);
434 }
435
436 let slice = &mut genome.genes_mut()[start..=end];
438 slice.shuffle(rng);
439 }
440}
441
442#[derive(Clone, Debug)]
451pub struct PermutationSwapMutation {
452 pub num_swaps: usize,
454}
455
456impl PermutationSwapMutation {
457 pub fn new() -> Self {
459 Self { num_swaps: 1 }
460 }
461
462 pub fn with_swaps(num_swaps: usize) -> Self {
464 Self { num_swaps }
465 }
466}
467
468impl Default for PermutationSwapMutation {
469 fn default() -> Self {
472 Self::new()
473 }
474}
475
476impl MutationOperator<Permutation> for PermutationSwapMutation {
477 fn mutate<R: Rng>(&self, genome: &mut Permutation, rng: &mut R) {
478 let n = genome.dimension();
479 if n < 2 {
480 return;
481 }
482
483 for _ in 0..self.num_swaps {
484 let i = rng.gen_range(0..n);
485 let j = rng.gen_range(0..n);
486 if i != j {
487 genome.swap(i, j);
488 }
489 }
490 }
491}
492
493#[derive(Clone, Debug)]
498pub struct InsertMutation {
499 pub num_inserts: usize,
501}
502
503impl InsertMutation {
504 pub fn new() -> Self {
506 Self { num_inserts: 1 }
507 }
508
509 pub fn with_inserts(num_inserts: usize) -> Self {
511 Self { num_inserts }
512 }
513}
514
515impl Default for InsertMutation {
516 fn default() -> Self {
519 Self::new()
520 }
521}
522
523impl MutationOperator<Permutation> for InsertMutation {
524 fn mutate<R: Rng>(&self, genome: &mut Permutation, rng: &mut R) {
525 let n = genome.dimension();
526 if n < 2 {
527 return;
528 }
529
530 for _ in 0..self.num_inserts {
531 let from = rng.gen_range(0..n);
532 let to = rng.gen_range(0..n);
533 if from != to {
534 genome.insert(from, to);
535 }
536 }
537 }
538}
539
540#[derive(Clone, Debug, Default)]
546pub struct InversionMutation;
547
548impl InversionMutation {
549 pub fn new() -> Self {
551 Self
552 }
553}
554
555impl MutationOperator<Permutation> for InversionMutation {
556 fn mutate<R: Rng>(&self, genome: &mut Permutation, rng: &mut R) {
557 let n = genome.dimension();
558 if n < 2 {
559 return;
560 }
561
562 let mut start = rng.gen_range(0..n);
563 let mut end = rng.gen_range(0..n);
564 if start > end {
565 std::mem::swap(&mut start, &mut end);
566 }
567
568 genome.reverse_segment(start, end);
569 }
570}
571
572#[derive(Clone, Debug, Default)]
577pub struct PermutationScrambleMutation;
578
579impl PermutationScrambleMutation {
580 pub fn new() -> Self {
582 Self
583 }
584}
585
586impl MutationOperator<Permutation> for PermutationScrambleMutation {
587 fn mutate<R: Rng>(&self, genome: &mut Permutation, rng: &mut R) {
588 use rand::seq::SliceRandom;
589
590 let n = genome.dimension();
591 if n < 2 {
592 return;
593 }
594
595 let mut start = rng.gen_range(0..n);
596 let mut end = rng.gen_range(0..n);
597 if start > end {
598 std::mem::swap(&mut start, &mut end);
599 }
600
601 let perm = genome.permutation_mut();
603 perm[start..=end].shuffle(rng);
604 }
605}
606
607#[derive(Clone, Debug, Default)]
612pub struct DisplacementMutation;
613
614impl DisplacementMutation {
615 pub fn new() -> Self {
617 Self
618 }
619}
620
621impl MutationOperator<Permutation> for DisplacementMutation {
622 fn mutate<R: Rng>(&self, genome: &mut Permutation, rng: &mut R) {
623 let n = genome.dimension();
624 if n < 3 {
625 return;
626 }
627
628 let mut start = rng.gen_range(0..n);
630 let mut end = rng.gen_range(0..n);
631 if start > end {
632 std::mem::swap(&mut start, &mut end);
633 }
634
635 let segment_len = end - start + 1;
636 if segment_len >= n {
637 return; }
639
640 let perm = genome.permutation_mut();
642 let segment: Vec<usize> = perm[start..=end].to_vec();
643
644 let remaining: Vec<usize> = perm[..start]
646 .iter()
647 .chain(perm[end + 1..].iter())
648 .copied()
649 .collect();
650
651 let insert_pos = rng.gen_range(0..=remaining.len());
653
654 let new_perm: Vec<usize> = remaining[..insert_pos]
656 .iter()
657 .chain(segment.iter())
658 .chain(remaining[insert_pos..].iter())
659 .copied()
660 .collect();
661
662 perm.copy_from_slice(&new_perm);
663 }
664}
665
666#[derive(Clone, Debug)]
670pub struct AdaptivePermutationMutation {
671 pub swap_prob: f64,
673 pub insert_prob: f64,
675 pub inversion_prob: f64,
677 pub scramble_prob: f64,
679}
680
681impl AdaptivePermutationMutation {
682 pub fn new() -> Self {
684 Self {
685 swap_prob: 0.25,
686 insert_prob: 0.25,
687 inversion_prob: 0.25,
688 scramble_prob: 0.25,
689 }
690 }
691
692 pub fn with_probs(swap: f64, insert: f64, inversion: f64, scramble: f64) -> Self {
694 Self {
695 swap_prob: swap,
696 insert_prob: insert,
697 inversion_prob: inversion,
698 scramble_prob: scramble,
699 }
700 }
701}
702
703impl Default for AdaptivePermutationMutation {
704 fn default() -> Self {
705 Self::new()
706 }
707}
708
709impl MutationOperator<Permutation> for AdaptivePermutationMutation {
710 fn mutate<R: Rng>(&self, genome: &mut Permutation, rng: &mut R) {
711 let total = self.swap_prob + self.insert_prob + self.inversion_prob + self.scramble_prob;
712 if total <= 0.0 {
713 return;
714 }
715
716 let r = rng.gen::<f64>() * total;
717 let mut cumulative = 0.0;
718
719 cumulative += self.swap_prob;
720 if r < cumulative {
721 PermutationSwapMutation::new().mutate(genome, rng);
722 return;
723 }
724
725 cumulative += self.insert_prob;
726 if r < cumulative {
727 InsertMutation::new().mutate(genome, rng);
728 return;
729 }
730
731 cumulative += self.inversion_prob;
732 if r < cumulative {
733 InversionMutation::new().mutate(genome, rng);
734 return;
735 }
736
737 PermutationScrambleMutation::new().mutate(genome, rng);
738 }
739}
740
741#[derive(Clone, Debug)]
754pub struct PointMutation {
755 pub mutation_probability: f64,
757 pub function_probability: f64,
759}
760
761impl PointMutation {
762 pub fn new() -> Self {
766 Self {
767 mutation_probability: 0.1,
768 function_probability: 0.9,
769 }
770 }
771
772 pub fn with_probability(mut self, probability: f64) -> Self {
774 assert!(
775 (0.0..=1.0).contains(&probability),
776 "Probability must be in [0, 1]"
777 );
778 self.mutation_probability = probability;
779 self
780 }
781
782 pub fn with_function_probability(mut self, probability: f64) -> Self {
784 assert!(
785 (0.0..=1.0).contains(&probability),
786 "Probability must be in [0, 1]"
787 );
788 self.function_probability = probability;
789 self
790 }
791
792 fn mutate_node_in_place<T: Terminal, F: Function, R: Rng>(
801 &self,
802 node: &mut TreeNode<T, F>,
803 rng: &mut R,
804 ) {
805 match node {
806 TreeNode::Terminal(t) => *t = T::random(rng),
807 TreeNode::Function(func, _children) => {
808 let target_arity = func.arity();
809 let matching_funcs: Vec<&F> = F::functions()
810 .iter()
811 .filter(|f| f.arity() == target_arity)
812 .collect();
813 if !matching_funcs.is_empty() {
814 *func = matching_funcs[rng.gen_range(0..matching_funcs.len())].clone();
815 }
816 }
818 }
819 }
820}
821
822impl Default for PointMutation {
823 fn default() -> Self {
824 Self::new()
825 }
826}
827
828impl<T: Terminal, F: Function> MutationOperator<TreeGenome<T, F>> for PointMutation {
829 fn mutate<R: Rng>(&self, genome: &mut TreeGenome<T, F>, rng: &mut R) {
830 let mut stack: Vec<&mut TreeNode<T, F>> = vec![&mut genome.root];
836 while let Some(node) = stack.pop() {
837 if rng.gen::<f64>() < self.mutation_probability {
838 self.mutate_node_in_place(node, rng);
839 }
840 if let TreeNode::Function(_, children) = node {
841 for child in children.iter_mut() {
842 stack.push(child);
843 }
844 }
845 }
846 }
847
848 fn mutation_probability(&self) -> Option<f64> {
849 Some(self.mutation_probability)
850 }
851}
852
853#[derive(Clone, Debug)]
858pub struct SubtreeMutation {
859 pub max_subtree_depth: usize,
861 pub function_probability: f64,
863 pub terminal_probability: f64,
865}
866
867impl SubtreeMutation {
868 pub fn new() -> Self {
870 Self {
871 max_subtree_depth: 4,
872 function_probability: 0.9,
873 terminal_probability: 0.3,
874 }
875 }
876
877 pub fn with_max_depth(mut self, depth: usize) -> Self {
879 self.max_subtree_depth = depth;
880 self
881 }
882
883 pub fn with_function_probability(mut self, probability: f64) -> Self {
885 assert!(
886 (0.0..=1.0).contains(&probability),
887 "Probability must be in [0, 1]"
888 );
889 self.function_probability = probability;
890 self
891 }
892
893 pub fn with_terminal_probability(mut self, probability: f64) -> Self {
895 assert!(
896 (0.0..=1.0).contains(&probability),
897 "Probability must be in [0, 1]"
898 );
899 self.terminal_probability = probability;
900 self
901 }
902}
903
904impl Default for SubtreeMutation {
905 fn default() -> Self {
906 Self::new()
907 }
908}
909
910impl<T: Terminal, F: Function> MutationOperator<TreeGenome<T, F>> for SubtreeMutation {
911 fn mutate<R: Rng>(&self, genome: &mut TreeGenome<T, F>, rng: &mut R) {
912 let position = if rng.gen::<f64>() < self.function_probability {
914 genome
915 .random_function_position(rng)
916 .unwrap_or_else(|| genome.random_terminal_position(rng).unwrap_or_default())
917 } else {
918 genome
919 .random_terminal_position(rng)
920 .unwrap_or_else(|| genome.random_function_position(rng).unwrap_or_default())
921 };
922
923 let point_depth = position.len();
931 let budget = genome
932 .max_depth
933 .saturating_sub(point_depth)
934 .min(self.max_subtree_depth)
935 .max(1);
936
937 let new_root =
943 TreeGenome::<T, F>::generate_grow(rng, budget - 1, self.terminal_probability).root;
944
945 let new_root = if point_depth + new_root.depth() > genome.max_depth {
949 TreeNode::Terminal(T::random(rng))
950 } else {
951 new_root
952 };
953
954 genome.root.replace_subtree(&position, new_root);
956 }
957}
958
959#[derive(Clone, Debug, Default)]
964pub struct HoistMutation {
965 pub function_probability: f64,
967}
968
969impl HoistMutation {
970 pub fn new() -> Self {
972 Self {
973 function_probability: 0.5,
974 }
975 }
976
977 pub fn with_function_probability(mut self, probability: f64) -> Self {
979 assert!(
980 (0.0..=1.0).contains(&probability),
981 "Probability must be in [0, 1]"
982 );
983 self.function_probability = probability;
984 self
985 }
986}
987
988impl<T: Terminal, F: Function> MutationOperator<TreeGenome<T, F>> for HoistMutation {
989 fn mutate<R: Rng>(&self, genome: &mut TreeGenome<T, F>, rng: &mut R) {
990 let position = if rng.gen::<f64>() < self.function_probability {
992 genome
993 .random_function_position(rng)
994 .unwrap_or_else(|| genome.random_terminal_position(rng).unwrap_or_default())
995 } else {
996 genome.random_position(rng)
997 };
998
999 if position.is_empty() {
1001 return;
1002 }
1003
1004 if let Some(subtree) = genome.root.get_subtree(&position) {
1006 genome.root = subtree.clone();
1007 }
1008 }
1009}
1010
1011#[derive(Clone, Debug, Default)]
1016pub struct ShrinkMutation;
1017
1018impl ShrinkMutation {
1019 pub fn new() -> Self {
1021 Self
1022 }
1023}
1024
1025impl<T: Terminal, F: Function> MutationOperator<TreeGenome<T, F>> for ShrinkMutation {
1026 fn mutate<R: Rng>(&self, genome: &mut TreeGenome<T, F>, rng: &mut R) {
1027 if let Some(func_position) = genome.random_function_position(rng) {
1029 if func_position.is_empty() {
1031 return;
1032 }
1033
1034 if let Some(subtree) = genome.root.get_subtree(&func_position) {
1036 let terminal_positions = subtree.terminal_positions();
1037 if !terminal_positions.is_empty() {
1038 let term_pos = &terminal_positions[rng.gen_range(0..terminal_positions.len())];
1040
1041 if let Some(terminal_node) = subtree.get_subtree(term_pos) {
1043 let replacement = terminal_node.clone();
1044 genome.root.replace_subtree(&func_position, replacement);
1046 }
1047 }
1048 }
1049 }
1050 }
1051}
1052
1053#[derive(Clone, Debug)]
1057pub struct AdaptiveTreeMutation {
1058 pub point_prob: f64,
1060 pub subtree_prob: f64,
1062 pub hoist_prob: f64,
1064 pub shrink_prob: f64,
1066}
1067
1068impl AdaptiveTreeMutation {
1069 pub fn new() -> Self {
1071 Self {
1072 point_prob: 0.4,
1073 subtree_prob: 0.3,
1074 hoist_prob: 0.15,
1075 shrink_prob: 0.15,
1076 }
1077 }
1078
1079 pub fn with_probs(point: f64, subtree: f64, hoist: f64, shrink: f64) -> Self {
1081 Self {
1082 point_prob: point,
1083 subtree_prob: subtree,
1084 hoist_prob: hoist,
1085 shrink_prob: shrink,
1086 }
1087 }
1088}
1089
1090impl Default for AdaptiveTreeMutation {
1091 fn default() -> Self {
1092 Self::new()
1093 }
1094}
1095
1096impl<T: Terminal, F: Function> MutationOperator<TreeGenome<T, F>> for AdaptiveTreeMutation {
1097 fn mutate<R: Rng>(&self, genome: &mut TreeGenome<T, F>, rng: &mut R) {
1098 let total = self.point_prob + self.subtree_prob + self.hoist_prob + self.shrink_prob;
1099 if total <= 0.0 {
1100 return;
1101 }
1102
1103 let r = rng.gen::<f64>() * total;
1104 let mut cumulative = 0.0;
1105
1106 cumulative += self.point_prob;
1107 if r < cumulative {
1108 PointMutation::new().mutate(genome, rng);
1109 return;
1110 }
1111
1112 cumulative += self.subtree_prob;
1113 if r < cumulative {
1114 SubtreeMutation::new().mutate(genome, rng);
1115 return;
1116 }
1117
1118 cumulative += self.hoist_prob;
1119 if r < cumulative {
1120 HoistMutation::new().mutate(genome, rng);
1121 return;
1122 }
1123
1124 ShrinkMutation::new().mutate(genome, rng);
1125 }
1126}
1127
1128#[cfg(test)]
1129mod tests {
1130 use super::*;
1131 use approx::assert_relative_eq;
1132
1133 #[test]
1134 fn test_polynomial_mutation_respects_bounds() {
1135 let mut rng = rand::thread_rng();
1136 let bounds = MultiBounds::symmetric(5.0, 10);
1137
1138 for _ in 0..100 {
1139 let mut genome = RealVector::generate(&mut rng, &bounds);
1140 let mutation = PolynomialMutation::new(20.0).with_probability(1.0);
1141 mutation.mutate_bounded(&mut genome, &bounds, &mut rng);
1142
1143 for (i, &gene) in genome.genes().iter().enumerate() {
1144 let bound = bounds.get(i).unwrap();
1145 assert!(
1146 gene >= bound.min && gene <= bound.max,
1147 "Gene {} out of bounds: {} not in [{}, {}]",
1148 i,
1149 gene,
1150 bound.min,
1151 bound.max
1152 );
1153 }
1154 }
1155 }
1156
1157 #[test]
1158 fn test_polynomial_mutation_changes_genome() {
1159 let mut rng = rand::thread_rng();
1160 let bounds = MultiBounds::symmetric(5.0, 10);
1161 let original = RealVector::zeros(10);
1162 let mut genome = original.clone();
1163
1164 let mutation = PolynomialMutation::new(20.0).with_probability(1.0);
1165 mutation.mutate_bounded(&mut genome, &bounds, &mut rng);
1166
1167 let changed = genome
1169 .genes()
1170 .iter()
1171 .zip(original.genes())
1172 .filter(|(&a, &b)| a != b)
1173 .count();
1174 assert!(changed > 0, "No genes were mutated");
1175 }
1176
1177 #[test]
1178 fn test_polynomial_mutation_eta_effect() {
1179 let mut rng = rand::thread_rng();
1180 let bounds = MultiBounds::symmetric(1.0, 1);
1181
1182 let low_eta = PolynomialMutation::new(1.0).with_probability(1.0);
1184 let high_eta = PolynomialMutation::new(100.0).with_probability(1.0);
1186
1187 let mut low_total_change = 0.0;
1188 let mut high_total_change = 0.0;
1189 let trials = 1000;
1190
1191 for _ in 0..trials {
1192 let mut genome_low = RealVector::new(vec![0.0]);
1193 let mut genome_high = RealVector::new(vec![0.0]);
1194
1195 low_eta.mutate_bounded(&mut genome_low, &bounds, &mut rng);
1196 high_eta.mutate_bounded(&mut genome_high, &bounds, &mut rng);
1197
1198 low_total_change += genome_low[0].abs();
1199 high_total_change += genome_high[0].abs();
1200 }
1201
1202 assert!(
1203 low_total_change > high_total_change,
1204 "Low eta should produce larger average changes"
1205 );
1206 }
1207
1208 #[test]
1209 fn test_gaussian_mutation_changes_genome() {
1210 let mut rng = rand::thread_rng();
1211 let original = RealVector::zeros(10);
1212 let mut genome = original.clone();
1213
1214 let mutation = GaussianMutation::new(0.1).with_probability(1.0);
1215 mutation.mutate(&mut genome, &mut rng);
1216
1217 assert_ne!(genome, original);
1218 }
1219
1220 #[test]
1221 fn test_gaussian_mutation_bounded() {
1222 let mut rng = rand::thread_rng();
1223 let bounds = MultiBounds::symmetric(1.0, 10);
1224
1225 for _ in 0..100 {
1226 let mut genome = RealVector::zeros(10);
1227 let mutation = GaussianMutation::new(10.0).with_probability(1.0);
1228 mutation.mutate_bounded(&mut genome, &bounds, &mut rng);
1229
1230 for (i, &gene) in genome.genes().iter().enumerate() {
1231 let bound = bounds.get(i).unwrap();
1232 assert!(gene >= bound.min && gene <= bound.max);
1233 }
1234 }
1235 }
1236
1237 #[test]
1238 fn test_uniform_mutation() {
1239 let mut rng = rand::thread_rng();
1240 let bounds = MultiBounds::symmetric(1.0, 10);
1241
1242 for _ in 0..100 {
1243 let mut genome = RealVector::zeros(10);
1244 let mutation = UniformMutation::new().with_probability(1.0);
1245 mutation.mutate_bounded(&mut genome, &bounds, &mut rng);
1246
1247 for (i, &gene) in genome.genes().iter().enumerate() {
1248 let bound = bounds.get(i).unwrap();
1249 assert!(gene >= bound.min && gene <= bound.max);
1250 }
1251 }
1252 }
1253
1254 #[test]
1255 fn test_bit_flip_mutation() {
1256 let mut rng = rand::thread_rng();
1257 let original = BitString::zeros(100);
1258 let mut genome = original.clone();
1259
1260 let mutation = BitFlipMutation::new().with_probability(0.5);
1261 mutation.mutate(&mut genome, &mut rng);
1262
1263 let flipped = genome.count_ones();
1265 assert!(
1266 flipped > 20 && flipped < 80,
1267 "Expected ~50 flips, got {}",
1268 flipped
1269 );
1270 }
1271
1272 #[test]
1273 fn test_bit_flip_mutation_default_probability() {
1274 let mut rng = rand::thread_rng();
1275 let original = BitString::zeros(100);
1276 let mut genome = original.clone();
1277
1278 let mutation = BitFlipMutation::new(); mutation.mutate(&mut genome, &mut rng);
1280
1281 let mut total_flips = 0;
1285 for _ in 0..100 {
1286 let mut g = BitString::zeros(100);
1287 mutation.mutate(&mut g, &mut rng);
1288 total_flips += g.count_ones();
1289 }
1290
1291 let avg = total_flips as f64 / 100.0;
1293 assert!(avg > 0.5 && avg < 2.0, "Expected avg ~1, got {}", avg);
1294 }
1295
1296 #[test]
1297 fn test_swap_mutation() {
1298 let mut rng = rand::thread_rng();
1299 let mut genome = RealVector::new(vec![0.0, 1.0, 2.0, 3.0, 4.0]);
1300
1301 let mutation = SwapMutation::new();
1302 mutation.mutate(&mut genome, &mut rng);
1303
1304 let sum: f64 = genome.genes().iter().sum();
1306 assert_relative_eq!(sum, 10.0);
1307 }
1308
1309 #[test]
1310 fn test_swap_mutation_multiple() {
1311 let mut rng = rand::thread_rng();
1312 let original: Vec<f64> = (0..10).map(|i| i as f64).collect();
1313 let mut genome = RealVector::new(original.clone());
1314
1315 let mutation = SwapMutation::with_swaps(5);
1316 mutation.mutate(&mut genome, &mut rng);
1317
1318 let sum: f64 = genome.genes().iter().sum();
1320 assert_relative_eq!(sum, 45.0);
1321 }
1322
1323 #[test]
1324 fn test_scramble_mutation() {
1325 let mut rng = rand::thread_rng();
1326 let original: Vec<f64> = (0..10).map(|i| i as f64).collect();
1327 let mut genome = RealVector::new(original.clone());
1328
1329 let mutation = ScrambleMutation::new();
1330 mutation.mutate(&mut genome, &mut rng);
1331
1332 let sum: f64 = genome.genes().iter().sum();
1334 assert_relative_eq!(sum, 45.0);
1335
1336 let mut sorted: Vec<f64> = genome.genes().to_vec();
1338 sorted.sort_by(|a, b| a.partial_cmp(b).unwrap());
1339 assert_eq!(sorted, original);
1340 }
1341
1342 #[test]
1343 fn test_scramble_mutation_bitstring() {
1344 let mut rng = rand::thread_rng();
1345 let original = BitString::new(vec![true, true, true, false, false, false, true, false]);
1346 let mut genome = original.clone();
1347
1348 let mutation = ScrambleMutation::new();
1349 mutation.mutate(&mut genome, &mut rng);
1350
1351 assert_eq!(genome.count_ones(), original.count_ones());
1353 }
1354
1355 #[test]
1360 fn test_permutation_swap_mutation() {
1361 let mut rng = rand::thread_rng();
1362 let original = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
1363 let mut genome = original.clone();
1364
1365 let mutation = PermutationSwapMutation::new();
1366 mutation.mutate(&mut genome, &mut rng);
1367
1368 assert!(genome.is_valid_permutation());
1370 assert_eq!(genome.dimension(), 8);
1371 }
1372
1373 #[test]
1374 fn test_permutation_swap_mutation_multiple() {
1375 let mut rng = rand::thread_rng();
1376 let original = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
1377 let mut genome = original.clone();
1378
1379 let mutation = PermutationSwapMutation::with_swaps(5);
1380 mutation.mutate(&mut genome, &mut rng);
1381
1382 assert!(genome.is_valid_permutation());
1384 assert_eq!(genome.dimension(), 10);
1385 }
1386
1387 #[test]
1388 fn test_insert_mutation() {
1389 let mut rng = rand::thread_rng();
1390
1391 for _ in 0..50 {
1392 let mut genome = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
1393
1394 let mutation = InsertMutation::new();
1395 mutation.mutate(&mut genome, &mut rng);
1396
1397 assert!(genome.is_valid_permutation());
1398 assert_eq!(genome.dimension(), 8);
1399 }
1400 }
1401
1402 #[test]
1403 fn test_inversion_mutation() {
1404 let mut rng = rand::thread_rng();
1405
1406 for _ in 0..50 {
1407 let mut genome = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
1408
1409 let mutation = InversionMutation::new();
1410 mutation.mutate(&mut genome, &mut rng);
1411
1412 assert!(genome.is_valid_permutation());
1413 assert_eq!(genome.dimension(), 8);
1414 }
1415 }
1416
1417 #[test]
1418 fn test_inversion_mutation_reverses_segment() {
1419 use rand::SeedableRng;
1420 let mut rng = rand::rngs::StdRng::seed_from_u64(42);
1421
1422 let mut genome = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
1423
1424 let mutation = InversionMutation::new();
1425 mutation.mutate(&mut genome, &mut rng);
1426
1427 assert!(genome.is_valid_permutation());
1429 }
1430
1431 #[test]
1432 fn test_permutation_scramble_mutation() {
1433 let mut rng = rand::thread_rng();
1434
1435 for _ in 0..50 {
1436 let mut genome = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
1437
1438 let mutation = PermutationScrambleMutation::new();
1439 mutation.mutate(&mut genome, &mut rng);
1440
1441 assert!(genome.is_valid_permutation());
1442 assert_eq!(genome.dimension(), 8);
1443 }
1444 }
1445
1446 #[test]
1447 fn test_displacement_mutation() {
1448 let mut rng = rand::thread_rng();
1449
1450 for _ in 0..50 {
1451 let mut genome = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
1452
1453 let mutation = DisplacementMutation::new();
1454 mutation.mutate(&mut genome, &mut rng);
1455
1456 assert!(genome.is_valid_permutation());
1457 assert_eq!(genome.dimension(), 10);
1458 }
1459 }
1460
1461 #[test]
1462 fn test_adaptive_permutation_mutation() {
1463 let mut rng = rand::thread_rng();
1464
1465 for _ in 0..100 {
1466 let mut genome = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
1467
1468 let mutation = AdaptivePermutationMutation::new();
1469 mutation.mutate(&mut genome, &mut rng);
1470
1471 assert!(genome.is_valid_permutation());
1472 assert_eq!(genome.dimension(), 8);
1473 }
1474 }
1475
1476 #[test]
1477 fn test_adaptive_permutation_mutation_custom_probs() {
1478 let mut rng = rand::thread_rng();
1479
1480 let mutation = AdaptivePermutationMutation::with_probs(0.0, 0.0, 1.0, 0.0);
1482
1483 for _ in 0..50 {
1484 let mut genome = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
1485 mutation.mutate(&mut genome, &mut rng);
1486
1487 assert!(genome.is_valid_permutation());
1488 }
1489 }
1490
1491 use crate::genome::tree::{ArithmeticFunction, ArithmeticTerminal};
1496
1497 fn create_test_tree() -> TreeGenome<ArithmeticTerminal, ArithmeticFunction> {
1498 let x0 = TreeNode::terminal(ArithmeticTerminal::Variable(0));
1500 let c1 = TreeNode::terminal(ArithmeticTerminal::Constant(1.0));
1501 let x1 = TreeNode::terminal(ArithmeticTerminal::Variable(1));
1502 let mul = TreeNode::function(ArithmeticFunction::Mul, vec![c1, x1]);
1503 let add = TreeNode::function(ArithmeticFunction::Add, vec![x0, mul]);
1504 TreeGenome::new(add, 5)
1505 }
1506
1507 #[test]
1508 fn test_point_mutation_preserves_structure() {
1509 let mut rng = rand::thread_rng();
1510 let original = create_test_tree();
1511 let original_size = original.size();
1512
1513 for _ in 0..50 {
1514 let mut genome = original.clone();
1515 let mutation = PointMutation::new().with_probability(1.0);
1516 mutation.mutate(&mut genome, &mut rng);
1517
1518 assert_eq!(genome.size(), original_size);
1520 assert!(genome.evaluate(&[1.0, 2.0]).is_finite());
1521 }
1522 }
1523
1524 #[test]
1525 fn test_point_mutation_changes_tree() {
1526 let mut rng = rand::thread_rng();
1527 let original = create_test_tree();
1528 let mut any_changed = false;
1529
1530 for _ in 0..100 {
1531 let mut genome = original.clone();
1532 let mutation = PointMutation::new().with_probability(1.0);
1533 mutation.mutate(&mut genome, &mut rng);
1534
1535 let orig_val = original.evaluate(&[1.0, 2.0]);
1537 let new_val = genome.evaluate(&[1.0, 2.0]);
1538 if (orig_val - new_val).abs() > 1e-10 {
1539 any_changed = true;
1540 break;
1541 }
1542 }
1543
1544 assert!(
1545 any_changed,
1546 "Point mutation should sometimes change the tree"
1547 );
1548 }
1549
1550 #[test]
1551 fn test_point_mutation_deep_tree_no_stack_overflow() {
1552 use rand::SeedableRng;
1556 let mut rng = rand::rngs::StdRng::seed_from_u64(7);
1557 let depth = 100_000usize;
1558 let mut root: TreeNode<ArithmeticTerminal, ArithmeticFunction> =
1559 TreeNode::terminal(ArithmeticTerminal::Constant(1.0));
1560 for _ in 0..depth {
1561 root = TreeNode::function(ArithmeticFunction::Neg, vec![root]);
1562 }
1563 let mut genome = TreeGenome::new(root, depth + 1);
1564 let size_before = genome.size();
1565
1566 PointMutation::new()
1568 .with_probability(1.0)
1569 .mutate(&mut genome, &mut rng);
1570
1571 assert_eq!(genome.size(), size_before);
1573 genome.dismantle();
1575 }
1576
1577 #[test]
1578 fn test_subtree_mutation() {
1579 use rand::SeedableRng;
1580 let mut rng = rand::rngs::StdRng::seed_from_u64(42);
1581 let original = create_test_tree();
1582
1583 for _ in 0..50 {
1584 let mut genome = original.clone();
1585 let mutation = SubtreeMutation::new().with_max_depth(3);
1586 mutation.mutate(&mut genome, &mut rng);
1587
1588 assert!(genome.size() >= 1);
1590 let result = genome.evaluate(&[1.0, 2.0]);
1593 assert!(result.is_nan() || result.is_finite());
1594 }
1595 }
1596
1597 #[test]
1598 fn test_hoist_mutation_reduces_tree() {
1599 let mut rng = rand::thread_rng();
1600
1601 let tree: TreeGenome<ArithmeticTerminal, ArithmeticFunction> =
1603 TreeGenome::generate_full(&mut rng, 4, 5);
1604
1605 let original_size = tree.size();
1606
1607 for _ in 0..50 {
1608 let mut genome = tree.clone();
1609 let mutation = HoistMutation::new();
1610 mutation.mutate(&mut genome, &mut rng);
1611
1612 assert!(genome.size() <= original_size);
1614 assert!(genome.evaluate(&[1.0, 2.0]).is_finite());
1615 }
1616 }
1617
1618 #[test]
1619 fn test_shrink_mutation_reduces_tree() {
1620 let mut rng = rand::thread_rng();
1621
1622 let tree: TreeGenome<ArithmeticTerminal, ArithmeticFunction> =
1624 TreeGenome::generate_full(&mut rng, 4, 5);
1625
1626 for _ in 0..50 {
1627 let mut genome = tree.clone();
1628 let original_size = genome.size();
1629 let mutation = ShrinkMutation::new();
1630 mutation.mutate(&mut genome, &mut rng);
1631
1632 assert!(genome.size() <= original_size);
1634 assert!(genome.evaluate(&[1.0, 2.0]).is_finite());
1635 }
1636 }
1637
1638 #[test]
1639 fn test_adaptive_tree_mutation() {
1640 let mut rng = rand::thread_rng();
1641
1642 for _ in 0..100 {
1643 let mut genome: TreeGenome<ArithmeticTerminal, ArithmeticFunction> =
1644 TreeGenome::generate_ramped_half_and_half(&mut rng, 2, 5);
1645
1646 let mutation = AdaptiveTreeMutation::new();
1647 mutation.mutate(&mut genome, &mut rng);
1648
1649 assert!(genome.size() >= 1);
1651 assert!(genome.evaluate(&[1.0, 2.0]).is_finite());
1652 }
1653 }
1654
1655 #[test]
1656 fn test_adaptive_tree_mutation_custom_probs() {
1657 let mut rng = rand::thread_rng();
1658
1659 let mutation = AdaptiveTreeMutation::with_probs(1.0, 0.0, 0.0, 0.0);
1661
1662 for _ in 0..50 {
1663 let mut genome: TreeGenome<ArithmeticTerminal, ArithmeticFunction> =
1664 TreeGenome::generate_ramped_half_and_half(&mut rng, 2, 5);
1665 let original_size = genome.size();
1666 mutation.mutate(&mut genome, &mut rng);
1667
1668 assert_eq!(genome.size(), original_size);
1670 }
1671 }
1672
1673 #[test]
1674 fn test_subtree_mutation_respects_max_depth() {
1675 use rand::SeedableRng;
1682 let mut rng = rand::rngs::StdRng::seed_from_u64(2024);
1683
1684 let max_depth = 5;
1685 let mut genome: TreeGenome<ArithmeticTerminal, ArithmeticFunction> =
1688 TreeGenome::generate_full(&mut rng, max_depth - 1, max_depth);
1689 assert!(genome.depth() <= max_depth);
1690
1691 let mutation = SubtreeMutation::new()
1694 .with_max_depth(12)
1695 .with_terminal_probability(0.1);
1696
1697 for i in 0..500 {
1698 mutation.mutate(&mut genome, &mut rng);
1699 assert!(
1700 genome.depth() <= max_depth,
1701 "iteration {i}: tree depth {} exceeded max_depth {max_depth}",
1702 genome.depth()
1703 );
1704 }
1705 }
1706
1707 #[test]
1708 fn test_default_swap_mutations_actually_mutate() {
1709 assert_eq!(SwapMutation::default().num_swaps, 1);
1714 assert_eq!(PermutationSwapMutation::default().num_swaps, 1);
1715 assert_eq!(InsertMutation::default().num_inserts, 1);
1716
1717 use rand::SeedableRng;
1718 let mut rng = rand::rngs::StdRng::seed_from_u64(99);
1719
1720 let mut any_changed = false;
1722 for _ in 0..50 {
1723 let original = RealVector::new(vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]);
1724 let mut genome = original.clone();
1725 SwapMutation::default().mutate(&mut genome, &mut rng);
1726 if genome.genes() != original.genes() {
1727 any_changed = true;
1728 break;
1729 }
1730 }
1731 assert!(
1732 any_changed,
1733 "Default SwapMutation never mutated (num_swaps == 0?)"
1734 );
1735
1736 let mut perm_changed = false;
1738 for _ in 0..50 {
1739 let original = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
1740 let mut genome = original.clone();
1741 PermutationSwapMutation::default().mutate(&mut genome, &mut rng);
1742 if genome.as_slice() != original.as_slice() {
1743 perm_changed = true;
1744 break;
1745 }
1746 }
1747 assert!(
1748 perm_changed,
1749 "Default PermutationSwapMutation never mutated (num_swaps == 0?)"
1750 );
1751
1752 let mut insert_changed = false;
1754 for _ in 0..50 {
1755 let original = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
1756 let mut genome = original.clone();
1757 InsertMutation::default().mutate(&mut genome, &mut rng);
1758 if genome.as_slice() != original.as_slice() {
1759 insert_changed = true;
1760 break;
1761 }
1762 }
1763 assert!(
1764 insert_changed,
1765 "Default InsertMutation never mutated (num_inserts == 0?)"
1766 );
1767 }
1768
1769 #[test]
1770 fn test_unbounded_polynomial_mutation_stays_local() {
1771 use rand::SeedableRng;
1776 let mut rng = rand::rngs::StdRng::seed_from_u64(1);
1777
1778 for _ in 0..200 {
1779 let mut genome = RealVector::new(vec![0.0, 1.0, -1.0, 2.5, -3.0]);
1780 let mutation = PolynomialMutation::new(20.0).with_probability(1.0);
1781 mutation.mutate(&mut genome, &mut rng); for &g in genome.genes() {
1784 assert!(
1785 g.abs() < 100.0,
1786 "unbounded polynomial mutation produced a destructive value: {g}"
1787 );
1788 }
1789 }
1790
1791 let mut genome = RealVector::new(vec![0.0; 1000]);
1793 let mutation = PolynomialMutation::new(20.0)
1794 .with_probability(1.0)
1795 .with_unbounded_sigma(0.05);
1796 mutation.mutate(&mut genome, &mut rng);
1797 let variance: f64 =
1798 genome.genes().iter().map(|g| g * g).sum::<f64>() / genome.dimension() as f64;
1799 assert!(
1801 variance.sqrt() < 0.2,
1802 "fixed sigma not honored: std {}",
1803 variance.sqrt()
1804 );
1805 }
1806
1807 #[test]
1808 fn test_mutation_probability_reports_effective_rate() {
1809 assert_eq!(
1814 MutationOperator::<RealVector>::mutation_probability(&PolynomialMutation::new(20.0)),
1815 None
1816 );
1817 assert_eq!(
1818 MutationOperator::<RealVector>::mutation_probability(
1819 &PolynomialMutation::new(20.0).with_probability(0.25)
1820 ),
1821 Some(0.25)
1822 );
1823 assert_eq!(
1824 MutationOperator::<RealVector>::mutation_probability(&GaussianMutation::new(0.1)),
1825 None
1826 );
1827 assert_eq!(
1828 MutationOperator::<RealVector>::mutation_probability(&UniformMutation::new()),
1829 None
1830 );
1831 assert_eq!(
1832 MutationOperator::<BitString>::mutation_probability(&BitFlipMutation::new()),
1833 None
1834 );
1835 assert_eq!(
1836 MutationOperator::<BitString>::mutation_probability(
1837 &BitFlipMutation::new().with_probability(0.5)
1838 ),
1839 Some(0.5)
1840 );
1841 }
1842}