Skip to content

Commit c90e4eb

Browse files
committed
Fix CrossoverRejuvenate by replacing bottom parents with top parents
1 parent 2a85fb9 commit c90e4eb

File tree

5 files changed

+28
-12
lines changed

5 files changed

+28
-12
lines changed

CHANGELOG.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5454
### Added
5555
* Add `best_chromosome_indices()` on `Population`, used to implement
5656
`elitism_rate` without the need for a full sort
57-
* Add `CrossoverRejuvenate` as new implementation for the old `CrossoverClone`
58-
behaviour. No copying, just turn parents into offspring in-place. The
57+
* Add `CrossoverRejuvenate` as new limited cloning implementation for the old
58+
`CrossoverClone` behaviour, which had limited cloning. The new
5959
`CrossoverClone` now adds actual clones as offspring without removing the
60-
parents. However `CrossoverRejuvenate` works remarkably bad, as the old
61-
`CrossoverClone` had the pre-v0.20 selection-rate which still copied the best
62-
parent chromosomes to repopulate after selection.
60+
parents, which is much more cloning than the original. For
61+
`CrossoverRejuvenate`: drop non-selected parents, then clone top parents to
62+
repopulate, then rejuvenate selected parents to children in place. No copying
63+
of chromosomes for creating the offspring itself, only for repopulating the
64+
dropped non-selected parents (smaller fraction). However the cloning of
65+
top-parents is crucial for driving the Evolve process as experimenting with
66+
the evolve_nqueens example demonstrated.
6367

6468
## [0.19.4] - 2025-05-05
6569

examples/evolve_nqueens.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ fn main() {
5151
.with_target_fitness_score(0)
5252
// .with_replace_on_equal_fitness(true) // not crucial for this problem
5353
.with_mutate(MutateSingleGene::new(0.2))
54-
// .with_crossover(CrossoverRejuvenate::new(0.9)) // works remarkably bad, extremely interesting why, probably the best_genes are lost all the time?
55-
.with_crossover(CrossoverClone::new(0.9))
54+
.with_crossover(CrossoverRejuvenate::new(0.9))
55+
// .with_crossover(CrossoverClone::new(0.9))
5656
.with_select(SelectElite::new(0.5, 0.5))
5757
.with_reporter(EvolveReporterSimple::new(100))
5858
.build()

src/crossover/clone.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ impl Crossover for Clone {
2424
let now = Instant::now();
2525
let existing_population_size = state.population.chromosomes.len();
2626
let selected_population_size =
27-
(state.population.size() as f32 * self.selection_rate).ceil() as usize;
27+
(existing_population_size as f32 * self.selection_rate).ceil() as usize;
2828
genotype
2929
.chromosome_cloner_expand(&mut state.population.chromosomes, selected_population_size);
3030
state

src/crossover/rejuvenate.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ use crate::strategy::{StrategyAction, StrategyReporter, StrategyState};
66
use rand::Rng;
77
use std::time::Instant;
88

9-
/// Rejuvenate parents to children in place, no copying of chromosomes
9+
/// Drop non-selected parents, then clone top parents to repopulate, then rejuvenate selected
10+
/// parents to children in place. No copying of chromosomes for creating the offspring itself, only
11+
/// for repopulating the dropped non-selected parents (smaller fraction)
1012
/// Allowed for unique genotypes.
1113
#[derive(Clone, Debug)]
1214
pub struct Rejuvenate {
@@ -15,15 +17,25 @@ pub struct Rejuvenate {
1517
impl Crossover for Rejuvenate {
1618
fn call<G: EvolveGenotype, R: Rng, SR: StrategyReporter<Genotype = G>>(
1719
&mut self,
18-
_genotype: &mut G,
20+
genotype: &mut G,
1921
state: &mut EvolveState<G>,
2022
_config: &EvolveConfig,
2123
_reporter: &mut SR,
2224
_rng: &mut R,
2325
) {
2426
let now = Instant::now();
27+
let existing_population_size = state.population.chromosomes.len();
2528
let selected_population_size =
26-
(state.population.size() as f32 * self.selection_rate).ceil() as usize;
29+
(existing_population_size as f32 * self.selection_rate).ceil() as usize;
30+
let dropped_population_size = (existing_population_size - selected_population_size).max(0);
31+
32+
genotype.chromosome_destructor_truncate(
33+
&mut state.population.chromosomes,
34+
selected_population_size,
35+
);
36+
genotype
37+
.chromosome_cloner_expand(&mut state.population.chromosomes, dropped_population_size);
38+
2739
state
2840
.population
2941
.chromosomes

tests/crossover/rejuvenate_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ fn standard() {
1616
let population: Population<BinaryChromosome> = build::population_with_age(vec![
1717
(vec![true, true, true], 1),
1818
(vec![false, false, false], 2),
19-
(vec![true, true, true], 1),
19+
(vec![true, false, false], 2),
2020
]);
2121

2222
let mut state = EvolveState::new(&genotype);

0 commit comments

Comments
 (0)