@@ -88,7 +88,7 @@ When implementing `calculate_for_chromosome`, access genes via `chromosome.genes
8888
8989** WARNING** : ` UniqueGenotype ` will cause a ** runtime panic** with gene-based or
9090point-based crossovers. Use ` CrossoverClone ` (clones parents, relies on mutation
91- for diversity) or ` CrossoverRejuvenate ` (generates fresh random chromosomes ).
91+ for diversity) or ` CrossoverRejuvenate ` (like Clone but optimized for less memory copying ).
9292` MultiUniqueGenotype ` supports point-based crossovers (` CrossoverSinglePoint ` ,
9393` CrossoverMultiPoint ` ) but panics on gene-based ones (` CrossoverUniform ` ,
9494` CrossoverSingleGene ` , ` CrossoverMultiGene ` ).
@@ -157,15 +157,18 @@ CrossoverClone::new(
157157)
158158
159159CrossoverRejuvenate :: new (
160- selection_rate : f32 , // No actual crossover, generates fresh random chromosomes .
160+ selection_rate : f32 , // Like Clone but drops non-selected first, then refills. Less memory copying .
161161)
162162```
163163
164164### Mutate
165165
166+ ** Rate guidance depends on genotype size and type — see "Mutation tuning for
167+ large float genomes" in Troubleshooting.**
168+
166169``` rust
167170MutateSingleGene :: new (
168- mutation_probability : f32 , // 0.05-0.3 typical. Probability per chromosome .
171+ mutation_probability : f32 , // 0.05-0.3 typical for binary. See note above for floats .
169172)
170173
171174MutateMultiGene :: new (
@@ -195,23 +198,27 @@ MutateMultiGeneDynamic::new(
195198``` rust
196199ExtensionMassExtinction :: new (
197200 cardinality_threshold : usize , // Trigger when unique chromosomes drop below this.
198- survival_rate : f32 , // Fraction that survives extinction .
199- elitism_rate : f32 , // Fraction of elite preserved.
201+ survival_rate : f32 , // Fraction that survives (random selection + elite) .
202+ elitism_rate : f32 , // Fraction of elite preserved before random reduction .
200203)
204+ // Randomly trims population. Recovery happens naturally through offspring in following generations.
201205
202206ExtensionMassGenesis :: new (
203- cardinality_threshold : usize , // Replace all non-elite with fresh random chromosomes .
207+ cardinality_threshold : usize , // Trims to only 2 best (Adam & Eve). Most aggressive reset .
204208)
209+ // Extreme version of MassExtinction. Population recovers through offspring in following generations.
205210
206211ExtensionMassDegeneration :: new (
207212 cardinality_threshold : usize ,
208- number_of_rounds : usize , // Rounds of random mutation applied.
209- elitism_rate : f32 ,
213+ number_of_mutations : usize , // Number of gene mutations applied per chromosome .
214+ elitism_rate : f32 , // Fraction of elite preserved before mutation.
210215)
216+ // Only extension that actually mutates genes. No population trim, same size throughout.
211217
212218ExtensionMassDeduplication :: new (
213- cardinality_threshold : usize , // Replace duplicate chromosomes with fresh ones .
219+ cardinality_threshold : usize , // Trims to only unique chromosomes (by genes hash) .
214220)
221+ // Removes duplicates. Population recovers through offspring in following generations.
215222```
216223
217224## Builder Methods (Evolve)
@@ -599,6 +606,40 @@ fn main() {
599606- Add an extension like ` ExtensionMassGenesis ` or ` ExtensionMassDegeneration ` to
600607 escape local optima when diversity drops
601608
609+ ** Mutation tuning for large float genomes (RangeGenotype/MultiRangeGenotype)?**
610+
611+ The "typical" mutation rates assume small binary genomes. For large float genomes
612+ (hundreds to thousands of genes), the effective per-gene mutation rate matters
613+ more than the per-chromosome probability. Think in terms of what fraction of all
614+ genes in the population actually change per generation.
615+
616+ - ** Binary genes:** mutation flips 0↔1, which is a large relative change. Low
617+ rates (1-5% per chromosome) suffice.
618+ - ** Float genes:** mutation nudges a continuous value. Each mutation has less
619+ relative impact, so you need far more mutations to maintain diversity.
620+
621+ Concrete example for a 2000-gene float genome, population 100:
622+ - ` MutateSingleGene(0.2) ` → 1 gene × 20% of offspring = effective 0.01% of all
623+ genes change per generation. ** Population will collapse to near-clones.**
624+ - ` MutateMultiGene(10, 1.0) ` → ~ 5.5 genes × 100% of offspring = effective 0.28%
625+ of all genes change per generation. ** Maintains diversity.**
626+
627+ Rule of thumb for float genomes: target 0.1%-1.0% effective per-gene mutation
628+ rate across the population. Use ` MutateMultiGene ` with high ` mutation_probability `
629+ and scale ` number_of_mutations ` with genome size.
630+
631+ ** But high mutation prevents convergence.** Use scaled mutation types to get both
632+ exploration early and convergence late:
633+ - ` MutationType::RangeScaled(vec![100.0, 100.0, 50.0, 10.0, 1.0]) ` — starts
634+ with full-range Random mutations (100% of allele range = effectively Random),
635+ then progressively narrows the mutation bandwidth. Best for float genomes:
636+ wide exploration phases first, then tight range-bound convergence.
637+ - ` MutationType::StepScaled(vec![10.0, 1.0, 0.1]) ` — fixed step sizes that
638+ decrease through phases. Better for grid-like or discrete problems.
639+
640+ Combine with ` max_stale_generations ` to trigger phase transitions automatically
641+ (advances to next phase when fitness plateaus).
642+
602643** Runtime too slow?**
603644- Use ` .with_par_fitness(true) ` for expensive fitness calculations
604645- Use ` .with_fitness_cache(size) ` if many chromosomes share the same genes
@@ -610,6 +651,9 @@ fn main() {
610651- All chromosomes returned ` None ` from ` calculate_for_chromosome `
611652- Check your fitness function's validity constraints — they may be too strict
612653- Increase population size so some valid solutions appear in the initial population
654+ - Prefer large penalties over ` None ` : returning ` Some(very_bad_score) ` lets the
655+ algorithm converge incrementally out of invalid space, while ` None ` provides
656+ no gradient signal and ranks last unconditionally
613657
614658## Gotchas
615659
0 commit comments