1use std::collections::{HashMap, HashSet};
6
7use rand::Rng;
8
9use crate::error::{OperatorError, OperatorResult};
10use crate::genome::bit_string::BitString;
11use crate::genome::bounds::MultiBounds;
12use crate::genome::permutation::Permutation;
13use crate::genome::real_vector::RealVector;
14use crate::genome::traits::{
15 BinaryGenome, EvolutionaryGenome, PermutationGenome, RealValuedGenome,
16};
17use crate::operators::traits::{BoundedCrossoverOperator, CrossoverOperator};
18
19#[derive(Clone, Debug)]
53pub struct SbxCrossover {
54 pub eta: f64,
57 pub crossover_probability: f64,
60 pub exchange_probability: f64,
64}
65
66impl SbxCrossover {
67 pub fn new(eta: f64) -> Self {
69 assert!(eta >= 0.0, "Distribution index must be non-negative");
70 Self {
71 eta,
72 crossover_probability: 0.9,
73 exchange_probability: 0.5,
74 }
75 }
76
77 pub fn with_probability(mut self, probability: f64) -> Self {
84 assert!(
85 (0.0..=1.0).contains(&probability),
86 "Probability must be in [0, 1]"
87 );
88 self.crossover_probability = probability;
89 self
90 }
91
92 pub fn with_exchange_probability(mut self, probability: f64) -> Self {
94 assert!(
95 (0.0..=1.0).contains(&probability),
96 "Probability must be in [0, 1]"
97 );
98 self.exchange_probability = probability;
99 self
100 }
101
102 fn spread_factor(&self, u: f64) -> f64 {
104 if u <= 0.5 {
105 (2.0 * u).powf(1.0 / (self.eta + 1.0))
106 } else {
107 (1.0 / (2.0 * (1.0 - u))).powf(1.0 / (self.eta + 1.0))
108 }
109 }
110
111 fn sbx_bounded_pair(&self, y1: f64, y2: f64, yl: f64, yu: f64, u: f64) -> (f64, f64) {
120 let dy = y2 - y1;
121 let exp = self.eta + 1.0;
122 let power = 1.0 / exp;
123
124 let beta_l = 1.0 + 2.0 * (y1 - yl) / dy;
126 let alpha_l = 2.0 - beta_l.powf(-exp);
127 let betaq_l = if u <= 1.0 / alpha_l {
128 (u * alpha_l).powf(power)
129 } else {
130 (1.0 / (2.0 - u * alpha_l)).powf(power)
131 };
132 let c_low = 0.5 * ((y1 + y2) - betaq_l * dy);
133
134 let beta_u = 1.0 + 2.0 * (yu - y2) / dy;
136 let alpha_u = 2.0 - beta_u.powf(-exp);
137 let betaq_u = if u <= 1.0 / alpha_u {
138 (u * alpha_u).powf(power)
139 } else {
140 (1.0 / (2.0 - u * alpha_u)).powf(power)
141 };
142 let c_high = 0.5 * ((y1 + y2) + betaq_u * dy);
143
144 (c_low.clamp(yl, yu), c_high.clamp(yl, yu))
148 }
149
150 fn apply_sbx<R: Rng>(
152 &self,
153 parent1: &[f64],
154 parent2: &[f64],
155 bounds: Option<&MultiBounds>,
156 rng: &mut R,
157 ) -> (Vec<f64>, Vec<f64>) {
158 let mut child1: Vec<f64> = parent1.to_vec();
159 let mut child2: Vec<f64> = parent2.to_vec();
160
161 for i in 0..parent1.len() {
162 if rng.gen::<f64>() < self.exchange_probability {
165 let x1 = parent1[i];
166 let x2 = parent2[i];
167
168 if (x1 - x2).abs() > 1e-14 {
170 let u = rng.gen::<f64>();
171
172 let (c_low, c_high) = match bounds.and_then(|b| b.get(i)) {
173 Some(bound) => {
174 let (y1, y2) = if x1 <= x2 { (x1, x2) } else { (x2, x1) };
176 self.sbx_bounded_pair(y1, y2, bound.min, bound.max, u)
177 }
178 None => {
179 let beta = self.spread_factor(u);
181 let y1 = 0.5 * ((1.0 + beta) * x1 + (1.0 - beta) * x2);
182 let y2 = 0.5 * ((1.0 - beta) * x1 + (1.0 + beta) * x2);
183 (y1, y2)
184 }
185 };
186
187 if rng.gen::<bool>() {
190 child1[i] = c_low;
191 child2[i] = c_high;
192 } else {
193 child1[i] = c_high;
194 child2[i] = c_low;
195 }
196 }
197 }
198 }
199
200 (child1, child2)
201 }
202}
203
204impl CrossoverOperator<RealVector> for SbxCrossover {
205 fn crossover<R: Rng>(
206 &self,
207 parent1: &RealVector,
208 parent2: &RealVector,
209 rng: &mut R,
210 ) -> OperatorResult<(RealVector, RealVector)> {
211 if parent1.dimension() != parent2.dimension() {
212 return OperatorResult::Failed(OperatorError::CrossoverFailed(
213 "Parent dimensions do not match".to_string(),
214 ));
215 }
216
217 let (child1_genes, child2_genes) =
218 self.apply_sbx(parent1.genes(), parent2.genes(), None, rng);
219
220 let child1 = RealVector::from_genes(child1_genes).unwrap();
221 let child2 = RealVector::from_genes(child2_genes).unwrap();
222
223 OperatorResult::Success((child1, child2))
224 }
225
226 fn crossover_probability(&self) -> f64 {
227 self.crossover_probability
228 }
229}
230
231impl BoundedCrossoverOperator<RealVector> for SbxCrossover {
232 fn crossover_bounded<R: Rng>(
233 &self,
234 parent1: &RealVector,
235 parent2: &RealVector,
236 bounds: &MultiBounds,
237 rng: &mut R,
238 ) -> OperatorResult<(RealVector, RealVector)> {
239 if parent1.dimension() != parent2.dimension() {
240 return OperatorResult::Failed(OperatorError::CrossoverFailed(
241 "Parent dimensions do not match".to_string(),
242 ));
243 }
244
245 let (child1_genes, child2_genes) =
246 self.apply_sbx(parent1.genes(), parent2.genes(), Some(bounds), rng);
247
248 let child1 = RealVector::from_genes(child1_genes).unwrap();
249 let child2 = RealVector::from_genes(child2_genes).unwrap();
250
251 OperatorResult::Success((child1, child2))
252 }
253}
254
255#[derive(Clone, Debug)]
259pub struct BlxAlphaCrossover {
260 pub alpha: f64,
262}
263
264impl BlxAlphaCrossover {
265 pub fn new(alpha: f64) -> Self {
267 assert!(alpha >= 0.0, "Alpha must be non-negative");
268 Self { alpha }
269 }
270
271 pub fn default_alpha() -> Self {
273 Self::new(0.5)
274 }
275}
276
277impl CrossoverOperator<RealVector> for BlxAlphaCrossover {
278 fn crossover<R: Rng>(
279 &self,
280 parent1: &RealVector,
281 parent2: &RealVector,
282 rng: &mut R,
283 ) -> OperatorResult<(RealVector, RealVector)> {
284 if parent1.dimension() != parent2.dimension() {
285 return OperatorResult::Failed(OperatorError::CrossoverFailed(
286 "Parent dimensions do not match".to_string(),
287 ));
288 }
289
290 let mut child1_genes = Vec::with_capacity(parent1.dimension());
291 let mut child2_genes = Vec::with_capacity(parent2.dimension());
292
293 for i in 0..parent1.dimension() {
294 let x1 = parent1[i];
295 let x2 = parent2[i];
296
297 let min_val = x1.min(x2);
298 let max_val = x1.max(x2);
299 let range = max_val - min_val;
300
301 let low = min_val - self.alpha * range;
302 let high = max_val + self.alpha * range;
303
304 child1_genes.push(rng.gen_range(low..=high));
305 child2_genes.push(rng.gen_range(low..=high));
306 }
307
308 let child1 = RealVector::from_genes(child1_genes).unwrap();
309 let child2 = RealVector::from_genes(child2_genes).unwrap();
310
311 OperatorResult::Success((child1, child2))
312 }
313}
314
315#[derive(Clone, Debug)]
319pub struct UniformCrossover {
320 pub bias: f64,
322}
323
324impl UniformCrossover {
325 pub fn new() -> Self {
327 Self { bias: 0.5 }
328 }
329
330 pub fn with_bias(bias: f64) -> Self {
332 assert!((0.0..=1.0).contains(&bias), "Bias must be in [0, 1]");
333 Self { bias }
334 }
335}
336
337impl Default for UniformCrossover {
338 fn default() -> Self {
339 Self::new()
340 }
341}
342
343impl CrossoverOperator<BitString> for UniformCrossover {
344 fn crossover<R: Rng>(
345 &self,
346 parent1: &BitString,
347 parent2: &BitString,
348 rng: &mut R,
349 ) -> OperatorResult<(BitString, BitString)> {
350 if parent1.dimension() != parent2.dimension() {
351 return OperatorResult::Failed(OperatorError::CrossoverFailed(
352 "Parent dimensions do not match".to_string(),
353 ));
354 }
355
356 let mut child1_bits = Vec::with_capacity(parent1.dimension());
357 let mut child2_bits = Vec::with_capacity(parent2.dimension());
358
359 for i in 0..parent1.dimension() {
360 if rng.gen::<f64>() < self.bias {
361 child1_bits.push(parent1[i]);
362 child2_bits.push(parent2[i]);
363 } else {
364 child1_bits.push(parent2[i]);
365 child2_bits.push(parent1[i]);
366 }
367 }
368
369 let child1 = BitString::from_bits(child1_bits).unwrap();
370 let child2 = BitString::from_bits(child2_bits).unwrap();
371
372 OperatorResult::Success((child1, child2))
373 }
374}
375
376#[derive(Clone, Debug, Default)]
378pub struct OnePointCrossover;
379
380impl OnePointCrossover {
381 pub fn new() -> Self {
383 Self
384 }
385}
386
387impl CrossoverOperator<BitString> for OnePointCrossover {
388 fn crossover<R: Rng>(
389 &self,
390 parent1: &BitString,
391 parent2: &BitString,
392 rng: &mut R,
393 ) -> OperatorResult<(BitString, BitString)> {
394 if parent1.dimension() != parent2.dimension() {
395 return OperatorResult::Failed(OperatorError::CrossoverFailed(
396 "Parent dimensions do not match".to_string(),
397 ));
398 }
399
400 let n = parent1.dimension();
401 if n == 0 {
402 return OperatorResult::Success((parent1.clone(), parent2.clone()));
403 }
404
405 let crossover_point = rng.gen_range(0..n);
406
407 let mut child1_bits = Vec::with_capacity(n);
408 let mut child2_bits = Vec::with_capacity(n);
409
410 for i in 0..n {
411 if i < crossover_point {
412 child1_bits.push(parent1[i]);
413 child2_bits.push(parent2[i]);
414 } else {
415 child1_bits.push(parent2[i]);
416 child2_bits.push(parent1[i]);
417 }
418 }
419
420 let child1 = BitString::from_bits(child1_bits).unwrap();
421 let child2 = BitString::from_bits(child2_bits).unwrap();
422
423 OperatorResult::Success((child1, child2))
424 }
425}
426
427#[derive(Clone, Debug, Default)]
429pub struct TwoPointCrossover;
430
431impl TwoPointCrossover {
432 pub fn new() -> Self {
434 Self
435 }
436}
437
438impl CrossoverOperator<BitString> for TwoPointCrossover {
439 fn crossover<R: Rng>(
440 &self,
441 parent1: &BitString,
442 parent2: &BitString,
443 rng: &mut R,
444 ) -> OperatorResult<(BitString, BitString)> {
445 if parent1.dimension() != parent2.dimension() {
446 return OperatorResult::Failed(OperatorError::CrossoverFailed(
447 "Parent dimensions do not match".to_string(),
448 ));
449 }
450
451 let n = parent1.dimension();
452 if n < 2 {
453 return OperatorResult::Success((parent1.clone(), parent2.clone()));
454 }
455
456 let mut point1 = rng.gen_range(0..n);
457 let mut point2 = rng.gen_range(0..n);
458 if point1 > point2 {
459 std::mem::swap(&mut point1, &mut point2);
460 }
461
462 let mut child1_bits = Vec::with_capacity(n);
463 let mut child2_bits = Vec::with_capacity(n);
464
465 for i in 0..n {
466 if i < point1 || i >= point2 {
467 child1_bits.push(parent1[i]);
468 child2_bits.push(parent2[i]);
469 } else {
470 child1_bits.push(parent2[i]);
471 child2_bits.push(parent1[i]);
472 }
473 }
474
475 let child1 = BitString::from_bits(child1_bits).unwrap();
476 let child2 = BitString::from_bits(child2_bits).unwrap();
477
478 OperatorResult::Success((child1, child2))
479 }
480}
481
482#[derive(Clone, Debug)]
486pub struct ArithmeticCrossover {
487 pub weight: f64,
489}
490
491impl ArithmeticCrossover {
492 pub fn new(weight: f64) -> Self {
494 assert!((0.0..=1.0).contains(&weight), "Weight must be in [0, 1]");
495 Self { weight }
496 }
497
498 pub fn uniform() -> Self {
500 Self::new(0.5)
501 }
502}
503
504impl CrossoverOperator<RealVector> for ArithmeticCrossover {
505 fn crossover<R: Rng>(
506 &self,
507 parent1: &RealVector,
508 parent2: &RealVector,
509 _rng: &mut R,
510 ) -> OperatorResult<(RealVector, RealVector)> {
511 if parent1.dimension() != parent2.dimension() {
512 return OperatorResult::Failed(OperatorError::CrossoverFailed(
513 "Parent dimensions do not match".to_string(),
514 ));
515 }
516
517 let w = self.weight;
518 let mut child1_genes = Vec::with_capacity(parent1.dimension());
519 let mut child2_genes = Vec::with_capacity(parent2.dimension());
520
521 for i in 0..parent1.dimension() {
522 child1_genes.push(w * parent1[i] + (1.0 - w) * parent2[i]);
523 child2_genes.push((1.0 - w) * parent1[i] + w * parent2[i]);
524 }
525
526 let child1 = RealVector::from_genes(child1_genes).unwrap();
527 let child2 = RealVector::from_genes(child2_genes).unwrap();
528
529 OperatorResult::Success((child1, child2))
530 }
531}
532
533#[derive(Clone, Debug, Default)]
546pub struct PmxCrossover;
547
548impl PmxCrossover {
549 pub fn new() -> Self {
551 Self
552 }
553}
554
555impl CrossoverOperator<Permutation> for PmxCrossover {
556 fn crossover<R: Rng>(
557 &self,
558 parent1: &Permutation,
559 parent2: &Permutation,
560 rng: &mut R,
561 ) -> OperatorResult<(Permutation, Permutation)> {
562 let n = parent1.dimension();
563
564 if n != parent2.dimension() {
565 return OperatorResult::Failed(OperatorError::CrossoverFailed(
566 "Parent dimensions do not match".to_string(),
567 ));
568 }
569
570 if n < 2 {
571 return OperatorResult::Success((parent1.clone(), parent2.clone()));
572 }
573
574 let mut start = rng.gen_range(0..n);
576 let mut end = rng.gen_range(0..n);
577 if start > end {
578 std::mem::swap(&mut start, &mut end);
579 }
580
581 let p1 = parent1.permutation();
582 let p2 = parent2.permutation();
583
584 let mut child1 = vec![usize::MAX; n];
586 let mut child2 = vec![usize::MAX; n];
587
588 for i in start..=end {
590 child1[i] = p2[i];
591 child2[i] = p1[i];
592 }
593
594 let mut map1: HashMap<usize, usize> = HashMap::new();
596 let mut map2: HashMap<usize, usize> = HashMap::new();
597 for i in start..=end {
598 map1.insert(p2[i], p1[i]);
599 map2.insert(p1[i], p2[i]);
600 }
601
602 for i in (0..start).chain((end + 1)..n) {
604 let mut val1 = p1[i];
606 while child1[start..=end].contains(&val1) {
607 val1 = *map1.get(&val1).unwrap_or(&val1);
608 }
609 child1[i] = val1;
610
611 let mut val2 = p2[i];
613 while child2[start..=end].contains(&val2) {
614 val2 = *map2.get(&val2).unwrap_or(&val2);
615 }
616 child2[i] = val2;
617 }
618
619 let c1 = match Permutation::try_new(child1) {
620 Ok(p) => p,
621 Err(e) => {
622 return OperatorResult::Failed(OperatorError::CrossoverFailed(format!(
623 "PMX produced invalid child1: {}",
624 e
625 )))
626 }
627 };
628 let c2 = match Permutation::try_new(child2) {
629 Ok(p) => p,
630 Err(e) => {
631 return OperatorResult::Failed(OperatorError::CrossoverFailed(format!(
632 "PMX produced invalid child2: {}",
633 e
634 )))
635 }
636 };
637
638 OperatorResult::Success((c1, c2))
639 }
640}
641
642#[derive(Clone, Debug, Default)]
650pub struct OxCrossover;
651
652impl OxCrossover {
653 pub fn new() -> Self {
655 Self
656 }
657}
658
659impl CrossoverOperator<Permutation> for OxCrossover {
660 fn crossover<R: Rng>(
661 &self,
662 parent1: &Permutation,
663 parent2: &Permutation,
664 rng: &mut R,
665 ) -> OperatorResult<(Permutation, Permutation)> {
666 let n = parent1.dimension();
667
668 if n != parent2.dimension() {
669 return OperatorResult::Failed(OperatorError::CrossoverFailed(
670 "Parent dimensions do not match".to_string(),
671 ));
672 }
673
674 if n < 2 {
675 return OperatorResult::Success((parent1.clone(), parent2.clone()));
676 }
677
678 let mut start = rng.gen_range(0..n);
680 let mut end = rng.gen_range(0..n);
681 if start > end {
682 std::mem::swap(&mut start, &mut end);
683 }
684
685 let child1 = Self::ox_single(parent1, parent2, start, end);
686 let child2 = Self::ox_single(parent2, parent1, start, end);
687
688 let c1 = match Permutation::try_new(child1) {
689 Ok(p) => p,
690 Err(e) => {
691 return OperatorResult::Failed(OperatorError::CrossoverFailed(format!(
692 "OX produced invalid child1: {}",
693 e
694 )))
695 }
696 };
697 let c2 = match Permutation::try_new(child2) {
698 Ok(p) => p,
699 Err(e) => {
700 return OperatorResult::Failed(OperatorError::CrossoverFailed(format!(
701 "OX produced invalid child2: {}",
702 e
703 )))
704 }
705 };
706
707 OperatorResult::Success((c1, c2))
708 }
709}
710
711impl OxCrossover {
712 fn ox_single(
714 parent1: &Permutation,
715 parent2: &Permutation,
716 start: usize,
717 end: usize,
718 ) -> Vec<usize> {
719 let n = parent1.dimension();
720 let p1 = parent1.permutation();
721 let p2 = parent2.permutation();
722
723 let mut child = vec![usize::MAX; n];
724
725 let segment: HashSet<usize> = p1[start..=end].iter().copied().collect();
727 for i in start..=end {
728 child[i] = p1[i];
729 }
730
731 let mut pos = (end + 1) % n;
733 let mut p2_idx = (end + 1) % n;
734
735 while pos != start {
736 while segment.contains(&p2[p2_idx]) {
738 p2_idx = (p2_idx + 1) % n;
739 }
740
741 child[pos] = p2[p2_idx];
742 pos = (pos + 1) % n;
743 p2_idx = (p2_idx + 1) % n;
744 }
745
746 child
747 }
748}
749
750#[derive(Clone, Debug, Default)]
759pub struct CxCrossover;
760
761impl CxCrossover {
762 pub fn new() -> Self {
764 Self
765 }
766}
767
768impl CrossoverOperator<Permutation> for CxCrossover {
769 fn crossover<R: Rng>(
770 &self,
771 parent1: &Permutation,
772 parent2: &Permutation,
773 _rng: &mut R,
774 ) -> OperatorResult<(Permutation, Permutation)> {
775 let n = parent1.dimension();
776
777 if n != parent2.dimension() {
778 return OperatorResult::Failed(OperatorError::CrossoverFailed(
779 "Parent dimensions do not match".to_string(),
780 ));
781 }
782
783 if n == 0 {
784 return OperatorResult::Success((parent1.clone(), parent2.clone()));
785 }
786
787 let p1 = parent1.permutation();
788 let p2 = parent2.permutation();
789
790 let mut pos_in_p1: HashMap<usize, usize> = HashMap::new();
792 for (i, &val) in p1.iter().enumerate() {
793 pos_in_p1.insert(val, i);
794 }
795
796 let mut child1 = vec![usize::MAX; n];
798 let mut child2 = vec![usize::MAX; n];
799 let mut visited = vec![false; n];
800 let mut use_p1 = true; for start in 0..n {
803 if visited[start] {
804 continue;
805 }
806
807 let mut cycle_positions = Vec::new();
809 let mut pos = start;
810
811 loop {
812 cycle_positions.push(pos);
813 visited[pos] = true;
814
815 let val_in_p2 = p2[pos];
817 pos = *pos_in_p1.get(&val_in_p2).unwrap();
818
819 if pos == start {
820 break;
821 }
822 }
823
824 for &cycle_pos in &cycle_positions {
826 if use_p1 {
827 child1[cycle_pos] = p1[cycle_pos];
828 child2[cycle_pos] = p2[cycle_pos];
829 } else {
830 child1[cycle_pos] = p2[cycle_pos];
831 child2[cycle_pos] = p1[cycle_pos];
832 }
833 }
834
835 use_p1 = !use_p1; }
837
838 let c1 = match Permutation::try_new(child1) {
839 Ok(p) => p,
840 Err(e) => {
841 return OperatorResult::Failed(OperatorError::CrossoverFailed(format!(
842 "CX produced invalid child1: {}",
843 e
844 )))
845 }
846 };
847 let c2 = match Permutation::try_new(child2) {
848 Ok(p) => p,
849 Err(e) => {
850 return OperatorResult::Failed(OperatorError::CrossoverFailed(format!(
851 "CX produced invalid child2: {}",
852 e
853 )))
854 }
855 };
856
857 OperatorResult::Success((c1, c2))
858 }
859}
860
861#[derive(Clone, Debug, Default)]
866pub struct EdgeRecombinationCrossover;
867
868impl EdgeRecombinationCrossover {
869 pub fn new() -> Self {
871 Self
872 }
873
874 fn build_edge_table(
876 parent1: &Permutation,
877 parent2: &Permutation,
878 ) -> HashMap<usize, HashSet<usize>> {
879 let n = parent1.dimension();
880 let p1 = parent1.permutation();
881 let p2 = parent2.permutation();
882
883 let mut edges: HashMap<usize, HashSet<usize>> = HashMap::new();
884
885 for i in 0..n {
887 edges.insert(i, HashSet::new());
888 }
889
890 for i in 0..n {
892 let curr = p1[i];
893 let prev = p1[(i + n - 1) % n];
894 let next = p1[(i + 1) % n];
895 edges.get_mut(&curr).unwrap().insert(prev);
896 edges.get_mut(&curr).unwrap().insert(next);
897 }
898
899 for i in 0..n {
901 let curr = p2[i];
902 let prev = p2[(i + n - 1) % n];
903 let next = p2[(i + 1) % n];
904 edges.get_mut(&curr).unwrap().insert(prev);
905 edges.get_mut(&curr).unwrap().insert(next);
906 }
907
908 edges
909 }
910}
911
912impl CrossoverOperator<Permutation> for EdgeRecombinationCrossover {
913 fn crossover<R: Rng>(
914 &self,
915 parent1: &Permutation,
916 parent2: &Permutation,
917 rng: &mut R,
918 ) -> OperatorResult<(Permutation, Permutation)> {
919 let n = parent1.dimension();
920
921 if n != parent2.dimension() {
922 return OperatorResult::Failed(OperatorError::CrossoverFailed(
923 "Parent dimensions do not match".to_string(),
924 ));
925 }
926
927 if n < 2 {
928 return OperatorResult::Success((parent1.clone(), parent2.clone()));
929 }
930
931 let mut edges = Self::build_edge_table(parent1, parent2);
933
934 let mut child = Vec::with_capacity(n);
936 let mut remaining: HashSet<usize> = (0..n).collect();
937
938 let mut current = parent1.permutation()[0];
940 child.push(current);
941 remaining.remove(¤t);
942
943 for edge_set in edges.values_mut() {
945 edge_set.remove(¤t);
946 }
947
948 while child.len() < n {
949 let neighbors = edges.get(¤t).cloned().unwrap_or_default();
951
952 let next = if !neighbors.is_empty() {
954 let filtered: Vec<usize> = neighbors
955 .iter()
956 .filter(|x| remaining.contains(x))
957 .copied()
958 .collect();
959 if filtered.is_empty() {
960 let remaining_vec: Vec<usize> = remaining.iter().copied().collect();
962 remaining_vec[rng.gen_range(0..remaining_vec.len())]
963 } else {
964 *filtered
966 .iter()
967 .min_by_key(|&&x| edges.get(&x).map(|s| s.len()).unwrap_or(0))
968 .unwrap()
969 }
970 } else {
971 let remaining_vec: Vec<usize> = remaining.iter().copied().collect();
973 remaining_vec[rng.gen_range(0..remaining_vec.len())]
974 };
975
976 child.push(next);
977 remaining.remove(&next);
978 current = next;
979
980 for edge_set in edges.values_mut() {
982 edge_set.remove(¤t);
983 }
984 }
985
986 let mut edges2 = Self::build_edge_table(parent1, parent2);
988 let mut child2 = Vec::with_capacity(n);
989 let mut remaining2: HashSet<usize> = (0..n).collect();
990
991 let mut current2 = parent2.permutation()[0];
993 child2.push(current2);
994 remaining2.remove(¤t2);
995
996 for edge_set in edges2.values_mut() {
997 edge_set.remove(¤t2);
998 }
999
1000 while child2.len() < n {
1001 let neighbors = edges2.get(¤t2).cloned().unwrap_or_default();
1002
1003 let next2 = if !neighbors.is_empty() {
1004 let filtered: Vec<usize> = neighbors
1005 .iter()
1006 .filter(|x| remaining2.contains(x))
1007 .copied()
1008 .collect();
1009 if filtered.is_empty() {
1010 let remaining_vec: Vec<usize> = remaining2.iter().copied().collect();
1011 remaining_vec[rng.gen_range(0..remaining_vec.len())]
1012 } else {
1013 *filtered
1014 .iter()
1015 .min_by_key(|&&x| edges2.get(&x).map(|s| s.len()).unwrap_or(0))
1016 .unwrap()
1017 }
1018 } else {
1019 let remaining_vec: Vec<usize> = remaining2.iter().copied().collect();
1020 remaining_vec[rng.gen_range(0..remaining_vec.len())]
1021 };
1022
1023 child2.push(next2);
1024 remaining2.remove(&next2);
1025 current2 = next2;
1026
1027 for edge_set in edges2.values_mut() {
1028 edge_set.remove(¤t2);
1029 }
1030 }
1031
1032 let c1 = match Permutation::try_new(child) {
1033 Ok(p) => p,
1034 Err(e) => {
1035 return OperatorResult::Failed(OperatorError::CrossoverFailed(format!(
1036 "ERX produced invalid child1: {}",
1037 e
1038 )))
1039 }
1040 };
1041 let c2 = match Permutation::try_new(child2) {
1042 Ok(p) => p,
1043 Err(e) => {
1044 return OperatorResult::Failed(OperatorError::CrossoverFailed(format!(
1045 "ERX produced invalid child2: {}",
1046 e
1047 )))
1048 }
1049 };
1050
1051 OperatorResult::Success((c1, c2))
1052 }
1053}
1054
1055use crate::genome::tree::{Function, Terminal, TreeGenome, TreeNode};
1060
1061#[derive(Clone, Debug)]
1066pub struct SubtreeCrossover {
1067 pub max_depth: Option<usize>,
1069 pub function_probability: f64,
1071}
1072
1073impl Default for SubtreeCrossover {
1074 fn default() -> Self {
1075 Self {
1076 max_depth: Some(17), function_probability: 0.9, }
1079 }
1080}
1081
1082impl SubtreeCrossover {
1083 pub fn new() -> Self {
1085 Self::default()
1086 }
1087
1088 pub fn with_max_depth(mut self, max_depth: usize) -> Self {
1090 self.max_depth = Some(max_depth);
1091 self
1092 }
1093
1094 pub fn without_depth_limit(mut self) -> Self {
1096 self.max_depth = None;
1097 self
1098 }
1099
1100 pub fn with_function_probability(mut self, prob: f64) -> Self {
1102 self.function_probability = prob.clamp(0.0, 1.0);
1103 self
1104 }
1105
1106 fn select_crossover_point<T: Terminal, F: Function, R: Rng>(
1108 &self,
1109 tree: &TreeNode<T, F>,
1110 rng: &mut R,
1111 ) -> Vec<usize> {
1112 let select_function = rng.gen::<f64>() < self.function_probability;
1114
1115 let positions = if select_function {
1116 let func_pos = tree.function_positions();
1117 if func_pos.is_empty() {
1118 tree.positions() } else {
1120 func_pos
1121 }
1122 } else {
1123 let term_pos = tree.terminal_positions();
1124 if term_pos.is_empty() {
1125 tree.positions()
1126 } else {
1127 term_pos
1128 }
1129 };
1130
1131 if positions.is_empty() {
1132 vec![] } else {
1134 positions[rng.gen_range(0..positions.len())].clone()
1135 }
1136 }
1137}
1138
1139impl<T: Terminal, F: Function> CrossoverOperator<TreeGenome<T, F>> for SubtreeCrossover {
1140 fn crossover<R: Rng>(
1141 &self,
1142 parent1: &TreeGenome<T, F>,
1143 parent2: &TreeGenome<T, F>,
1144 rng: &mut R,
1145 ) -> OperatorResult<(TreeGenome<T, F>, TreeGenome<T, F>)> {
1146 let point1 = self.select_crossover_point(&parent1.root, rng);
1148 let point2 = self.select_crossover_point(&parent2.root, rng);
1149
1150 let subtree1 = parent1
1152 .root
1153 .get_subtree(&point1)
1154 .cloned()
1155 .unwrap_or_else(|| parent1.root.clone());
1156 let subtree2 = parent2
1157 .root
1158 .get_subtree(&point2)
1159 .cloned()
1160 .unwrap_or_else(|| parent2.root.clone());
1161
1162 let mut child1_root = parent1.root.clone();
1164 let mut child2_root = parent2.root.clone();
1165
1166 if point1.is_empty() {
1168 child1_root = subtree2.clone();
1169 } else {
1170 child1_root.replace_subtree(&point1, subtree2.clone());
1171 }
1172
1173 if point2.is_empty() {
1174 child2_root = subtree1.clone();
1175 } else {
1176 child2_root.replace_subtree(&point2, subtree1);
1177 }
1178
1179 if let Some(max_depth) = self.max_depth {
1181 if child1_root.depth() > max_depth {
1182 return OperatorResult::Success((parent1.clone(), parent2.clone()));
1184 }
1185 if child2_root.depth() > max_depth {
1186 return OperatorResult::Success((parent1.clone(), parent2.clone()));
1187 }
1188 }
1189
1190 let child1 = TreeGenome::new(child1_root, parent1.max_depth);
1191 let child2 = TreeGenome::new(child2_root, parent2.max_depth);
1192
1193 OperatorResult::Success((child1, child2))
1194 }
1195}
1196
1197#[cfg(test)]
1198mod tests {
1199 use super::*;
1200 use approx::assert_relative_eq;
1201
1202 #[test]
1203 fn test_sbx_creates_valid_offspring() {
1204 let mut rng = rand::thread_rng();
1205 let parent1 = RealVector::new(vec![0.0, 0.0, 0.0]);
1206 let parent2 = RealVector::new(vec![1.0, 1.0, 1.0]);
1207
1208 let sbx = SbxCrossover::new(20.0);
1209 let result = sbx.crossover(&parent1, &parent2, &mut rng);
1210
1211 assert!(result.is_ok());
1212 let (child1, child2) = result.genome().unwrap();
1213 assert_eq!(child1.dimension(), 3);
1214 assert_eq!(child2.dimension(), 3);
1215 }
1216
1217 #[test]
1218 fn test_sbx_with_bounds() {
1219 let mut rng = rand::thread_rng();
1220 let parent1 = RealVector::new(vec![-0.3, -0.2]);
1222 let parent2 = RealVector::new(vec![0.3, 0.4]);
1223 let bounds = MultiBounds::symmetric(0.5, 2);
1224
1225 let sbx = SbxCrossover::new(2.0).with_exchange_probability(1.0);
1228
1229 for _ in 0..100 {
1231 let result = sbx.crossover_bounded(&parent1, &parent2, &bounds, &mut rng);
1232 let (child1, child2) = result.genome().unwrap();
1233
1234 for gene in child1.genes() {
1235 assert!(
1236 *gene >= -0.5 && *gene <= 0.5,
1237 "gene {} out of bounds [-0.5, 0.5]",
1238 gene
1239 );
1240 }
1241 for gene in child2.genes() {
1242 assert!(
1243 *gene >= -0.5 && *gene <= 0.5,
1244 "gene {} out of bounds [-0.5, 0.5]",
1245 gene
1246 );
1247 }
1248 }
1249 }
1250
1251 #[test]
1252 fn test_sbx_default_probabilities_are_distinct() {
1253 let sbx = SbxCrossover::new(20.0);
1258 assert_eq!(sbx.crossover_probability, 0.9);
1259 assert_eq!(sbx.exchange_probability, 0.5);
1260
1261 let sbx = SbxCrossover::new(20.0).with_probability(0.7);
1264 assert_eq!(
1265 CrossoverOperator::<RealVector>::crossover_probability(&sbx),
1266 0.7
1267 );
1268 assert_eq!(sbx.exchange_probability, 0.5);
1269
1270 let sbx = SbxCrossover::new(20.0).with_exchange_probability(0.3);
1272 assert_eq!(sbx.exchange_probability, 0.3);
1273 assert_eq!(
1274 CrossoverOperator::<RealVector>::crossover_probability(&sbx),
1275 0.9
1276 );
1277 }
1278
1279 #[test]
1280 fn test_sbx_bounded_no_atom_at_bounds_mean_preserving() {
1281 use rand::SeedableRng;
1289 let mut rng = rand::rngs::StdRng::seed_from_u64(12345);
1290
1291 let bounds = MultiBounds::symmetric(1.0, 1); let parent1 = RealVector::new(vec![-0.6]);
1293 let parent2 = RealVector::new(vec![0.6]);
1294 let parent_mid = 0.0;
1295
1296 let sbx = SbxCrossover::new(2.0).with_exchange_probability(1.0);
1299
1300 let n = 20_000;
1301 let mut atom_count = 0usize;
1302 let mut sum_children = 0.0;
1303 let mut child_samples = 0usize;
1304 let eps = 1e-9;
1305
1306 for _ in 0..n {
1307 let result = sbx.crossover_bounded(&parent1, &parent2, &bounds, &mut rng);
1308 let (c1, c2) = result.genome().unwrap();
1309 for &g in c1.genes().iter().chain(c2.genes()) {
1310 assert!(
1311 (-1.0..=1.0).contains(&g),
1312 "child gene {g} escaped bounds [-1, 1]"
1313 );
1314 if (g - 1.0).abs() < eps || (g + 1.0).abs() < eps {
1315 atom_count += 1;
1316 }
1317 sum_children += g;
1318 child_samples += 1;
1319 }
1320 }
1321
1322 let atom_frac = atom_count as f64 / child_samples as f64;
1323 assert!(
1324 atom_frac < 0.01,
1325 "too many children pinned to the bounds: {atom_frac} (expected ~0)"
1326 );
1327
1328 let mean_children = sum_children / child_samples as f64;
1329 assert!(
1330 (mean_children - parent_mid).abs() < 0.02,
1331 "bounded SBX is not mean-preserving: mean {mean_children} vs parent midpoint {parent_mid}"
1332 );
1333 }
1334
1335 #[test]
1336 fn test_sbx_spread_factor() {
1337 let sbx = SbxCrossover::new(20.0);
1338
1339 let beta = sbx.spread_factor(0.5);
1341 assert_relative_eq!(beta, 1.0, epsilon = 1e-10);
1342
1343 let beta_low = sbx.spread_factor(0.25);
1345 let beta_high = sbx.spread_factor(0.75);
1346 assert_relative_eq!(beta_low, 1.0 / beta_high, epsilon = 1e-10);
1347 }
1348
1349 #[test]
1350 fn test_sbx_identical_parents() {
1351 let mut rng = rand::thread_rng();
1352 let parent = RealVector::new(vec![1.0, 2.0, 3.0]);
1353
1354 let sbx = SbxCrossover::new(20.0);
1355 let result = sbx.crossover(&parent, &parent, &mut rng);
1356
1357 let (child1, child2) = result.genome().unwrap();
1358 assert_eq!(child1.genes(), parent.genes());
1360 assert_eq!(child2.genes(), parent.genes());
1361 }
1362
1363 #[test]
1364 fn test_sbx_dimension_mismatch() {
1365 let mut rng = rand::thread_rng();
1366 let parent1 = RealVector::new(vec![1.0, 2.0]);
1367 let parent2 = RealVector::new(vec![1.0, 2.0, 3.0]);
1368
1369 let sbx = SbxCrossover::new(20.0);
1370 let result = sbx.crossover(&parent1, &parent2, &mut rng);
1371
1372 assert!(!result.is_ok());
1373 }
1374
1375 #[test]
1376 fn test_blx_alpha_creates_valid_offspring() {
1377 let mut rng = rand::thread_rng();
1378 let parent1 = RealVector::new(vec![0.0, 0.0]);
1379 let parent2 = RealVector::new(vec![1.0, 1.0]);
1380
1381 let blx = BlxAlphaCrossover::new(0.5);
1382 let result = blx.crossover(&parent1, &parent2, &mut rng);
1383
1384 assert!(result.is_ok());
1385 let (child1, child2) = result.genome().unwrap();
1386 assert_eq!(child1.dimension(), 2);
1387 assert_eq!(child2.dimension(), 2);
1388 }
1389
1390 #[test]
1391 fn test_blx_alpha_range() {
1392 let mut rng = rand::thread_rng();
1393 let parent1 = RealVector::new(vec![0.0]);
1394 let parent2 = RealVector::new(vec![1.0]);
1395
1396 let blx = BlxAlphaCrossover::new(0.0); for _ in 0..100 {
1399 let result = blx.crossover(&parent1, &parent2, &mut rng);
1400 let (child1, child2) = result.genome().unwrap();
1401
1402 assert!(child1[0] >= 0.0 && child1[0] <= 1.0);
1404 assert!(child2[0] >= 0.0 && child2[0] <= 1.0);
1405 }
1406 }
1407
1408 #[test]
1409 fn test_uniform_crossover_creates_valid_offspring() {
1410 let mut rng = rand::thread_rng();
1411 let parent1 = BitString::new(vec![true, true, true, true]);
1412 let parent2 = BitString::new(vec![false, false, false, false]);
1413
1414 let ux = UniformCrossover::new();
1415 let result = ux.crossover(&parent1, &parent2, &mut rng);
1416
1417 assert!(result.is_ok());
1418 let (child1, child2) = result.genome().unwrap();
1419 assert_eq!(child1.len(), 4);
1420 assert_eq!(child2.len(), 4);
1421 }
1422
1423 #[test]
1424 fn test_uniform_crossover_complementary() {
1425 let mut rng = rand::thread_rng();
1426 let parent1 = BitString::new(vec![true, true, true, true]);
1427 let parent2 = BitString::new(vec![false, false, false, false]);
1428
1429 let ux = UniformCrossover::new();
1430 let result = ux.crossover(&parent1, &parent2, &mut rng);
1431
1432 let (child1, child2) = result.genome().unwrap();
1433
1434 for i in 0..4 {
1436 assert_ne!(child1[i], child2[i]);
1437 }
1438 }
1439
1440 #[test]
1441 fn test_one_point_crossover() {
1442 let mut rng = rand::thread_rng();
1443 let parent1 = BitString::ones(10);
1444 let parent2 = BitString::zeros(10);
1445
1446 let opx = OnePointCrossover::new();
1447 let result = opx.crossover(&parent1, &parent2, &mut rng);
1448
1449 assert!(result.is_ok());
1450 let (child1, child2) = result.genome().unwrap();
1451
1452 for i in 0..10 {
1455 assert_ne!(child1[i], child2[i]);
1456 }
1457 }
1458
1459 #[test]
1460 fn test_two_point_crossover() {
1461 let mut rng = rand::thread_rng();
1462 let parent1 = BitString::ones(10);
1463 let parent2 = BitString::zeros(10);
1464
1465 let tpx = TwoPointCrossover::new();
1466 let result = tpx.crossover(&parent1, &parent2, &mut rng);
1467
1468 assert!(result.is_ok());
1469 let (child1, child2) = result.genome().unwrap();
1470 assert_eq!(child1.len(), 10);
1471 assert_eq!(child2.len(), 10);
1472 }
1473
1474 #[test]
1475 fn test_arithmetic_crossover() {
1476 let mut rng = rand::thread_rng();
1477 let parent1 = RealVector::new(vec![0.0, 0.0]);
1478 let parent2 = RealVector::new(vec![1.0, 1.0]);
1479
1480 let ax = ArithmeticCrossover::new(0.5);
1481 let result = ax.crossover(&parent1, &parent2, &mut rng);
1482
1483 let (child1, child2) = result.genome().unwrap();
1484
1485 for gene in child1.genes() {
1487 assert_relative_eq!(*gene, 0.5);
1488 }
1489 for gene in child2.genes() {
1490 assert_relative_eq!(*gene, 0.5);
1491 }
1492 }
1493
1494 #[test]
1495 fn test_arithmetic_crossover_weighted() {
1496 let mut rng = rand::thread_rng();
1497 let parent1 = RealVector::new(vec![0.0]);
1498 let parent2 = RealVector::new(vec![1.0]);
1499
1500 let ax = ArithmeticCrossover::new(0.75);
1501 let result = ax.crossover(&parent1, &parent2, &mut rng);
1502
1503 let (child1, child2) = result.genome().unwrap();
1504
1505 assert_relative_eq!(child1[0], 0.25);
1508 assert_relative_eq!(child2[0], 0.75);
1509 }
1510
1511 #[test]
1516 fn test_pmx_creates_valid_permutations() {
1517 let mut rng = rand::thread_rng();
1518 let parent1 = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
1519 let parent2 = Permutation::new(vec![7, 6, 5, 4, 3, 2, 1, 0]);
1520
1521 let pmx = PmxCrossover::new();
1522
1523 for _ in 0..100 {
1524 let result = pmx.crossover(&parent1, &parent2, &mut rng);
1525 assert!(result.is_ok());
1526 let (child1, child2) = result.genome().unwrap();
1527
1528 assert!(child1.is_valid_permutation());
1529 assert!(child2.is_valid_permutation());
1530 assert_eq!(child1.dimension(), 8);
1531 assert_eq!(child2.dimension(), 8);
1532 }
1533 }
1534
1535 #[test]
1536 fn test_pmx_identical_parents() {
1537 let mut rng = rand::thread_rng();
1538 let parent = Permutation::new(vec![0, 1, 2, 3, 4]);
1539
1540 let pmx = PmxCrossover::new();
1541 let result = pmx.crossover(&parent, &parent, &mut rng);
1542
1543 let (child1, child2) = result.genome().unwrap();
1544 assert_eq!(child1.as_slice(), parent.as_slice());
1546 assert_eq!(child2.as_slice(), parent.as_slice());
1547 }
1548
1549 #[test]
1550 fn test_pmx_dimension_mismatch() {
1551 let mut rng = rand::thread_rng();
1552 let parent1 = Permutation::new(vec![0, 1, 2, 3]);
1553 let parent2 = Permutation::new(vec![0, 1, 2, 3, 4]);
1554
1555 let pmx = PmxCrossover::new();
1556 let result = pmx.crossover(&parent1, &parent2, &mut rng);
1557
1558 assert!(!result.is_ok());
1559 }
1560
1561 #[test]
1562 fn test_ox_creates_valid_permutations() {
1563 let mut rng = rand::thread_rng();
1564 let parent1 = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
1565 let parent2 = Permutation::new(vec![7, 6, 5, 4, 3, 2, 1, 0]);
1566
1567 let ox = OxCrossover::new();
1568
1569 for _ in 0..100 {
1570 let result = ox.crossover(&parent1, &parent2, &mut rng);
1571 assert!(result.is_ok());
1572 let (child1, child2) = result.genome().unwrap();
1573
1574 assert!(child1.is_valid_permutation());
1575 assert!(child2.is_valid_permutation());
1576 assert_eq!(child1.dimension(), 8);
1577 assert_eq!(child2.dimension(), 8);
1578 }
1579 }
1580
1581 #[test]
1582 fn test_ox_preserves_segment() {
1583 use rand::SeedableRng;
1584 let mut rng = rand::rngs::StdRng::seed_from_u64(42);
1585 let parent1 = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
1586 let parent2 = Permutation::new(vec![7, 6, 5, 4, 3, 2, 1, 0]);
1587
1588 let ox = OxCrossover::new();
1589 let result = ox.crossover(&parent1, &parent2, &mut rng);
1590
1591 let (child1, child2) = result.genome().unwrap();
1592 assert!(child1.is_valid_permutation());
1593 assert!(child2.is_valid_permutation());
1594 }
1595
1596 #[test]
1597 fn test_cx_creates_valid_permutations() {
1598 let mut rng = rand::thread_rng();
1599 let parent1 = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
1600 let parent2 = Permutation::new(vec![1, 2, 3, 4, 5, 6, 7, 0]);
1601
1602 let cx = CxCrossover::new();
1603
1604 for _ in 0..100 {
1605 let result = cx.crossover(&parent1, &parent2, &mut rng);
1606 assert!(result.is_ok());
1607 let (child1, child2) = result.genome().unwrap();
1608
1609 assert!(child1.is_valid_permutation());
1610 assert!(child2.is_valid_permutation());
1611 assert_eq!(child1.dimension(), 8);
1612 assert_eq!(child2.dimension(), 8);
1613 }
1614 }
1615
1616 #[test]
1617 fn test_cx_preserves_positions() {
1618 let mut rng = rand::thread_rng();
1619 let parent1 = Permutation::new(vec![0, 1, 2, 3, 4]);
1621 let parent2 = Permutation::new(vec![4, 3, 2, 1, 0]);
1622
1623 let cx = CxCrossover::new();
1624 let result = cx.crossover(&parent1, &parent2, &mut rng);
1625
1626 let (child1, child2) = result.genome().unwrap();
1627
1628 for i in 0..5 {
1630 let c1_val = child1[i];
1631 let c2_val = child2[i];
1632
1633 assert!(c1_val == parent1[i] || c1_val == parent2[i]);
1634 assert!(c2_val == parent1[i] || c2_val == parent2[i]);
1635 }
1636 }
1637
1638 #[test]
1639 fn test_cx_identical_parents() {
1640 let mut rng = rand::thread_rng();
1641 let parent = Permutation::new(vec![0, 1, 2, 3, 4]);
1642
1643 let cx = CxCrossover::new();
1644 let result = cx.crossover(&parent, &parent, &mut rng);
1645
1646 let (child1, child2) = result.genome().unwrap();
1647 assert_eq!(child1.as_slice(), parent.as_slice());
1649 assert_eq!(child2.as_slice(), parent.as_slice());
1650 }
1651
1652 #[test]
1653 fn test_erx_creates_valid_permutations() {
1654 let mut rng = rand::thread_rng();
1655 let parent1 = Permutation::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
1656 let parent2 = Permutation::new(vec![7, 6, 5, 4, 3, 2, 1, 0]);
1657
1658 let erx = EdgeRecombinationCrossover::new();
1659
1660 for _ in 0..50 {
1661 let result = erx.crossover(&parent1, &parent2, &mut rng);
1662 assert!(result.is_ok());
1663 let (child1, child2) = result.genome().unwrap();
1664
1665 assert!(child1.is_valid_permutation());
1666 assert!(child2.is_valid_permutation());
1667 assert_eq!(child1.dimension(), 8);
1668 assert_eq!(child2.dimension(), 8);
1669 }
1670 }
1671
1672 #[test]
1673 fn test_erx_preserves_some_edges() {
1674 use rand::SeedableRng;
1675 let mut rng = rand::rngs::StdRng::seed_from_u64(42);
1676
1677 let parent1 = Permutation::new(vec![0, 1, 2, 3, 4]);
1679 let parent2 = Permutation::new(vec![0, 1, 4, 3, 2]);
1680
1681 let erx = EdgeRecombinationCrossover::new();
1683 let result = erx.crossover(&parent1, &parent2, &mut rng);
1684
1685 let (child1, _child2) = result.genome().unwrap();
1686 assert!(child1.is_valid_permutation());
1687 }
1688}