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