1use radiate_core::{
2 AlterContext, BoundedGene, Chromosome, Crossover, Expr, Gene, RateSet, random_provider,
3};
4use radiate_utils::Float;
5
6#[derive(Debug, Clone, PartialEq)]
15pub struct BlendCrossover {
16 rate: Expr,
17 alpha: f32,
18}
19
20impl BlendCrossover {
21 pub fn new(rate: impl Into<Expr>, alpha: f32) -> Self {
24 let rate = rate.into();
25
26 if !(0.0..=1.0).contains(&alpha) {
27 panic!("Alpha must be between 0 and 1");
28 }
29
30 BlendCrossover { rate, alpha }
31 }
32}
33
34impl<A, G, C> Crossover<C> for BlendCrossover
35where
36 A: Float,
37 G: Gene<Allele = A> + BoundedGene,
38 C: Chromosome<Gene = G>,
39{
40 fn rates(&self) -> RateSet {
41 RateSet::new(self.rate.clone())
42 }
43
44 #[inline]
45 fn cross_chromosomes(
46 &self,
47 chrom_one: &mut C,
48 chrom_two: &mut C,
49 ctx: &mut AlterContext,
50 ) -> usize {
51 let mut cross_count = 0;
52 let alpha = A::from(self.alpha).unwrap();
53
54 random_provider::with_rng(|rand| {
55 chrom_one.zip(chrom_two).for_each(|one, two| {
56 if rand.bool(ctx.rate()) {
57 let allele_one = *one.allele();
58 let allele_two = *two.allele();
59
60 let new_allele_one = allele_one - (alpha * (allele_two - allele_one));
61 let new_allele_two = allele_two - (alpha * (allele_one - allele_two));
62
63 one.set_allele(new_allele_one);
64 two.set_allele(new_allele_two);
65
66 cross_count += 1;
67 }
68 });
69 });
70
71 cross_count
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78 use radiate_core::{Chromosome, FloatChromosome, FloatGene, alter::AlterUpdates};
79
80 #[test]
81 fn test_cross_chromosomes_basic() {
82 let crossover = BlendCrossover::new(1.0, 0.5);
83
84 let genes1 = vec![
85 FloatGene::new(1.0, 0.0..10.0, -10.0..10.0),
86 FloatGene::new(2.0, 0.0..10.0, -10.0..10.0),
87 FloatGene::new(3.0, 0.0..10.0, -10.0..10.0),
88 ];
89 let genes2 = vec![
90 FloatGene::new(4.0, 0.0..10.0, -10.0..10.0),
91 FloatGene::new(5.0, 0.0..10.0, -10.0..10.0),
92 FloatGene::new(6.0, 0.0..10.0, -10.0..10.0),
93 ];
94
95 let mut chrom_one = FloatChromosome::new(genes1);
96 let mut chrom_two = FloatChromosome::new(genes2);
97
98 let original_one: Vec<f32> = chrom_one.iter().map(|g| *g.allele()).collect();
99 let original_two: Vec<f32> = chrom_two.iter().map(|g| *g.allele()).collect();
100
101 let mut updates = AlterUpdates::default();
102 let mut ctx = AlterContext::new(&mut updates, 0, 1.0, &[]);
103
104 let result = crossover.cross_chromosomes(&mut chrom_one, &mut chrom_two, &mut ctx);
105
106 assert_eq!(result, 3);
107
108 let alpha = 0.5;
112
113 for i in 0..chrom_one.len() {
114 let expected_one = original_one[i] - (alpha * (original_two[i] - original_one[i]));
115 let expected_two = original_two[i] - (alpha * (original_one[i] - original_two[i]));
116
117 assert!((chrom_one.get(i).unwrap().allele() - expected_one).abs() < 1e-6);
118 assert!((chrom_two.get(i).unwrap().allele() - expected_two).abs() < 1e-6);
119 }
120 }
121
122 #[test]
123 fn test_cross_chromosomes_zero_rate() {
124 let crossover = BlendCrossover::new(0.0, 0.5);
125
126 let genes1 = vec![
127 FloatGene::new(1.0, 0.0..10.0, 0.0..10.0),
128 FloatGene::new(2.0, 0.0..10.0, 0.0..10.0),
129 ];
130 let genes2 = vec![
131 FloatGene::new(4.0, 0.0..10.0, 0.0..10.0),
132 FloatGene::new(5.0, 0.0..10.0, 0.0..10.0),
133 ];
134
135 let mut chrom_one = FloatChromosome::new(genes1);
136 let mut chrom_two = FloatChromosome::new(genes2);
137
138 let original_one: Vec<f32> = chrom_one.iter().map(|g| *g.allele()).collect();
139 let original_two: Vec<f32> = chrom_two.iter().map(|g| *g.allele()).collect();
140
141 let mut updates = AlterUpdates::default();
142 let mut ctx = AlterContext::new(&mut updates, 0, 0.0, &[]);
143
144 let result = crossover.cross_chromosomes(&mut chrom_one, &mut chrom_two, &mut ctx);
145
146 assert_eq!(result, 0);
147
148 for i in 0..chrom_one.len() {
150 assert_eq!(*chrom_one.get(i).unwrap().allele(), original_one[i]);
151 assert_eq!(*chrom_two.get(i).unwrap().allele(), original_two[i]);
152 }
153 }
154
155 #[test]
156 fn test_cross_chromosomes_different_lengths() {
157 let crossover = BlendCrossover::new(1.0, 0.3);
158
159 let genes1 = vec![
160 FloatGene::new(1.0, 0.0..10.0, 0.0..10.0),
161 FloatGene::new(2.0, 0.0..10.0, 0.0..10.0),
162 FloatGene::new(3.0, 0.0..10.0, 0.0..10.0),
163 ];
164 let genes2 = vec![
165 FloatGene::new(4.0, 0.0..10.0, 0.0..10.0),
166 FloatGene::new(5.0, 0.0..10.0, 0.0..10.0),
167 ];
168
169 let mut chrom_one = FloatChromosome::new(genes1);
170 let mut chrom_two = FloatChromosome::new(genes2);
171
172 let mut updates = AlterUpdates::default();
173 let mut ctx = AlterContext::new(&mut updates, 0, 1.0, &[]);
174
175 let result = crossover.cross_chromosomes(&mut chrom_one, &mut chrom_two, &mut ctx);
176
177 assert_eq!(result, 2);
178
179 let alpha = 0.3_f32;
180 let expected_one_0 = 1.0_f32 - (alpha * (4.0_f32 - 1.0_f32));
181 let expected_two_0 = 4.0_f32 - (alpha * (1.0_f32 - 4.0_f32));
182 let expected_one_1 = 2.0_f32 - (alpha * (5.0_f32 - 2.0_f32));
183 let expected_two_1 = 5.0_f32 - (alpha * (2.0_f32 - 5.0_f32));
184
185 assert!((*chrom_one.get(0).unwrap().allele() - expected_one_0).abs() < 1e-6);
186 assert!((*chrom_two.get(0).unwrap().allele() - expected_two_0).abs() < 1e-6);
187 assert!((*chrom_one.get(1).unwrap().allele() - expected_one_1).abs() < 1e-6);
188 assert!((*chrom_two.get(1).unwrap().allele() - expected_two_1).abs() < 1e-6);
189
190 assert_eq!(*chrom_one.get(2).unwrap().allele(), 3.0);
191 }
192
193 #[test]
194 fn test_cross_chromosomes_alpha_zero() {
195 let crossover = BlendCrossover::new(1.0, 0.0);
196
197 let genes1 = vec![
198 FloatGene::new(1.0, 0.0..10.0, 0.0..10.0),
199 FloatGene::new(2.0, 0.0..10.0, 0.0..10.0),
200 ];
201 let genes2 = vec![
202 FloatGene::new(4.0, 0.0..10.0, 0.0..10.0),
203 FloatGene::new(5.0, 0.0..10.0, 0.0..10.0),
204 ];
205
206 let mut chrom_one = FloatChromosome::new(genes1);
207 let mut chrom_two = FloatChromosome::new(genes2);
208
209 let original_one: Vec<f32> = chrom_one.iter().map(|g| *g.allele()).collect();
210 let original_two: Vec<f32> = chrom_two.iter().map(|g| *g.allele()).collect();
211
212 let mut updates = AlterUpdates::default();
213 let mut ctx = AlterContext::new(&mut updates, 0, 1.0, &[]);
214
215 let result = crossover.cross_chromosomes(&mut chrom_one, &mut chrom_two, &mut ctx);
216
217 assert_eq!(result, 2);
218
219 for i in 0..chrom_one.len() {
221 assert_eq!(*chrom_one.get(i).unwrap().allele(), original_one[i]);
222 assert_eq!(*chrom_two.get(i).unwrap().allele(), original_two[i]);
223 }
224 }
225
226 #[test]
227 fn test_cross_chromosomes_alpha_one() {
228 let crossover = BlendCrossover::new(1.0, 1.0);
229
230 let genes1 = vec![
231 FloatGene::new(1.0, 0.0..10.0, -10.0..10.0),
232 FloatGene::new(2.0, 0.0..10.0, -10.0..10.0),
233 ];
234 let genes2 = vec![
235 FloatGene::new(4.0, 0.0..10.0, -10.0..10.0),
236 FloatGene::new(5.0, 0.0..10.0, -10.0..10.0),
237 ];
238
239 let mut chrom_one = FloatChromosome::new(genes1);
240 let mut chrom_two = FloatChromosome::new(genes2);
241
242 let mut updates = AlterUpdates::default();
243 let mut ctx = AlterContext::new(&mut updates, 0, 1.0, &[]);
244
245 let result = crossover.cross_chromosomes(&mut chrom_one, &mut chrom_two, &mut ctx);
246
247 assert_eq!(result, 2);
248
249 assert_eq!(*chrom_one.get(0).unwrap().allele(), -2.0);
251 assert_eq!(*chrom_two.get(0).unwrap().allele(), 7.0);
252 assert_eq!(*chrom_one.get(1).unwrap().allele(), -1.0);
253 assert_eq!(*chrom_two.get(1).unwrap().allele(), 8.0);
254 }
255
256 #[test]
257 fn test_cross_chromosomes_identical_parents() {
258 let crossover = BlendCrossover::new(1.0, 0.5);
259
260 let genes = vec![
261 FloatGene::new(1.0, 0.0..10.0, 0.0..10.0),
262 FloatGene::new(2.0, 0.0..10.0, 0.0..10.0),
263 ];
264
265 let mut chrom_one = FloatChromosome::new(genes.clone());
266 let mut chrom_two = FloatChromosome::new(genes);
267
268 let mut updates = AlterUpdates::default();
269 let mut ctx = AlterContext::new(&mut updates, 0, 1.0, &[]);
270
271 let result = crossover.cross_chromosomes(&mut chrom_one, &mut chrom_two, &mut ctx);
272
273 assert_eq!(result, 2);
274
275 for i in 0..chrom_one.len() {
277 assert_eq!(
278 *chrom_one.get(i).unwrap().allele(),
279 *chrom_two.get(i).unwrap().allele()
280 );
281 }
282 }
283
284 #[test]
285 fn test_cross_chromosomes_property_based() {
286 let crossover = BlendCrossover::new(1.0, 0.5);
287
288 for _ in 0..50 {
289 let genes1: Vec<FloatGene<f32>> = (0..5)
290 .map(|_| {
291 FloatGene::new(
292 random_provider::random::<f32>() * 10.0,
293 0.0..10.0,
294 -10.0..10.0,
295 )
296 })
297 .collect();
298 let genes2: Vec<FloatGene<f32>> = (0..5)
299 .map(|_| {
300 FloatGene::new(
301 random_provider::random::<f32>() * 10.0,
302 0.0..10.0,
303 -10.0..10.0,
304 )
305 })
306 .collect();
307
308 let mut chrom_one = FloatChromosome::new(genes1);
309 let mut chrom_two = FloatChromosome::new(genes2);
310
311 let original_one: Vec<f32> = chrom_one.iter().map(|g| *g.allele()).collect();
312 let original_two: Vec<f32> = chrom_two.iter().map(|g| *g.allele()).collect();
313
314 let mut updates = AlterUpdates::default();
315 let mut ctx = AlterContext::new(&mut updates, 0, 1.0, &[]);
316
317 let result = crossover.cross_chromosomes(&mut chrom_one, &mut chrom_two, &mut ctx);
318
319 assert_eq!(result, 5);
320
321 let alpha = 0.5;
322 for i in 0..chrom_one.len() {
323 let expected_one = original_one[i] - (alpha * (original_two[i] - original_one[i]));
324 let expected_two = original_two[i] - (alpha * (original_one[i] - original_two[i]));
325
326 let gene_one = chrom_one.get(i).unwrap();
327 let gene_two = chrom_two.get(i).unwrap();
328
329 if expected_one < *gene_one.bound_range().0
330 || expected_one > *gene_one.bound_range().1
331 {
332 assert!(*gene_one.allele() >= *gene_one.bound_range().0);
333 assert!(*gene_one.allele() <= *gene_one.bound_range().1);
334 } else {
335 assert!((gene_one.allele() - expected_one).abs() < 1e-6);
336 }
337
338 if expected_two < *gene_two.bound_range().0
339 || expected_two > *gene_two.bound_range().1
340 {
341 assert!(*gene_two.allele() >= *gene_two.bound_range().0);
342 assert!(*gene_two.allele() <= *gene_two.bound_range().1);
343 } else {
344 assert!((gene_two.allele() - expected_two).abs() < 1e-6);
345 }
346 }
347 }
348 }
349
350 #[test]
351 fn test_cross_chromosomes_edge_cases() {
352 let crossover = BlendCrossover::new(1.0, 0.5);
353
354 let genes1 = vec![FloatGene::new(1.0, 0.0..10.0, 0.0..10.0)];
356 let genes2 = vec![FloatGene::new(4.0, 0.0..10.0, 0.0..10.0)];
357
358 let mut chrom_one = FloatChromosome::new(genes1);
359 let mut chrom_two = FloatChromosome::new(genes2);
360
361 let mut updates = AlterUpdates::default();
362 let mut ctx = AlterContext::new(&mut updates, 0, 1.0, &[]);
363
364 let result = crossover.cross_chromosomes(&mut chrom_one, &mut chrom_two, &mut ctx);
365
366 assert_eq!(result, 1);
367
368 let mut empty_one = FloatChromosome::<f32>::new(vec![]);
370 let mut empty_two = FloatChromosome::<f32>::new(vec![]);
371
372 let result = crossover.cross_chromosomes(&mut empty_one, &mut empty_two, &mut ctx);
373 assert_eq!(result, 0);
374 }
375
376 #[test]
377 fn test_blend_formula_verification() {
378 let crossover = BlendCrossover::new(1.0, 0.3);
379 let alpha = 0.3;
380
381 let genes1 = vec![FloatGene::new(2.0, 0.0..10.0, 0.0..10.0)];
383 let genes2 = vec![FloatGene::new(8.0, 0.0..10.0, 0.0..10.0)];
384
385 let mut chrom_one = FloatChromosome::<f32>::new(genes1);
386 let mut chrom_two = FloatChromosome::<f32>::new(genes2);
387
388 let mut updates = AlterUpdates::default();
389 let mut ctx = AlterContext::new(&mut updates, 0, 1.0, &[]);
390
391 crossover.cross_chromosomes(&mut chrom_one, &mut chrom_two, &mut ctx);
392
393 let expected_one = 2.0 - (alpha * (8.0 - 2.0));
397 let expected_two = 8.0 - (alpha * (2.0 - 8.0));
398
399 assert!((chrom_one.get(0).unwrap().allele() - expected_one).abs() < 1e-6);
400 assert!((chrom_two.get(0).unwrap().allele() - expected_two).abs() < 1e-6);
401 }
402}